Merge "update telecom test apps directory (NPE & RoleManager)" into tm-dev
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index eea3a6f..be67223 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -73,7 +73,7 @@
<string name="non_primary_user" msgid="315564589279622098">"Alleen de eigenaar van het apparaat kan geblokkeerd nummers bekijken en beheren."</string>
<string name="delete_icon_description" msgid="5335959254954774373">"Blokkering opheffen"</string>
<string name="blocked_numbers_butter_bar_title" msgid="582982373755950791">"Blokkering tijdelijk uitgezet"</string>
- <string name="blocked_numbers_butter_bar_body" msgid="1261213114919301485">"Als je een noodnummer belt of er een sms naartoe stuurt, wordt de blokkering uitgeschakeld om te zorgen dat hulpdiensten contact met je kunnen opnemen."</string>
+ <string name="blocked_numbers_butter_bar_body" msgid="1261213114919301485">"Als je een noodnummer belt of er een sms naartoe stuurt, wordt de blokkering uitgezet om te zorgen dat hulpdiensten contact met je kunnen opnemen."</string>
<string name="blocked_numbers_butter_bar_button" msgid="2704456308072489793">"Nu opnieuw aanzetten"</string>
<string name="blocked_numbers_number_blocked_message" msgid="4314736791180919167">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> geblokkeerd"</string>
<string name="blocked_numbers_number_unblocked_message" msgid="2933071624674945601">"Blokkering van <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> opgeheven"</string>
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index f98513e..9d6dd07 100755
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -3300,7 +3300,7 @@
*
* @return {@code True} if there are any non-external calls, {@code false} otherwise.
*/
- boolean hasAnyCalls() {
+ public boolean hasAnyCalls() {
if (mCalls.isEmpty()) {
return false;
}
diff --git a/src/com/android/server/telecom/HeadsetMediaButton.java b/src/com/android/server/telecom/HeadsetMediaButton.java
index ad95c34..b1471c2 100644
--- a/src/com/android/server/telecom/HeadsetMediaButton.java
+++ b/src/com/android/server/telecom/HeadsetMediaButton.java
@@ -46,6 +46,47 @@
private static final int MSG_MEDIA_SESSION_INITIALIZE = 0;
private static final int MSG_MEDIA_SESSION_SET_ACTIVE = 1;
+ /**
+ * Wrapper class that abstracts an instance of {@link MediaSession} to the
+ * {@link MediaSessionAdapter} interface this class uses. This is done because
+ * {@link MediaSession} is a final class and cannot be mocked for testing purposes.
+ */
+ public class MediaSessionWrapper implements MediaSessionAdapter {
+ private final MediaSession mMediaSession;
+
+ public MediaSessionWrapper(MediaSession mediaSession) {
+ mMediaSession = mediaSession;
+ }
+
+ /**
+ * Sets the underlying {@link MediaSession} active status.
+ * @param active
+ */
+ @Override
+ public void setActive(boolean active) {
+ mMediaSession.setActive(active);
+ }
+
+ /**
+ * Gets the underlying {@link MediaSession} active status.
+ * @return {@code true} if active, {@code false} otherwise.
+ */
+ @Override
+ public boolean isActive() {
+ return mMediaSession.isActive();
+ }
+ }
+
+ /**
+ * Interface which defines the basic functionality of a {@link MediaSession} which is important
+ * for the {@link HeadsetMediaButton} to operator; this is for testing purposes so we can mock
+ * out that functionality.
+ */
+ public interface MediaSessionAdapter {
+ void setActive(boolean active);
+ boolean isActive();
+ }
+
private final MediaSession.Callback mSessionCallback = new MediaSession.Callback() {
@Override
public boolean onMediaButtonEvent(Intent intent) {
@@ -81,7 +122,7 @@
session.setFlags(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY
| MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
session.setPlaybackToLocal(AUDIO_ATTRIBUTES);
- mSession = session;
+ mSession = new MediaSessionWrapper(session);
break;
}
case MSG_MEDIA_SESSION_SET_ACTIVE: {
@@ -102,9 +143,37 @@
private final Context mContext;
private final CallsManager mCallsManager;
private final TelecomSystem.SyncRoot mLock;
- private MediaSession mSession;
+ private MediaSessionAdapter mSession;
private KeyEvent mLastHookEvent;
+ /**
+ * Constructor used for testing purposes to initialize a {@link HeadsetMediaButton} with a
+ * specified {@link MediaSessionAdapter}. Will not trigger MSG_MEDIA_SESSION_INITIALIZE and
+ * cause an actual {@link MediaSession} instance to be created.
+ * @param context the context
+ * @param callsManager the mock calls manager
+ * @param lock the lock
+ * @param adapter the adapter
+ */
+ @VisibleForTesting
+ public HeadsetMediaButton(
+ Context context,
+ CallsManager callsManager,
+ TelecomSystem.SyncRoot lock,
+ MediaSessionAdapter adapter) {
+ mContext = context;
+ mCallsManager = callsManager;
+ mLock = lock;
+ mSession = adapter;
+ }
+
+ /**
+ * Production code constructor; this version triggers MSG_MEDIA_SESSION_INITIALIZE which will
+ * create an actual instance of {@link MediaSession}.
+ * @param context the context
+ * @param callsManager the calls manager
+ * @param lock the telecom lock
+ */
public HeadsetMediaButton(
Context context,
CallsManager callsManager,
@@ -155,6 +224,13 @@
if (call.isExternalCall()) {
return;
}
+ handleCallAddition();
+ }
+
+ /**
+ * Triggers session activation due to call addition.
+ */
+ private void handleCallAddition() {
mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 1, 0).sendToTarget();
}
@@ -164,6 +240,13 @@
if (call.isExternalCall()) {
return;
}
+ handleCallRemoval();
+ }
+
+ /**
+ * Triggers session deactivation due to call removal.
+ */
+ private void handleCallRemoval() {
if (!mCallsManager.hasAnyCalls()) {
mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 0, 0).sendToTarget();
}
@@ -172,10 +255,20 @@
/** ${inheritDoc} */
@Override
public void onExternalCallChanged(Call call, boolean isExternalCall) {
+ // Note: We don't use the onCallAdded/onCallRemoved methods here since they do checks to see
+ // if the call is external or not and would skip the session activation/deactivation.
if (isExternalCall) {
- onCallRemoved(call);
+ handleCallRemoval();
} else {
- onCallAdded(call);
+ handleCallAddition();
}
}
+
+ @VisibleForTesting
+ /**
+ * @return the handler this class instance uses for operation; used for unit testing.
+ */
+ public Handler getHandler() {
+ return mMediaSessionHandler;
+ }
}
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index c78c8d5..6e952e3 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -21,6 +21,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
import android.app.Notification;
@@ -90,17 +91,6 @@
AppOpsManager.OnOpActiveChangedListener {
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
@@ -1622,27 +1612,31 @@
mNonUIInCallServiceConnections.connect(call);
}
- private InCallServiceInfo getDefaultDialerComponent() {
- String packageName = mDefaultDialerCache.getDefaultDialerApplication(
+ private @Nullable InCallServiceInfo getDefaultDialerComponent() {
+ String defaultPhoneAppName = mDefaultDialerCache.getDefaultDialerApplication(
mCallsManager.getCurrentUserHandle().getIdentifier());
- String systemPackageName = mDefaultDialerCache.getSystemDialerApplication();
- Log.d(this, "Default Dialer package: " + packageName);
+ String systemPhoneAppName = mDefaultDialerCache.getSystemDialerApplication();
- InCallServiceInfo defaultDialerComponent =
- (systemPackageName != null && systemPackageName.equals(packageName))
- ? getInCallServiceComponent(packageName, IN_CALL_SERVICE_TYPE_SYSTEM_UI,
- true /* ignoreDisabled */)
- : getInCallServiceComponent(packageName,
+ Log.d(this, "getDefaultDialerComponent: defaultPhoneAppName=[%s]", defaultPhoneAppName);
+ Log.d(this, "getDefaultDialerComponent: systemPhoneAppName=[%s]", systemPhoneAppName);
+
+ // Get the defaultPhoneApp InCallService component...
+ InCallServiceInfo defaultPhoneAppComponent =
+ (systemPhoneAppName != null && systemPhoneAppName.equals(defaultPhoneAppName)) ?
+ /* The defaultPhoneApp is also the systemPhoneApp. Get systemPhoneApp info*/
+ getInCallServiceComponent(defaultPhoneAppName,
+ IN_CALL_SERVICE_TYPE_SYSTEM_UI, true /* ignoreDisabled */)
+ /* The defaultPhoneApp is NOT the systemPhoneApp. Get defaultPhoneApp info*/
+ : getInCallServiceComponent(defaultPhoneAppName,
IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI, true /* ignoreDisabled */);
- 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);
- }
+ Log.d(this, "getDefaultDialerComponent: defaultPhoneAppComponent=[%s]",
+ defaultPhoneAppComponent);
- return defaultDialerComponent;
+ // defaultPhoneAppComponent is null in the case when the defaultPhoneApp does not implement
+ // the InCallService && is the package is different from the systemPhoneApp
+
+ return defaultPhoneAppComponent;
}
private InCallServiceInfo getCurrentCarModeComponent() {
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index f53f239..f8f0c49 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -21,6 +21,8 @@
import android.telecom.Logging.EventManager;
import android.telecom.Logging.EventManager.TimedEventPair;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.HashMap;
import java.util.Map;
@@ -34,6 +36,9 @@
private static final String LOGUTILS_TAG = "LogUtils";
public static final boolean SYSTRACE_DEBUG = false; /* STOP SHIP if true */
+ private static boolean sInitializedLoggerListeners = false; // used to gate re-init of listeners
+ private static int sInitializedCounter = 0; /* For testing purposes only */
+ private static final Object sLock = new Object(); // Coarse lock for all of LogUtils
public static class EventTimer {
private long mLastElapsedMillis;
@@ -263,13 +268,41 @@
}
public static void initLogging(Context context) {
- android.telecom.Log.setTag(TAG);
- android.telecom.Log.setSessionContext(context);
- for (EventManager.TimedEventPair p : Events.Timings.sTimedEvents) {
- android.telecom.Log.addRequestResponsePair(p);
+ android.telecom.Log.d(LOGUTILS_TAG, "initLogging: attempting to acquire LogUtils sLock");
+ synchronized (sLock) {
+ android.telecom.Log.d(LOGUTILS_TAG, "initLogging: grabbed LogUtils sLock");
+ if (!sInitializedLoggerListeners) {
+ android.telecom.Log.d(LOGUTILS_TAG,
+ "initLogging: called for first time. Registering the EventListener &"
+ + " SessionListener.");
+
+ android.telecom.Log.setTag(TAG);
+ android.telecom.Log.setSessionContext(context);
+ for (EventManager.TimedEventPair p : Events.Timings.sTimedEvents) {
+ android.telecom.Log.addRequestResponsePair(p);
+ }
+ android.telecom.Log.registerEventListener(LogUtils::eventRecordAdded);
+ // Store analytics about recently completed Sessions.
+ android.telecom.Log.registerSessionListener(Analytics::addSessionTiming);
+
+ // Ensure LogUtils#initLogging(Context) is called once throughout the entire
+ // lifecycle of not only TelecomSystem, but the Testing Framework.
+ sInitializedLoggerListeners = true;
+ sInitializedCounter++; /* For testing purposes only */
+ } else {
+ android.telecom.Log.d(LOGUTILS_TAG, "initLogging: called again. Doing nothing.");
+ }
}
- android.telecom.Log.registerEventListener(LogUtils::eventRecordAdded);
- // Store analytics about recently completed Sessions.
- android.telecom.Log.registerSessionListener(Analytics::addSessionTiming);
+ }
+
+ /**
+ * Needed in order to test if the registerEventListener & registerSessionListener are ever
+ * re-initialized in the entire process of the Testing Framework or TelecomSystem.
+ *
+ * @return the number of times initLogging(Context) listeners have been initialized
+ */
+ @VisibleForTesting
+ public static int getInitializedCounter() {
+ return sInitializedCounter;
}
}
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 4c23f81..64659eb 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -56,9 +56,9 @@
private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
put(NEW_DEVICE_CONNECTED, "NEW_DEVICE_CONNECTED");
put(LOST_DEVICE, "LOST_DEVICE");
- put(CONNECT_HFP, "CONNECT_HFP");
- put(DISCONNECT_HFP, "DISCONNECT_HFP");
- put(RETRY_HFP_CONNECTION, "RETRY_HFP_CONNECTION");
+ put(CONNECT_BT, "CONNECT_BT");
+ put(DISCONNECT_BT, "DISCONNECT_BT");
+ put(RETRY_BT_CONNECTION, "RETRY_BT_CONNECTION");
put(BT_AUDIO_IS_ON, "BT_AUDIO_IS_ON");
put(BT_AUDIO_LOST, "BT_AUDIO_LOST");
put(CONNECTION_TIMEOUT, "CONNECTION_TIMEOUT");
@@ -98,11 +98,11 @@
public static final int LOST_DEVICE = 2;
// arg2 (optional): the address of the specific device to connect to.
- public static final int CONNECT_HFP = 100;
+ public static final int CONNECT_BT = 100;
// No args.
- public static final int DISCONNECT_HFP = 101;
+ public static final int DISCONNECT_BT = 101;
// arg2: the address of the device to connect to.
- public static final int RETRY_HFP_CONNECTION = 102;
+ public static final int RETRY_BT_CONNECTION = 102;
// arg2: the address of the device that is on
public static final int BT_AUDIO_IS_ON = 200;
@@ -159,29 +159,29 @@
case LOST_DEVICE:
removeDevice((String) args.arg2);
break;
- case CONNECT_HFP:
+ case CONNECT_BT:
String actualAddress = connectBtAudio((String) args.arg2,
false /* switchingBtDevices*/);
if (actualAddress != null) {
transitionTo(getConnectingStateForAddress(actualAddress,
- "AudioOff/CONNECT_HFP"));
+ "AudioOff/CONNECT_BT"));
} else {
Log.w(LOG_TAG, "Tried to connect to %s but failed to connect to" +
- " any HFP device.", (String) args.arg2);
+ " any BT device.", (String) args.arg2);
}
break;
- case DISCONNECT_HFP:
+ case DISCONNECT_BT:
// Ignore.
break;
- case RETRY_HFP_CONNECTION:
- Log.i(LOG_TAG, "Retrying HFP connection to %s", (String) args.arg2);
+ case RETRY_BT_CONNECTION:
+ Log.i(LOG_TAG, "Retrying BT connection to %s", (String) args.arg2);
String retryAddress = connectBtAudio((String) args.arg2, args.argi1,
false /* switchingBtDevices*/);
if (retryAddress != null) {
transitionTo(getConnectingStateForAddress(retryAddress,
- "AudioOff/RETRY_HFP_CONNECTION"));
+ "AudioOff/RETRY_BT_CONNECTION"));
} else {
Log.i(LOG_TAG, "Retry failed.");
}
@@ -191,12 +191,12 @@
break;
case BT_AUDIO_IS_ON:
String address = (String) args.arg2;
- Log.w(LOG_TAG, "HFP audio unexpectedly turned on from device %s", address);
+ Log.w(LOG_TAG, "BT audio unexpectedly turned on from device %s", address);
transitionTo(getConnectedStateForAddress(address,
"AudioOff/BT_AUDIO_IS_ON"));
break;
case BT_AUDIO_LOST:
- Log.i(LOG_TAG, "Received HFP off for device %s while HFP off.",
+ Log.i(LOG_TAG, "Received BT off for device %s while BT off.",
(String) args.arg2);
mListener.onUnexpectedBluetoothStateChange();
break;
@@ -262,7 +262,7 @@
transitionToActualState();
}
break;
- case CONNECT_HFP:
+ case CONNECT_BT:
if (!switchingBtDevices) {
// Ignore repeated connection attempts to the same device
break;
@@ -272,16 +272,16 @@
true /* switchingBtDevices*/);
if (actualAddress != null) {
transitionTo(getConnectingStateForAddress(actualAddress,
- "AudioConnecting/CONNECT_HFP"));
+ "AudioConnecting/CONNECT_BT"));
} else {
Log.w(LOG_TAG, "Tried to connect to %s but failed" +
- " to connect to any HFP device.", (String) args.arg2);
+ " to connect to any BT device.", (String) args.arg2);
}
break;
- case DISCONNECT_HFP:
+ case DISCONNECT_BT:
mDeviceManager.disconnectAudio();
break;
- case RETRY_HFP_CONNECTION:
+ case RETRY_BT_CONNECTION:
if (!switchingBtDevices) {
Log.d(LOG_TAG, "Retry message came through while connecting.");
break;
@@ -291,7 +291,7 @@
true /* switchingBtDevices*/);
if (retryAddress != null) {
transitionTo(getConnectingStateForAddress(retryAddress,
- "AudioConnecting/RETRY_HFP_CONNECTION"));
+ "AudioConnecting/RETRY_BT_CONNECTION"));
} else {
Log.i(LOG_TAG, "Retry failed.");
}
@@ -318,7 +318,7 @@
mDeviceAddress);
transitionToActualState();
} else {
- Log.w(LOG_TAG, "Got HFP lost message for device %s while" +
+ Log.w(LOG_TAG, "Got BT lost message for device %s while" +
" connecting to %s.", address, mDeviceAddress);
mListener.onUnexpectedBluetoothStateChange();
}
@@ -351,7 +351,7 @@
public void enter() {
// Remove any of the retries that are still in the queue once any device becomes
// connected.
- removeMessages(RETRY_HFP_CONNECTION);
+ removeMessages(RETRY_BT_CONNECTION);
// Remove and add to ensure that the device is at the top.
mMostRecentlyUsedDevices.remove(mDeviceAddress);
mMostRecentlyUsedDevices.add(mDeviceAddress);
@@ -379,7 +379,7 @@
transitionToActualState();
}
break;
- case CONNECT_HFP:
+ case CONNECT_BT:
if (!switchingBtDevices) {
// Ignore connection to already connected device.
break;
@@ -389,16 +389,16 @@
true /* switchingBtDevices*/);
if (actualAddress != null) {
transitionTo(getConnectingStateForAddress(address,
- "AudioConnected/CONNECT_HFP"));
+ "AudioConnected/CONNECT_BT"));
} else {
Log.w(LOG_TAG, "Tried to connect to %s but failed" +
- " to connect to any HFP device.", (String) args.arg2);
+ " to connect to any BT device.", (String) args.arg2);
}
break;
- case DISCONNECT_HFP:
+ case DISCONNECT_BT:
mDeviceManager.disconnectAudio();
break;
- case RETRY_HFP_CONNECTION:
+ case RETRY_BT_CONNECTION:
if (!switchingBtDevices) {
Log.d(LOG_TAG, "Retry message came through while connected.");
break;
@@ -408,7 +408,7 @@
true /* switchingBtDevices*/);
if (retryAddress != null) {
transitionTo(getConnectingStateForAddress(retryAddress,
- "AudioConnected/RETRY_HFP_CONNECTION"));
+ "AudioConnected/RETRY_BT_CONNECTION"));
} else {
Log.i(LOG_TAG, "Retry failed.");
}
@@ -429,10 +429,10 @@
break;
case BT_AUDIO_LOST:
if (Objects.equals(mDeviceAddress, address) || address == null) {
- Log.i(LOG_TAG, "HFP connection with device %s lost.", mDeviceAddress);
+ Log.i(LOG_TAG, "BT connection with device %s lost.", mDeviceAddress);
transitionToActualState();
} else {
- Log.w(LOG_TAG, "Got HFP lost message for device %s while" +
+ Log.w(LOG_TAG, "Got BT lost message for device %s while" +
" connected to %s.", address, mDeviceAddress);
mListener.onUnexpectedBluetoothStateChange();
}
@@ -461,7 +461,7 @@
private BluetoothStateListener mListener;
private BluetoothDeviceManager mDeviceManager;
- // Tracks the active devices in the BT stack (HFP or hearing aid).
+ // Tracks the active devices in the BT stack (HFP or hearing aid or le audio).
private BluetoothDevice mHfpActiveDeviceCache = null;
private BluetoothDevice mHearingAidActiveDeviceCache = null;
private BluetoothDevice mLeAudioActiveDeviceCache = null;
@@ -505,7 +505,7 @@
}
/**
- * Returns whether there is a HFP device available to route audio to.
+ * Returns whether there is a BT device available to route audio to.
* @return true if there is a device, false otherwise.
*/
public boolean isBluetoothAvailable() {
@@ -550,16 +550,16 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = Log.createSubsession();
args.arg2 = address;
- sendMessage(CONNECT_HFP, args);
+ sendMessage(CONNECT_BT, args);
}
/**
- * Disconnects Bluetooth HFP audio.
+ * Disconnects Bluetooth audio.
*/
public void disconnectBluetoothAudio() {
SomeArgs args = SomeArgs.obtain();
args.arg1 = Log.createSubsession();
- sendMessage(DISCONNECT_HFP, args);
+ sendMessage(DISCONNECT_BT, args);
}
public void disconnectAudio() {
@@ -708,7 +708,7 @@
args.arg1 = Log.createSubsession();
args.arg2 = actualAddress;
args.argi1 = retryCount + 1;
- sendMessageDelayed(RETRY_HFP_CONNECTION, args,
+ sendMessageDelayed(RETRY_BT_CONNECTION, args,
mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
mContext.getContentResolver()));
}
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index f04537a..d1d48ce 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -85,14 +85,14 @@
@SmallTest
@Test
- public void testConnectHfpRetryWhileNotConnected() {
+ public void testConnectBtRetryWhileNotConnected() {
BluetoothRouteManager sm = setupStateMachine(
BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null, null, null, null, null);
when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
nullable(ContentResolver.class))).thenReturn(0L);
when(mBluetoothHeadset.connectAudio()).thenReturn(BluetoothStatusCodes.ERROR_UNKNOWN);
- executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, DEVICE1.getAddress());
+ executeRoutingAction(sm, BluetoothRouteManager.CONNECT_BT, DEVICE1.getAddress());
// Wait 3 times: for the first connection attempt, the retry attempt,
// the second retry, and once more to make sure there are only three attempts.
waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -146,7 +146,7 @@
@SmallTest
@Test
- public void testConnectHfpRetryWhileConnectedToAnotherDevice() {
+ public void testConnectBtRetryWhileConnectedToAnotherDevice() {
BluetoothRouteManager sm = setupStateMachine(
BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null, null, null, null,
@@ -154,7 +154,7 @@
when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
nullable(ContentResolver.class))).thenReturn(0L);
when(mBluetoothHeadset.connectAudio()).thenReturn(BluetoothStatusCodes.ERROR_UNKNOWN);
- executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, DEVICE2.getAddress());
+ executeRoutingAction(sm, BluetoothRouteManager.CONNECT_BT, DEVICE2.getAddress());
// Wait 3 times: the first connection attempt is accounted for in executeRoutingAction,
// so wait twice for the retry attempt, again to make sure there are only three attempts,
// and once more for good luck.
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
index 1a3112a..d923c90 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -440,7 +440,7 @@
.setInitialDevice(null)
.setConnectedDevices(DEVICE2, DEVICE1)
.setActiveDevice(DEVICE1)
- .setMessageType(BluetoothRouteManager.CONNECT_HFP)
+ .setMessageType(BluetoothRouteManager.CONNECT_BT)
.setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
.setExpectedBluetoothInteraction(CONNECT)
.setExpectedConnectionDevice(DEVICE1)
@@ -464,7 +464,7 @@
.build());
result.add(new BluetoothRouteTestParametersBuilder()
- .setName("Device loses HFP audio but remains connected. No fallback.")
+ .setName("Device loses BT audio but remains connected. No fallback.")
.setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
.setInitialDevice(DEVICE2)
.setConnectedDevices(DEVICE2)
@@ -477,7 +477,7 @@
.build());
result.add(new BluetoothRouteTestParametersBuilder()
- .setName("Device loses HFP audio but remains connected."
+ .setName("Device loses BT audio but remains connected."
+ " No fallback even though other devices available.")
.setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
.setInitialDevice(DEVICE2)
@@ -495,7 +495,7 @@
.setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
.setInitialDevice(DEVICE2)
.setConnectedDevices(DEVICE2, DEVICE1, DEVICE3)
- .setMessageType(BluetoothRouteManager.CONNECT_HFP)
+ .setMessageType(BluetoothRouteManager.CONNECT_BT)
.setMessageDevice(DEVICE3)
.setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
.setExpectedBluetoothInteraction(CONNECT_SWITCH_DEVICE)
@@ -509,7 +509,7 @@
.setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
.setInitialDevice(DEVICE2)
.setConnectedDevices(DEVICE2, DEVICE1, DEVICE3)
- .setMessageType(BluetoothRouteManager.CONNECT_HFP)
+ .setMessageType(BluetoothRouteManager.CONNECT_BT)
.setMessageDevice(DEVICE3)
.setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
.setExpectedBluetoothInteraction(CONNECT_SWITCH_DEVICE)
@@ -628,22 +628,22 @@
.build());
result.add(new BluetoothRouteTestParametersBuilder()
- .setName("Audio routing requests HFP disconnection while a device is active")
+ .setName("Audio routing requests BT disconnection while a device is active")
.setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
.setInitialDevice(DEVICE2)
.setConnectedDevices(DEVICE2, DEVICE3)
- .setMessageType(BluetoothRouteManager.DISCONNECT_HFP)
+ .setMessageType(BluetoothRouteManager.DISCONNECT_BT)
.setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED)
.setExpectedBluetoothInteraction(DISCONNECT)
.setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
.build());
result.add(new BluetoothRouteTestParametersBuilder()
- .setName("Audio routing requests HFP disconnection while a device is pending")
+ .setName("Audio routing requests BT disconnection while a device is pending")
.setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
.setInitialDevice(DEVICE2)
.setConnectedDevices(DEVICE2, DEVICE3)
- .setMessageType(BluetoothRouteManager.DISCONNECT_HFP)
+ .setMessageType(BluetoothRouteManager.DISCONNECT_BT)
.setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED)
.setExpectedBluetoothInteraction(DISCONNECT)
.setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
@@ -683,7 +683,7 @@
.setActiveDevice(DEVICE2)
.setConnectedDevices(DEVICE2, DEVICE3)
.setHearingAidBtDevices(Collections.singletonList(DEVICE2))
- .setMessageType(BluetoothRouteManager.CONNECT_HFP)
+ .setMessageType(BluetoothRouteManager.CONNECT_BT)
.setMessageDevice(DEVICE2)
.setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
.setExpectedBluetoothInteraction(NONE)
diff --git a/tests/src/com/android/server/telecom/tests/HeadsetMediaButtonTest.java b/tests/src/com/android/server/telecom/tests/HeadsetMediaButtonTest.java
new file mode 100644
index 0000000..6d15e60
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/HeadsetMediaButtonTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.tests;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.HeadsetMediaButton;
+import com.android.server.telecom.TelecomSystem;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(JUnit4.class)
+public class HeadsetMediaButtonTest extends TelecomTestCase {
+ private static final int TEST_TIMEOUT_MILLIS = 1000;
+
+ private HeadsetMediaButton mHeadsetMediaButton;
+
+ @Mock private CallsManager mMockCallsManager;
+ @Mock private HeadsetMediaButton.MediaSessionAdapter mMediaSessionAdapter;
+ private TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {};
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ mHeadsetMediaButton = new HeadsetMediaButton(mContext, mMockCallsManager, mLock,
+ mMediaSessionAdapter);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ mHeadsetMediaButton = null;
+ super.tearDown();
+ }
+
+ /**
+ * Nominal case; just add a call and remove it.
+ */
+ @Test
+ public void testAddCall() {
+ Call regularCall = getRegularCall();
+
+ when(mMockCallsManager.hasAnyCalls()).thenReturn(true);
+ mHeadsetMediaButton.onCallAdded(regularCall);
+ waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
+ verify(mMediaSessionAdapter).setActive(eq(true));
+ // ... and thus we see how the original code isn't amenable to tests.
+ when(mMediaSessionAdapter.isActive()).thenReturn(true);
+
+ when(mMockCallsManager.hasAnyCalls()).thenReturn(false);
+ mHeadsetMediaButton.onCallRemoved(regularCall);
+ waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
+ verify(mMediaSessionAdapter).setActive(eq(false));
+ }
+
+ /**
+ * Test a case where a regular call becomes an external call, and back again.
+ */
+ @Test
+ public void testRegularCallThatBecomesExternal() {
+ Call regularCall = getRegularCall();
+
+ // Start with a regular old call.
+ when(mMockCallsManager.hasAnyCalls()).thenReturn(true);
+ mHeadsetMediaButton.onCallAdded(regularCall);
+ waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
+ verify(mMediaSessionAdapter).setActive(eq(true));
+ when(mMediaSessionAdapter.isActive()).thenReturn(true);
+
+ // Change so it is external.
+ when(regularCall.isExternalCall()).thenReturn(true);
+ when(mMockCallsManager.hasAnyCalls()).thenReturn(false);
+ mHeadsetMediaButton.onExternalCallChanged(regularCall, true);
+ // Expect to set session inactive.
+ waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
+ verify(mMediaSessionAdapter).setActive(eq(false));
+
+ // For good measure lets make it non-external again.
+ when(regularCall.isExternalCall()).thenReturn(false);
+ when(mMockCallsManager.hasAnyCalls()).thenReturn(true);
+ mHeadsetMediaButton.onExternalCallChanged(regularCall, false);
+ // Expect to set session active.
+ waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS);
+ verify(mMediaSessionAdapter).setActive(eq(true));
+ }
+
+ /**
+ * @return a mock call instance of a regular non-external call.
+ */
+ private Call getRegularCall() {
+ Call regularCall = Mockito.mock(Call.class);
+ when(regularCall.isExternalCall()).thenReturn(false);
+ return regularCall;
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index dfc41a2..ddacf43 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -65,6 +65,7 @@
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.compat.testing.PlatformCompatChangeRule;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -917,13 +918,10 @@
/**
* Ensures that the {@link InCallController} will bind to an {@link InCallService} which
- * 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.
+ * supports third party app.
*/
@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)
@@ -967,9 +965,6 @@
// 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 sent by NotificationManager
- verify(mNotificationManager, times(1)).notify(eq(InCallController.NOTIFICATION_TAG),
- eq(InCallController.IN_CALL_SERVICE_NOTIFICATION_ID), any());
} finally {
mockitoSession.finishMocking();
}
@@ -1017,67 +1012,6 @@
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 {
diff --git a/tests/src/com/android/server/telecom/tests/LogUtilsTest.java b/tests/src/com/android/server/telecom/tests/LogUtilsTest.java
new file mode 100644
index 0000000..637dfbc
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/LogUtilsTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.tests;
+
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.telecom.LogUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class LogUtilsTest extends TelecomTestCase {
+
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Tests LogUtils#initLogging(Context) listeners cannot be initialized more than once by calling
+ * the init function multiple times. If the listeners are ever re-initialized, log spewing
+ * will occur.
+ *
+ * Note, LogUtils will already be initialized at the start of the testing framework,
+ * so you cannot assume it is 0 at the start of this testing class.
+ */
+ @SmallTest
+ @Test
+ public void testLogUtilsIsNotReInitialized() {
+
+ // assert the listeners of LogUtils are never re-initialized
+ assertTrue(LogUtils.getInitializedCounter() <= 1);
+ // call initLogging an arbitrary amount of times...
+ LogUtils.initLogging(mContext);
+ LogUtils.initLogging(mContext);
+ LogUtils.initLogging(mContext);
+ // assert the listeners of LogUtils are never re-initialized
+ assertTrue(LogUtils.getInitializedCounter() <= 1);
+ }
+}