Merge changes from topic "telecom-cts-presubmit"
* changes:
Remove extra space
Add CtsTelecomTestCases to presubmit
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index a562021..28f9df9 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -459,6 +459,8 @@
switch (msg.what) {
case SWITCH_EARPIECE:
case USER_SWITCH_EARPIECE:
+ case SPEAKER_ON:
+ // Ignore speakerphone state changes outside of calls.
case SPEAKER_OFF:
// Nothing to do here
return HANDLED;
@@ -484,7 +486,6 @@
return HANDLED;
case SWITCH_SPEAKER:
case USER_SWITCH_SPEAKER:
- case SPEAKER_ON:
transitionTo(mQuiescentSpeakerRoute);
return HANDLED;
case SWITCH_FOCUS:
@@ -677,12 +678,13 @@
return HANDLED;
case SWITCH_HEADSET:
case USER_SWITCH_HEADSET:
+ case SPEAKER_ON:
+ // Ignore speakerphone state changes outside of calls.
case SPEAKER_OFF:
// Nothing to do
return HANDLED;
case SWITCH_SPEAKER:
case USER_SWITCH_SPEAKER:
- case SPEAKER_ON:
transitionTo(mQuiescentSpeakerRoute);
return HANDLED;
case SWITCH_FOCUS:
@@ -1012,6 +1014,8 @@
return HANDLED;
case SWITCH_BLUETOOTH:
case USER_SWITCH_BLUETOOTH:
+ case SPEAKER_ON:
+ // Ignore speakerphone state changes outside of calls.
case SPEAKER_OFF:
// Nothing to do
return HANDLED;
@@ -1025,7 +1029,6 @@
return HANDLED;
case SWITCH_SPEAKER:
case USER_SWITCH_SPEAKER:
- case SPEAKER_ON:
transitionTo(mQuiescentSpeakerRoute);
return HANDLED;
case SWITCH_FOCUS:
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 158c7e1..0b6b55e 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -1289,7 +1289,12 @@
* Unbinds an existing bound connection to the in-call app.
*/
private void unbindFromServices() {
- mContext.unregisterReceiver(mPackageChangedReceiver);
+ try {
+ mContext.unregisterReceiver(mPackageChangedReceiver);
+ } catch (IllegalArgumentException e) {
+ // Ignore this -- we may or may not have registered it, but when we bind, we want to
+ // unregister no matter what.
+ }
if (mInCallServiceConnection != null) {
mInCallServiceConnection.disconnect();
mInCallServiceConnection = null;
@@ -1621,7 +1626,11 @@
inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
} catch (RemoteException ignored) {
}
- mBindingFuture.complete(true);
+ // Don't complete the binding future for non-ui incalls
+ if (info.getType() != IN_CALL_SERVICE_TYPE_NON_UI) {
+ mBindingFuture.complete(true);
+ }
+
Log.i(this, "%s calls sent to InCallService.", numCallsSent);
return true;
}
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index d2aa544..9421639 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -41,6 +41,7 @@
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
+import android.provider.BlockedNumberContract;
import android.provider.Settings;
import android.telecom.Log;
import android.telecom.PhoneAccount;
@@ -1477,6 +1478,28 @@
}
@Override
+ public void stopBlockSuppression() {
+ try {
+ Log.startSession("TSI.sBS");
+ enforceModifyPermission();
+ if (Binder.getCallingUid() != Process.SHELL_UID
+ && Binder.getCallingUid() != Process.ROOT_UID) {
+ throw new SecurityException("Shell-only API.");
+ }
+ synchronized (mLock) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BlockedNumberContract.SystemContract.endBlockSuppression(mContext);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public TelecomAnalytics dumpCallAnalytics() {
try {
Log.startSession("TSI.dCA");
diff --git a/src/com/android/server/telecom/ui/IncomingCallNotifier.java b/src/com/android/server/telecom/ui/IncomingCallNotifier.java
index 6e203aa..edea89b 100644
--- a/src/com/android/server/telecom/ui/IncomingCallNotifier.java
+++ b/src/com/android/server/telecom/ui/IncomingCallNotifier.java
@@ -41,6 +41,7 @@
import com.android.server.telecom.TelecomBroadcastIntentProcessor;
import com.android.server.telecom.components.TelecomBroadcastReceiver;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -68,7 +69,7 @@
public static final int NOTIFICATION_INCOMING_CALL = 1;
@VisibleForTesting
public static final String NOTIFICATION_TAG = IncomingCallNotifier.class.getSimpleName();
-
+ private final Object mLock = new Object();
public final Call.ListenerBase mCallListener = new Call.ListenerBase() {
@Override
@@ -104,8 +105,10 @@
@Override
public void onCallAdded(Call call) {
- if (!mCalls.contains(call)) {
- mCalls.add(call);
+ synchronized (mLock) {
+ if (!mCalls.contains(call)) {
+ mCalls.add(call);
+ }
}
updateIncomingCall();
@@ -113,10 +116,11 @@
@Override
public void onCallRemoved(Call call) {
- if (mCalls.contains(call)) {
- mCalls.remove(call);
+ synchronized (mLock) {
+ if (mCalls.contains(call)) {
+ mCalls.remove(call);
+ }
}
-
updateIncomingCall();
}
@@ -130,11 +134,16 @@
* UI.
*/
private void updateIncomingCall() {
- Optional<Call> incomingCallOp = mCalls.stream()
- .filter(call -> call.isSelfManaged() && call.isIncoming() &&
- call.getState() == CallState.RINGING &&
- call.getHandoverState() == HandoverState.HANDOVER_NONE)
- .findFirst();
+ Optional<Call> incomingCallOp;
+ synchronized (mLock) {
+ incomingCallOp = mCalls.stream()
+ .filter(Objects::nonNull)
+ .filter(call -> call.isSelfManaged() && call.isIncoming() &&
+ call.getState() == CallState.RINGING &&
+ call.getHandoverState() == HandoverState.HANDOVER_NONE)
+ .findFirst();
+ }
+
Call incomingCall = incomingCallOp.orElse(null);
if (incomingCall != null && mCallsManagerProxy != null &&
!mCallsManagerProxy.hasUnholdableCallsForOtherConnectionService(
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 3fb2a84..02b35a4 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -27,4 +27,10 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
+ <object type="module_controller"
+ class="com.android.tradefed.testtype.suite.module.TestFailureModuleController">
+ <option name="screenshot-on-failure" value="false" />
+ <option name="bugreportz-on-failure" value="false" />
+ <option name="logcat-on-failure" value="true" />
+ </object>
</configuration>
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
index 58f1ee7..bf105e5 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
@@ -384,8 +384,12 @@
// rest of the system
verifyNoSystemAudioChanges();
+ // Special case for SPEAKER_ON -- we don't expect any route transitions to happen when
+ // there are no calls, so set the expected state to the initial route.
+ int expectedRoute = (mParams.action == CallAudioRouteStateMachine.SPEAKER_ON)
+ ? mParams.initialRoute : mParams.expectedRoute;
// Verify the end state
- CallAudioState expectedState = new CallAudioState(false, mParams.expectedRoute,
+ CallAudioState expectedState = new CallAudioState(false, expectedRoute,
mParams.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER,
mParams.expectedBluetoothDevice, mParams.availableBluetoothDevices);
assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index c31761f..2b461f4 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.ArgumentMatchers.nullable;
@@ -98,6 +99,7 @@
import java.util.Collections;
import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
@RunWith(JUnit4.class)
@@ -133,6 +135,10 @@
private static final String CAR2_CLASS = "carcls";
private static final int CAR_UID = 4;
private static final int CAR2_UID = 5;
+ private static final String NONUI_PKG = "nonui_pkg";
+ private static final String NONUI_CLASS = "nonui_cls";
+ private static final int NONUI_UID = 6;
+
private static final PhoneAccountHandle PA_HANDLE =
new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"), "pa_id");
@@ -177,6 +183,8 @@
return new String[] { CAR_PKG };
case CAR2_UID:
return new String[] { CAR2_PKG };
+ case NONUI_UID:
+ return new String[] { NONUI_PKG };
}
return null;
}).when(mMockPackageManager).getPackagesForUid(anyInt());
@@ -189,6 +197,9 @@
when(mMockPackageManager.checkPermission(
matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
matches(CAR2_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mMockPackageManager.checkPermission(
+ matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
+ matches(NONUI_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED);
}
@Override
@@ -889,13 +900,15 @@
nullable(ContentResolver.class))).thenReturn(500L);
when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall));
- setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+ setupMockPackageManager(true /* default */, true /* nonui */, true /* system */,
+ false /* external calls */,
+ false /* self mgd in default*/, false /* self mgd in car*/);
mInCallController.bindToServices(mMockCall);
ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
ArgumentCaptor<ServiceConnection> serviceConnectionCaptor =
ArgumentCaptor.forClass(ServiceConnection.class);
- verify(mMockContext, times(1)).bindServiceAsUser(
+ verify(mMockContext, times(2)).bindServiceAsUser(
bindIntentCaptor.capture(),
serviceConnectionCaptor.capture(),
eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
@@ -908,13 +921,39 @@
// Start the connection, make sure we don't unbind, and make sure that we don't send
// anything to the in-call service yet.
- ServiceConnection serviceConnection = serviceConnectionCaptor.getValue();
+ List<ServiceConnection> serviceConnections = serviceConnectionCaptor.getAllValues();
+ List<Intent> intents = bindIntentCaptor.getAllValues();
+
+ // Find the non-ui service and have it connect first.
+ int nonUiIdx = findFirstIndexMatching(intents,
+ i -> NONUI_PKG.equals(i.getComponent().getPackageName()));
+ if (nonUiIdx < 0) {
+ fail("Did not bind to non-ui incall");
+ }
+
+ {
+ ComponentName nonUiComponentName = new ComponentName(NONUI_PKG, NONUI_CLASS);
+ IBinder mockBinder = mock(IBinder.class);
+ IInCallService mockInCallService = mock(IInCallService.class);
+ when(mockBinder.queryLocalInterface(anyString())).thenReturn(mockInCallService);
+ serviceConnections.get(nonUiIdx).onServiceConnected(nonUiComponentName, mockBinder);
+
+ // Make sure the non-ui binding didn't trigger the future.
+ assertFalse(bindTimeout.isDone());
+ }
+
+ int defDialerIdx = findFirstIndexMatching(intents,
+ i -> DEF_PKG.equals(i.getComponent().getPackageName()));
+ if (defDialerIdx < 0) {
+ fail("Did not bind to default dialer incall");
+ }
+
ComponentName defDialerComponentName = new ComponentName(DEF_PKG, DEF_CLASS);
IBinder mockBinder = mock(IBinder.class);
IInCallService mockInCallService = mock(IInCallService.class);
when(mockBinder.queryLocalInterface(anyString())).thenReturn(mockInCallService);
- serviceConnection.onServiceConnected(defDialerComponentName, mockBinder);
+ serviceConnections.get(defDialerIdx).onServiceConnected(defDialerComponentName, mockBinder);
verify(mockInCallService).setInCallAdapter(nullable(IInCallAdapter.class));
// Make sure that the future completed without timing out.
@@ -1110,9 +1149,21 @@
}};
}
+ private ResolveInfo getNonUiResolveinfo() {
+ return new ResolveInfo() {{
+ serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = NONUI_PKG;
+ serviceInfo.name = NONUI_CLASS;
+ serviceInfo.applicationInfo = new ApplicationInfo();
+ serviceInfo.applicationInfo.uid = NONUI_UID;
+ serviceInfo.enabled = true;
+ serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
+ }};
+ }
+
private void setupMockPackageManager(final boolean useDefaultDialer,
final boolean useSystemDialer, final boolean includeExternalCalls) {
- setupMockPackageManager(useDefaultDialer, useSystemDialer, includeExternalCalls,
+ setupMockPackageManager(useDefaultDialer, false, useSystemDialer, includeExternalCalls,
false /* self mgd */, false /* self mgd */);
}
@@ -1120,6 +1171,16 @@
final boolean useSystemDialer, final boolean includeExternalCalls,
final boolean includeSelfManagedCallsInDefaultDialer,
final boolean includeSelfManagedCallsInCarModeDialer) {
+ setupMockPackageManager(useDefaultDialer, false /* nonui */, useSystemDialer,
+ includeExternalCalls, includeSelfManagedCallsInDefaultDialer,
+ includeSelfManagedCallsInCarModeDialer);
+ }
+
+ private void setupMockPackageManager(final boolean useDefaultDialer,
+ final boolean useNonUiInCalls,
+ final boolean useSystemDialer, final boolean includeExternalCalls,
+ final boolean includeSelfManagedCallsInDefaultDialer,
+ final boolean includeSelfManagedCallsInCarModeDialer) {
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
@@ -1154,7 +1215,13 @@
resolveInfo.add(getCarModeResolveinfo(CAR2_PKG, CAR2_CLASS,
includeExternalCalls, includeSelfManagedCallsInCarModeDialer));
}
+ } else {
+ // InCallController uses a blank package name when querying for non-ui incalls
+ if (useNonUiInCalls) {
+ resolveInfo.add(getNonUiResolveinfo());
+ }
}
+
return resolveInfo;
}
}).when(mMockPackageManager).queryIntentServicesAsUser(
diff --git a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
index b0b1ec0..264e087 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
@@ -25,8 +25,10 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
public abstract class TelecomTestCase {
protected static final String TESTING_TAG = "Telecom-TEST";
@@ -75,4 +77,13 @@
}
}
}
+
+ protected static <T> int findFirstIndexMatching(List<T> items, Predicate<T> matcher) {
+ for (int i = 0; i < items.size(); i++) {
+ if (matcher.test(items.get(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
}