Merge "Make sure late connected non ui ICS are unbound." into udc-dev
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 2fc59fa..430305b 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -801,6 +801,9 @@
}
Call callToConnectWith = mCallIdMapper.getCalls().iterator().next();
for (InCallServiceBindingConnection newConnection : newConnections) {
+ // Ensure we track the new sub-connection so that when we later disconnect we will
+ // be able to disconnect it.
+ mSubConnections.add(newConnection);
newConnection.connect(callToConnectWith);
}
}
@@ -2189,7 +2192,8 @@
* Adds the call to the list of calls tracked by the {@link InCallController}.
* @param call The call to add.
*/
- private void addCall(Call call) {
+ @VisibleForTesting
+ public void addCall(Call call) {
if (mCallIdMapper.getCalls().size() == 0) {
mAppOpsManager.startWatchingActive(new String[] { OPSTR_RECORD_AUDIO },
java.lang.Runnable::run, this);
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index a08184b..2db07ab 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -35,7 +35,6 @@
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -53,10 +52,12 @@
import android.app.UiModeManager;
import android.content.AttributionSource;
import android.content.AttributionSourceState;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.PermissionChecker;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
@@ -66,7 +67,7 @@
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.compat.testing.PlatformCompatChangeRule;
-import android.os.Binder;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -123,14 +124,13 @@
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
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;
@@ -156,25 +156,25 @@
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
- private static final int CURRENT_USER_ID = 900973;
+ private static final int CURRENT_USER_ID = 9;
private static final String DEF_PKG = "defpkg";
private static final String DEF_CLASS = "defcls";
- private static final int DEF_UID = 1;
+ private static final int DEF_UID = 900972;
private static final String SYS_PKG = "syspkg";
private static final String SYS_CLASS = "syscls";
- private static final int SYS_UID = 2;
+ private static final int SYS_UID = 900971;
private static final String COMPANION_PKG = "cpnpkg";
private static final String COMPANION_CLASS = "cpncls";
- private static final int COMPANION_UID = 3;
+ private static final int COMPANION_UID = 900970;
private static final String CAR_PKG = "carpkg";
private static final String CAR2_PKG = "carpkg2";
private static final String CAR_CLASS = "carcls";
private static final String CAR2_CLASS = "carcls";
- private static final int CAR_UID = 4;
- private static final int CAR2_UID = 5;
+ private static final int CAR_UID = 900969;
+ private static final int CAR2_UID = 900968;
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 int NONUI_UID = 900973;
private static final String APPOP_NONUI_PKG = "appop_nonui_pkg";
private static final String APPOP_NONUI_CLASS = "appop_nonui_cls";
private static final int APPOP_NONUI_UID = 7;
@@ -190,6 +190,7 @@
private EmergencyCallHelper mEmergencyCallHelper;
private SystemStateHelper.SystemStateListener mSystemStateListener;
private CarModeTracker mCarModeTracker = spy(new CarModeTracker());
+ private BroadcastReceiver mRegisteredReceiver;
private final int serviceBindingFlags = Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
@@ -202,6 +203,7 @@
MockitoAnnotations.initMocks(this);
when(mMockCall.getAnalytics()).thenReturn(new Analytics.CallInfo());
when(mMockCall.getUserHandleFromTargetPhoneAccount()).thenReturn(mUserHandle);
+ when(mMockCall.getId()).thenReturn("TC@1");
doReturn(mMockResources).when(mMockContext).getResources();
doReturn(mMockAppOpsManager).when(mMockContext).getSystemService(AppOpsManager.class);
doReturn(SYS_PKG).when(mMockResources).getString(
@@ -225,6 +227,12 @@
mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter,
mEmergencyCallHelper, mCarModeTracker, mClockProxy);
+ // Capture the broadcast receiver registered.
+ doAnswer(invocation -> {
+ mRegisteredReceiver = invocation.getArgument(0);
+ return null;
+ }).when(mMockContext).registerReceiver(any(BroadcastReceiver.class),
+ any(IntentFilter.class));
ArgumentCaptor<SystemStateHelper.SystemStateListener> systemStateListenerArgumentCaptor
= ArgumentCaptor.forClass(SystemStateHelper.SystemStateListener.class);
@@ -879,6 +887,114 @@
}
/**
+ * This test verifies the behavior of Telecom when the system dialer crashes on binding and must
+ * be restarted. Specifically, it ensures when the system dialer crashes we revoke the runtime
+ * location permission, and when it restarts we re-grant the permission.
+ * @throws Exception
+ */
+ @MediumTest
+ @Test
+ public void testBindToLateConnectionNonUiIcs() throws Exception {
+ Bundle callExtras = new Bundle();
+ callExtras.putBoolean("whatever", true);
+
+ // Make a basic call and bind to the default dialer.
+ when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockCallsManager.isInEmergencyCall()).thenReturn(true);
+ when(mMockCall.isEmergencyCall()).thenReturn(true);
+ when(mMockContext.getSystemService(eq(UserManager.class)))
+ .thenReturn(mMockUserManager);
+ when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
+ when(mMockCall.isIncoming()).thenReturn(false);
+ when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
+ when(mMockCall.getIntentExtras()).thenReturn(callExtras);
+ when(mMockCall.isExternalCall()).thenReturn(false);
+ when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID))
+ .thenReturn(DEF_PKG);
+ ArgumentCaptor<ServiceConnection> serviceConnectionCaptor =
+ ArgumentCaptor.forClass(ServiceConnection.class);
+ when(mMockContext.bindServiceAsUser(any(Intent.class), serviceConnectionCaptor.capture(),
+ eq(serviceBindingFlags),
+ eq(mUserHandle))).thenReturn(true);
+ when(mTimeoutsAdapter.getEmergencyCallbackWindowMillis(any(ContentResolver.class)))
+ .thenReturn(300_000L);
+
+ // Setup package manager; there is a dialer and disable non-ui ICS
+ when(mMockPackageManager.queryIntentServicesAsUser(
+ any(Intent.class), anyInt(), anyInt())).thenReturn(
+ Arrays.asList(
+ getDefResolveInfo(false /* externalCalls */, false /* selfMgd */),
+ getNonUiResolveinfo(false /* selfManaged */,
+ false /* isEnabled */)
+ )
+ );
+ when(mMockPackageManager
+ .getComponentEnabledSetting(new ComponentName(DEF_PKG, DEF_CLASS)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+ when(mMockPackageManager
+ .getComponentEnabledSetting(new ComponentName(NONUI_PKG, NONUI_CLASS)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+
+ mInCallController.addCall(mMockCall);
+ mInCallController.bindToServices(mMockCall);
+
+ // There will be 4 calls for the various types of ICS.
+ verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
+ any(Intent.class),
+ eq(PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS),
+ eq(CURRENT_USER_ID));
+
+ // Verify bind to the dialer
+ ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockContext, times(1)).bindServiceAsUser(
+ bindIntentCaptor.capture(),
+ any(ServiceConnection.class),
+ eq(serviceBindingFlags),
+ eq(mUserHandle));
+
+ Intent bindIntent = bindIntentCaptor.getValue();
+ assertEquals(InCallService.SERVICE_INTERFACE, bindIntent.getAction());
+ assertEquals(SYS_PKG, bindIntent.getComponent().getPackageName());
+ assertEquals(SYS_CLASS, bindIntent.getComponent().getClassName());
+
+ // Setup mocks to enable nonui ICS
+ when(mMockPackageManager.queryIntentServicesAsUser(
+ any(Intent.class), anyInt(), anyInt())).thenReturn(
+ Arrays.asList(
+ getDefResolveInfo(false /* externalCalls */, false /* selfMgd */),
+ getNonUiResolveinfo(false /* selfManaged */,
+ true /* isEnabled */)
+ )
+ );
+ when(mMockPackageManager
+ .getComponentEnabledSetting(new ComponentName(NONUI_PKG, NONUI_CLASS)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+
+ // Emulate a late enable of the non-ui ICS
+ Intent packageUpdated = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ packageUpdated.setData(Uri.fromParts("package", NONUI_PKG, null));
+ packageUpdated.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST,
+ new String[] {NONUI_CLASS});
+ packageUpdated.putExtra(Intent.EXTRA_UID, NONUI_UID);
+ mRegisteredReceiver.onReceive(mMockContext, packageUpdated);
+
+ // Now, we expect to auto-rebind to the system dialer (verify 2 times since this is the
+ // second binding).
+ verify(mMockContext, times(2)).bindServiceAsUser(
+ bindIntentCaptor.capture(),
+ any(ServiceConnection.class),
+ eq(serviceBindingFlags),
+ eq(mUserHandle));
+
+ // Unbind!
+ mInCallController.unbindFromServices(UserHandle.of(CURRENT_USER_ID));
+
+ // Make sure we unbound 2 times
+ verify(mMockContext, times(2)).unbindService(any(ServiceConnection.class));
+ }
+
+ /**
* Ensures that the {@link InCallController} will bind to an {@link InCallService} which
* supports external calls.
*/
@@ -1558,14 +1674,14 @@
}};
}
- private ResolveInfo getNonUiResolveinfo(boolean supportsSelfManaged) {
+ private ResolveInfo getNonUiResolveinfo(boolean supportsSelfManaged, boolean isEnabled) {
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.enabled = isEnabled;
serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
serviceInfo.metaData = new Bundle();
if (supportsSelfManaged) {
@@ -1656,7 +1772,7 @@
} else {
// InCallController uses a blank package name when querying for non-ui incalls
if (useNonUiInCalls) {
- resolveInfo.add(getNonUiResolveinfo(includeSelfManagedCallsInNonUi));
+ resolveInfo.add(getNonUiResolveinfo(includeSelfManagedCallsInNonUi, true));
}
// InCallController uses a blank package name when querying for App Op non-ui incalls
if (useAppOpNonUiInCalls) {