Merge "Correct HeadsetMediaButton behavior for external calls." into tm-dev
diff --git a/Android.bp b/Android.bp
index 88cffb8..1b422aa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -54,7 +54,8 @@
"androidx.legacy_legacy-support-core-utils",
"androidx.core_core",
"androidx.fragment_fragment",
- "androidx.test.ext.junit"
+ "androidx.test.ext.junit",
+ "platform-compat-test-rules",
],
srcs: [
"tests/src/**/*.java",
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 6a7261e..6a1f5aa 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -537,9 +537,9 @@
pw.println("Foreground call:");
pw.println(mForegroundCall);
- pw.println("CallAudioModeStateMachine pending messages:");
+ pw.println("CallAudioModeStateMachine:");
pw.increaseIndent();
- mCallAudioModeStateMachine.dumpPendingMessages(pw);
+ mCallAudioModeStateMachine.dump(pw);
pw.decreaseIndent();
pw.println("CallAudioRouteStateMachine pending messages:");
@@ -557,6 +557,7 @@
@VisibleForTesting
public void setIsTonePlaying(boolean isTonePlaying) {
+ Log.i(this, "setIsTonePlaying; isTonePlaying=%b", isTonePlaying);
mIsTonePlaying = isTonePlaying;
mCallAudioModeStateMachine.sendMessageWithArgs(
isTonePlaying ? CallAudioModeStateMachine.TONE_STARTED_PLAYING
diff --git a/src/com/android/server/telecom/CallAudioModeStateMachine.java b/src/com/android/server/telecom/CallAudioModeStateMachine.java
index 2aa9d5d..a1c5f4b 100644
--- a/src/com/android/server/telecom/CallAudioModeStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioModeStateMachine.java
@@ -22,6 +22,7 @@
import android.telecom.Log;
import android.telecom.Logging.Runnable;
import android.telecom.Logging.Session;
+import android.util.LocalLog;
import android.util.SparseArray;
import com.android.internal.util.IState;
@@ -30,6 +31,11 @@
import com.android.internal.util.StateMachine;
public class CallAudioModeStateMachine extends StateMachine {
+ /**
+ * Captures the most recent CallAudioModeStateMachine state transitions and the corresponding
+ * changes to the {@link AudioManager#setMode}.
+ */
+ private LocalLog mLocalLog = new LocalLog(20);
public static class Factory {
public CallAudioModeStateMachine create(SystemStateHelper systemStateHelper,
AudioManager am) {
@@ -227,9 +233,12 @@
private class UnfocusedState extends BaseState {
@Override
public void enter() {
+ Log.i(LOG_TAG, "Audio focus entering UNFOCUSED state");
+ mLocalLog.log("Enter UNFOCUSED");
if (mIsInitialized) {
mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mLocalLog.log("Mode MODE_NORMAL");
mMostRecentMode = AudioManager.MODE_NORMAL;
// Don't release focus here -- wait until we get a signal that any other audio
// operations triggered by this are done before releasing focus.
@@ -290,9 +299,12 @@
private class AudioProcessingFocusState extends BaseState {
@Override
public void enter() {
+ Log.i(LOG_TAG, "Audio focus entering AUDIO_PROCESSING state");
+ mLocalLog.log("Enter AUDIO_PROCESSING");
if (mIsInitialized) {
mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);
mAudioManager.setMode(NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING);
+ mLocalLog.log("Mode MODE_CALL_SCREENING");
mMostRecentMode = NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING;
}
}
@@ -371,6 +383,7 @@
// trips up the audio system.
if (mAudioManager.getMode() != AudioManager.MODE_CALL_SCREENING) {
mAudioManager.setMode(AudioManager.MODE_RINGTONE);
+ mLocalLog.log("Mode MODE_RINGTONE");
}
mCallAudioManager.setCallAudioRouteFocusState(
CallAudioRouteStateMachine.RINGING_FOCUS);
@@ -383,6 +396,7 @@
@Override
public void enter() {
Log.i(LOG_TAG, "Audio focus entering RINGING state");
+ mLocalLog.log("Enter RINGING");
tryStartRinging();
mCallAudioManager.stopCallWaiting();
}
@@ -456,9 +470,11 @@
@Override
public void enter() {
Log.i(LOG_TAG, "Audio focus entering SIM CALL state");
+ mLocalLog.log("Enter SIM_CALL");
mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mLocalLog.log("Mode MODE_IN_CALL");
mMostRecentMode = AudioManager.MODE_IN_CALL;
mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
}
@@ -537,9 +553,11 @@
@Override
public void enter() {
Log.i(LOG_TAG, "Audio focus entering VOIP CALL state");
+ mLocalLog.log("Enter VOIP_CALL");
mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ mLocalLog.log("Mode MODE_IN_COMMUNICATION");
mMostRecentMode = AudioManager.MODE_IN_COMMUNICATION;
mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
}
@@ -614,9 +632,11 @@
@Override
public void enter() {
Log.i(LOG_TAG, "Audio focus entering TONE/HOLDING state");
+ mLocalLog.log("Enter TONE/HOLDING");
mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
mAudioManager.setMode(mMostRecentMode);
+ mLocalLog.log("Mode " + mMostRecentMode);
mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
}
@@ -763,6 +783,13 @@
getHandler().getLooper().dump(pw::println, "");
}
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("History:");
+ mLocalLog.dump(pw);
+ pw.println("Pending Msg:");
+ dumpPendingMessages(pw);
+ }
+
@Override
protected void onPostHandleMessage(Message msg) {
Log.endSession();
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 1e8f779..c78c8d5 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -22,8 +22,11 @@
import android.Manifest;
import android.annotation.NonNull;
import android.app.AppOpsManager;
+import android.app.compat.CompatChanges;
import android.app.Notification;
import android.app.NotificationManager;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -37,6 +40,7 @@
import android.content.pm.ServiceInfo;
import android.hardware.SensorPrivacyManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -87,6 +91,16 @@
public static final String NOTIFICATION_TAG = InCallController.class.getSimpleName();
public static final int IN_CALL_SERVICE_NOTIFICATION_ID = 3;
+ /**
+ * Enable a crash notification if the default dialer app does not implement the
+ * {@link InCallService} and the system Dialer takes over.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH = 218903401L; // bug id
+
public class InCallServiceConnection {
/**
* Indicates that a call to {@link #connect(Call)} has succeeded and resulted in a
@@ -1620,10 +1634,14 @@
true /* ignoreDisabled */)
: getInCallServiceComponent(packageName,
IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI, true /* ignoreDisabled */);
- if (packageName != null && defaultDialerComponent == null) {
+
+ if (packageName != null && defaultDialerComponent == null &&
+ CompatChanges.isChangeEnabled(ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH,
+ Binder.getCallingUid())) {
// The in call service of default phone app is disabled, send notification.
sendCrashedInCallServiceNotification(packageName);
}
+
return defaultDialerComponent;
}
diff --git a/src/com/android/server/telecom/InCallTonePlayer.java b/src/com/android/server/telecom/InCallTonePlayer.java
index 524d119..5a514ce 100644
--- a/src/com/android/server/telecom/InCallTonePlayer.java
+++ b/src/com/android/server/telecom/InCallTonePlayer.java
@@ -438,7 +438,8 @@
mToneMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
- Log.i(this, "playMediaTone: toneResourceId=%d completed.", toneResourceId);
+ Log.i(InCallTonePlayer.this, "playMediaTone: toneResourceId=%d completed.",
+ toneResourceId);
synchronized (InCallTonePlayer.this) {
mState = STATE_OFF;
}
@@ -508,14 +509,26 @@
}
private void cleanUpTonePlayer() {
+ Log.d(this, "cleanUpTonePlayer(): posting cleanup");
// Release focus on the main thread.
mMainThreadHandler.post(new Runnable("ICTP.cUTP", mLock) {
@Override
public void loggedRun() {
if (sTonesPlaying == 0) {
- Log.wtf(this, "Over-releasing focus for tone player.");
- } else if (--sTonesPlaying == 0 && mCallAudioManager != null) {
- mCallAudioManager.setIsTonePlaying(false);
+ Log.wtf(InCallTonePlayer.this,
+ "cleanUpTonePlayer(): Over-releasing focus for tone player.");
+ } else if (--sTonesPlaying == 0) {
+ Log.i(InCallTonePlayer.this,
+ "cleanUpTonePlayer(): tonesPlaying=%d, tone completed", sTonesPlaying);
+ if (mCallAudioManager != null) {
+ mCallAudioManager.setIsTonePlaying(false);
+ } else {
+ Log.w(InCallTonePlayer.this,
+ "cleanUpTonePlayer(): mCallAudioManager is null!");
+ }
+ } else {
+ Log.i(InCallTonePlayer.this,
+ "cleanUpTonePlayer(): tonesPlaying=%d; still playing", sTonesPlaying);
}
}
}.prepare());
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index dfcbbc4..e65a651 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -166,6 +166,7 @@
private final AtomicFile mAtomicFile;
private final Context mContext;
private final UserManager mUserManager;
+ private final TelephonyManager mTelephonyManager;
private final SubscriptionManager mSubscriptionManager;
private final DefaultDialerCache mDefaultDialerCache;
private final AppLabelProxy mAppLabelProxy;
@@ -195,6 +196,7 @@
mUserManager = UserManager.get(context);
mDefaultDialerCache = defaultDialerCache;
mSubscriptionManager = SubscriptionManager.from(mContext);
+ mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mAppLabelProxy = appLabelProxy;
mCurrentUserHandle = Process.myUserHandle();
@@ -217,9 +219,7 @@
PhoneAccount account = getPhoneAccountUnchecked(accountHandle);
if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
- TelephonyManager tm =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- return tm.getSubscriptionId(accountHandle);
+ return mTelephonyManager.getSubscriptionId(accountHandle);
}
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
@@ -1152,11 +1152,10 @@
"Notifying telephony of voice service override change for %d SIMs, hasService = %b",
simHandlesToNotify.size(),
hasService);
- TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
for (PhoneAccountHandle simHandle : simHandlesToNotify) {
// This may be null if there are no active SIMs but the device is still camped for
// emergency calls and registered a SIM_SUBSCRIPTION for that purpose.
- TelephonyManager simTm = tm.createForPhoneAccountHandle(simHandle);
+ TelephonyManager simTm = mTelephonyManager.createForPhoneAccountHandle(simHandle);
if (simTm == null) continue;
simTm.setVoiceServiceStateOverride(hasService);
}
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 228a489..2785739 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -21,6 +21,7 @@
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
+import android.bluetooth.BluetoothLeAudioCodecStatus;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
@@ -34,6 +35,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.concurrent.Executor;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -47,6 +49,37 @@
public static final int DEVICE_TYPE_HEARING_AID = 1;
public static final int DEVICE_TYPE_LE_AUDIO = 2;
+ private BluetoothLeAudio.Callback mLeAudioCallbacks =
+ new BluetoothLeAudio.Callback() {
+ @Override
+ public void onCodecConfigChanged(int groupId, BluetoothLeAudioCodecStatus status) {}
+ @Override
+ public void onGroupStatusChanged(int groupId, int groupStatus) {}
+ @Override
+ public void onGroupNodeAdded(BluetoothDevice device, int groupId) {
+ Log.i(this, device.getAddress() + " group added " + groupId);
+ if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
+ Log.w(this, "invalid parameter");
+ return;
+ }
+
+ synchronized (mLock) {
+ mGroupsByDevice.put(device, groupId);
+ }
+ }
+ @Override
+ public void onGroupNodeRemoved(BluetoothDevice device, int groupId) {
+ if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
+ Log.w(this, "invalid parameter");
+ return;
+ }
+
+ synchronized (mLock) {
+ mGroupsByDevice.remove(device);
+ }
+ }
+ };
+
private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
new BluetoothProfile.ServiceListener() {
@Override
@@ -66,6 +99,11 @@
mBluetoothLeAudioService = (BluetoothLeAudio) proxy;
logString = "Got BluetoothLeAudio: "
+ mBluetoothLeAudioService;
+ if (!mLeAudioCallbackRegistered) {
+ mBluetoothLeAudioService.registerCallback(
+ mExecutor, mLeAudioCallbacks);
+ mLeAudioCallbackRegistered = true;
+ }
} else {
logString = "Connected to non-requested bluetooth service." +
" Not changing bluetooth headset.";
@@ -145,11 +183,13 @@
private BluetoothRouteManager mBluetoothRouteManager;
private BluetoothHeadset mBluetoothHeadset;
private BluetoothHearingAid mBluetoothHearingAid;
+ private boolean mLeAudioCallbackRegistered = false;
private BluetoothLeAudio mBluetoothLeAudioService;
private boolean mLeAudioSetAsCommunicationDevice = false;
private BluetoothDevice mBluetoothHearingAidActiveDeviceCache;
private BluetoothAdapter mBluetoothAdapter;
private AudioManager mAudioManager;
+ private Executor mExecutor;
public BluetoothDeviceManager(Context context, BluetoothAdapter bluetoothAdapter) {
if (bluetoothAdapter != null) {
@@ -161,6 +201,7 @@
bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
BluetoothProfile.LE_AUDIO);
mAudioManager = context.getSystemService(AudioManager.class);
+ mExecutor = context.getMainExecutor();
}
}
@@ -265,6 +306,7 @@
public void setLeAudioServiceForTesting(BluetoothLeAudio bluetoothLeAudio) {
mBluetoothLeAudioService = bluetoothLeAudio;
+ mBluetoothLeAudioService.registerCallback(mExecutor, mLeAudioCallbacks);
}
public static String getDeviceTypeString(int deviceType) {
@@ -339,29 +381,6 @@
}
}
- void onGroupNodeAdded(BluetoothDevice device, int groupId) {
- Log.i(this, device.getAddress() + " group added " + groupId);
- if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
- Log.w(this, "invalid parameter");
- return;
- }
-
- synchronized (mLock) {
- mGroupsByDevice.put(device, groupId);
- }
- }
-
- void onGroupNodeRemoved(BluetoothDevice device, int groupId) {
- if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
- Log.w(this, "invalid parameter");
- return;
- }
-
- synchronized (mLock) {
- mGroupsByDevice.remove(device);
- }
- }
-
public void disconnectAudio() {
if (mBluetoothAdapter != null) {
for (BluetoothDevice device: mBluetoothAdapter.getActiveDevices(
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 17da806..5c5ded0 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -46,8 +46,6 @@
INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
- INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED);
- INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_STATUS_CHANGED);
}
// If not in a call, BSR won't listen to the Bluetooth stack's HFP on/off messages, since
@@ -74,9 +72,6 @@
case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED:
handleActiveDeviceChanged(intent);
break;
- case BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED:
- handleGroupNodeStatusChanged(intent);
- break;
}
} finally {
Log.endSession();
@@ -199,22 +194,6 @@
}
}
- private void handleGroupNodeStatusChanged(Intent intent) {
- BluetoothDevice device =
- intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-
- int groupId = intent.getIntExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_ID,
- BluetoothLeAudio.GROUP_ID_INVALID);
- int groupNodeStatus = intent.getIntExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_NODE_STATUS,
- -1);
-
- if (groupNodeStatus == BluetoothLeAudio.GROUP_NODE_ADDED) {
- mBluetoothDeviceManager.onGroupNodeAdded(device, groupId);
- } else {
- mBluetoothDeviceManager.onGroupNodeRemoved(device, groupId);
- }
- }
-
public BluetoothDeviceManager getBluetoothDeviceManager() {
return mBluetoothDeviceManager;
}
diff --git a/src/com/android/server/telecom/components/UserCallIntentProcessor.java b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
old mode 100644
new mode 100755
index 75c1996..ae76708
--- a/src/com/android/server/telecom/components/UserCallIntentProcessor.java
+++ b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
@@ -98,6 +98,7 @@
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
Uri handle = intent.getData();
+ if (handle == null) return;
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
diff --git a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
index b153fa4..03a5b43 100644
--- a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
+++ b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
@@ -17,7 +17,7 @@
package com.android.server.telecom.ui;
import static android.Manifest.permission.READ_PHONE_STATE;
-import static android.app.admin.DevicePolicyResources.Strings.Dialer.NOTIFICATION_MISSED_WORK_CALL_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Telecomm.NOTIFICATION_MISSED_WORK_CALL_TITLE;
import android.annotation.NonNull;
import android.app.BroadcastOptions;
@@ -337,9 +337,9 @@
CallerInfo ci = callInfo.getCallerInfo();
if (ci != null && ci.userType == CallerInfo.USER_TYPE_WORK) {
- titleText = mContext.getSystemService(DevicePolicyManager.class).getString(
- NOTIFICATION_MISSED_WORK_CALL_TITLE,
- () -> mContext.getString(R.string.notification_missedWorkCallTitle));
+ titleText = mContext.getSystemService(DevicePolicyManager.class).getResources()
+ .getString(NOTIFICATION_MISSED_WORK_CALL_TITLE, () ->
+ mContext.getString(R.string.notification_missedWorkCallTitle));
} else {
titleText = mContext.getString(R.string.notification_missedCallTitle);
}
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index 29462f5..f011f0c 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -45,6 +45,7 @@
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -68,6 +69,7 @@
BluetoothDeviceManager mBluetoothDeviceManager;
BluetoothProfile.ServiceListener serviceListenerUnderTest;
BluetoothStateReceiver receiverUnderTest;
+ ArgumentCaptor<BluetoothLeAudio.Callback> leAudioCallbacksTest;
private BluetoothDevice device1;
private BluetoothDevice device2;
@@ -109,7 +111,11 @@
mBluetoothDeviceManager.setHeadsetServiceForTesting(mBluetoothHeadset);
mBluetoothDeviceManager.setHearingAidServiceForTesting(mBluetoothHearingAid);
+
+ leAudioCallbacksTest =
+ ArgumentCaptor.forClass(BluetoothLeAudio.Callback.class);
mBluetoothDeviceManager.setLeAudioServiceForTesting(mBluetoothLeAudio);
+ verify(mBluetoothLeAudio).registerCallback(any(), leAudioCallbacksTest.capture());
}
@Override
@@ -162,8 +168,7 @@
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
- receiverUnderTest.onReceive(mContext,
- buildGroupNodeStatusChangedIntent(1, device5, BluetoothLeAudio.GROUP_NODE_ADDED));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device5, 1);
when(mBluetoothLeAudio.getConnectedGroupLeadDevice(1)).thenReturn(device5);
receiverUnderTest.onReceive(mContext,
@@ -172,8 +177,7 @@
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device6,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
- receiverUnderTest.onReceive(mContext,
- buildGroupNodeStatusChangedIntent(2, device6, BluetoothLeAudio.GROUP_NODE_ADDED));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device6, 2);
when(mBluetoothLeAudio.getConnectedGroupLeadDevice(2)).thenReturn(device6);
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device3,
@@ -216,13 +220,11 @@
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
- receiverUnderTest.onReceive(mContext,
- buildGroupNodeStatusChangedIntent(1, device5, BluetoothLeAudio.GROUP_NODE_ADDED));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device5, 1);
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device6,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
- receiverUnderTest.onReceive(mContext,
- buildGroupNodeStatusChangedIntent(1, device6, BluetoothLeAudio.GROUP_NODE_ADDED));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device6, 1);
when(mBluetoothLeAudio.getConnectedGroupLeadDevice(1)).thenReturn(device5);
assertEquals(2, mBluetoothDeviceManager.getNumConnectedDevices());
assertEquals(2, mBluetoothDeviceManager.getUniqueConnectedDevices().size());
@@ -376,8 +378,7 @@
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
- receiverUnderTest.onReceive(mContext,
- buildGroupNodeStatusChangedIntent(1, device5, BluetoothLeAudio.GROUP_NODE_ADDED));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device5, 1);
when(mAdapter.setActiveDevice(nullable(BluetoothDevice.class),
eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true);
@@ -408,13 +409,11 @@
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
- receiverUnderTest.onReceive(mContext,
- buildGroupNodeStatusChangedIntent(1, device5, BluetoothLeAudio.GROUP_NODE_ADDED));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device5, 1);
receiverUnderTest.onReceive(mContext,
buildConnectionActionIntent(BluetoothLeAudio.STATE_CONNECTED, device6,
BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
- receiverUnderTest.onReceive(mContext,
- buildGroupNodeStatusChangedIntent(1, device6, BluetoothLeAudio.GROUP_NODE_ADDED));
+ leAudioCallbacksTest.getValue().onGroupNodeAdded(device6, 1);
when(mAdapter.setActiveDevice(nullable(BluetoothDevice.class),
eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true);
mBluetoothDeviceManager.connectAudio(device5.getAddress());
@@ -457,22 +456,6 @@
return i;
}
- private Intent buildGroupNodeStatusChangedIntent(int groupId, BluetoothDevice device,
- int nodeStatus) {
- Intent i = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED);
- i.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- i.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_ID, groupId);
- i.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_NODE_STATUS, nodeStatus);
- return i;
- }
-
- private Intent buildGroupStatusChangedIntent(int groupId, int groupStatus) {
- Intent i = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_STATUS_CHANGED);
- i.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_ID, groupId);
- i.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_STATUS, groupStatus);
- return i;
- }
-
private BluetoothDevice makeBluetoothDevice(String address) {
Parcel p1 = Parcel.obtain();
p1.writeString(address);
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 79032da..dfc41a2 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -64,6 +64,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
+import android.compat.testing.PlatformCompatChangeRule;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -102,7 +103,9 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
@@ -120,6 +123,8 @@
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
+import libcore.junit.util.compat.CoreCompatChangeRule;
+
@RunWith(JUnit4.class)
public class InCallControllerTests extends TelecomTestCase {
@Mock CallsManager mMockCallsManager;
@@ -139,6 +144,9 @@
@Mock NotificationManager mNotificationManager;
@Mock PermissionInfo mMockPermissionInfo;
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
private static final int CURRENT_USER_ID = 900973;
private static final String DEF_PKG = "defpkg";
private static final String DEF_CLASS = "defcls";
@@ -909,10 +917,13 @@
/**
* Ensures that the {@link InCallController} will bind to an {@link InCallService} which
- * supports third party app
+ * supports third party app. Also, we want to verify a notification is sent to apps targeting
+ * Tiramisu and above when the InCallService of the default app is disabled.
*/
@MediumTest
@Test
+ @CoreCompatChangeRule.EnableCompatChanges({
+ InCallController.ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH})
public void testBindToService_ThirdPartyApp() throws Exception {
final MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
.strictness(Strictness.WARN)
@@ -926,6 +937,7 @@
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU;
+ // set up mock call for ICSC#sendCrashedInCallServiceNotification(String)
when(mMockContext.getApplicationInfo()).thenReturn(applicationInfo);
// Enable Third Party Companion App
@@ -1005,6 +1017,67 @@
eq(InCallController.IN_CALL_SERVICE_NOTIFICATION_ID), any());
}
+ /**
+ * Ensures that the {@link InCallController} will bind to an {@link InCallService} which
+ * supports third party app. Also, we want to verify a notification is NOT sent to apps
+ * targeting below Tiramisu when the InCallService of the default app is disabled.
+ */
+ @MediumTest
+ @Test
+ @CoreCompatChangeRule.DisableCompatChanges({
+ InCallController.ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH})
+ public void testBindToService_ThirdPartyAppBelowTiramisu() throws Exception {
+ final MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+ .strictness(Strictness.WARN)
+ .spyStatic(PermissionChecker.class)
+ .startMocking();
+ try {
+ setupMocks(false /* isExternalCall */);
+ setupMockPackageManager(false /* default */, false /* nonui */, true /* appop_nonui */,
+ true /* system */, false /* external calls */, false /* self mgd in default */,
+ false /* self mgd in car*/);
+
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = Build.VERSION_CODES.S_V2;
+ // set up mock call for ICSC#sendCrashedInCallServiceNotification(String)
+ when(mMockContext.getApplicationInfo()).thenReturn(applicationInfo);
+
+ // Enable Third Party Companion App
+ ExtendedMockito.doReturn(PermissionChecker.PERMISSION_GRANTED).when(() ->
+ PermissionChecker.checkPermissionForDataDeliveryFromDataSource(
+ any(Context.class), eq(Manifest.permission.MANAGE_ONGOING_CALLS),
+ anyInt(), any(AttributionSource.class), nullable(String.class)));
+
+ // Now bind; we should bind to the system dialer and app op non ui app.
+ mInCallController.bindToServices(mMockCall);
+
+ // Bind InCallServices
+ ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockContext, times(2)).bindServiceAsUser(
+ bindIntentCaptor.capture(),
+ any(ServiceConnection.class),
+ eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+ | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+ eq(UserHandle.CURRENT));
+
+ // Verify bind
+ assertEquals(2, bindIntentCaptor.getAllValues().size());
+
+ // Should have first bound to the system dialer.
+ verifyBinding(bindIntentCaptor, 0, SYS_PKG, SYS_CLASS);
+
+ // Should have next bound to the third party app op non ui app.
+ verifyBinding(bindIntentCaptor, 1, APPOP_NONUI_PKG, APPOP_NONUI_CLASS);
+
+ // Verify notification is NOT sent by NotificationManager
+ verify(mNotificationManager, times(0)).notify(eq(InCallController.NOTIFICATION_TAG),
+ eq(InCallController.IN_CALL_SERVICE_NOTIFICATION_ID), any());
+
+ } finally {
+ mockitoSession.finishMocking();
+ }
+ }
+
@MediumTest
@Test
public void testSanitizeContactName() throws Exception {