Don't complete in-call binding future for nonui
In InCallController, don't declare the binding process as complete until
a UI-present in-call service has finished binding.
Fixes: 155862765
Test: atest InCallControllerTests
Change-Id: I2019639e3031ead7a0fb0304aa3817ef002d992c
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 05dd61e..296d0b3 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -1554,7 +1554,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/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index e16b598..b0b6957 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
@@ -884,13 +895,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
@@ -903,13 +916,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.
@@ -1101,9 +1140,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 */);
}
@@ -1111,6 +1162,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 {
@@ -1145,7 +1206,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;
+ }
}