Loading src/com/android/server/telecom/InCallController.java +17 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; Loading Loading @@ -1542,9 +1543,17 @@ public class InCallController extends CallsManagerListenerBase { p -> packageManager.checkPermission( Manifest.permission.CONTROL_INCALL_EXPERIENCE, p) == PackageManager.PERMISSION_GRANTED); boolean hasAppOpsPermittedManageOngoingCalls = false; if (isAppOpsPermittedManageOngoingCalls(serviceInfo.applicationInfo.uid, serviceInfo.packageName)) { hasAppOpsPermittedManageOngoingCalls = true; } boolean isCarModeUIService = serviceInfo.metaData != null && serviceInfo.metaData.getBoolean( TelecomManager.METADATA_IN_CALL_SERVICE_CAR_MODE_UI, false); if (isCarModeUIService && hasControlInCallPermission) { return IN_CALL_SERVICE_TYPE_CAR_MODE_UI; } Loading @@ -1559,7 +1568,8 @@ public class InCallController extends CallsManagerListenerBase { // Also allow any in-call service that has the control-experience permission (to ensure // that it is a system app) and doesn't claim to show any UI. if (!isUIService && !isCarModeUIService && hasControlInCallPermission) { if (!isUIService && !isCarModeUIService && (hasControlInCallPermission || hasAppOpsPermittedManageOngoingCalls)) { return IN_CALL_SERVICE_TYPE_NON_UI; } Loading Loading @@ -1903,6 +1913,12 @@ public class InCallController extends CallsManagerListenerBase { } } private boolean isAppOpsPermittedManageOngoingCalls(int uid, String callingPackage) { return PermissionChecker.checkPermissionForPreflight(mContext, Manifest.permission.MANAGE_ONGOING_CALLS, PermissionChecker.PID_UNKNOWN, uid, callingPackage) == PermissionChecker.PERMISSION_GRANTED; } private void sendCrashedInCallServiceNotification(String packageName) { PackageManager packageManager = mContext.getPackageManager(); CharSequence appName; Loading tests/src/com/android/server/telecom/tests/ComponentContextFixture.java +10 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; Loading Loading @@ -485,6 +486,7 @@ public class ComponentContextFixture implements TestFixture<Context> { private final RoleManager mRoleManager = mock(RoleManager.class); private final TelephonyRegistryManager mTelephonyRegistryManager = mock(TelephonyRegistryManager.class); private final PermissionInfo mPermissionInfo = mock(PermissionInfo.class); private TelecomManager mTelecomManager = mock(TelecomManager.class); Loading Loading @@ -539,6 +541,14 @@ public class ComponentContextFixture implements TestFixture<Context> { matches(Manifest.permission.CALL_COMPANION_APP), anyString())) .thenReturn(PackageManager.PERMISSION_DENIED); try { when(mPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( mPermissionInfo); } catch (PackageManager.NameNotFoundException ex) { } when(mPermissionInfo.isAppOp()).thenReturn(true); // Used in CreateConnectionProcessor to rank emergency numbers by viability. // For the test, make them all equal to INVALID so that the preferred PhoneAccount will be // chosen. Loading tests/src/com/android/server/telecom/tests/InCallControllerTests.java +77 −6 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; Loading Loading @@ -121,6 +122,7 @@ public class InCallControllerTests extends TelecomTestCase { @Mock ClockProxy mClockProxy; @Mock Analytics.CallInfoImpl mCallInfo; @Mock NotificationManager mNotificationManager; @Mock PermissionInfo mMockPermissionInfo; private static final int CURRENT_USER_ID = 900973; private static final String DEF_PKG = "defpkg"; Loading @@ -141,6 +143,9 @@ public class InCallControllerTests extends TelecomTestCase { 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 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; private static final PhoneAccountHandle PA_HANDLE = new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"), "pa_id"); Loading Loading @@ -170,6 +175,8 @@ public class InCallControllerTests extends TelecomTestCase { mEmergencyCallHelper = new EmergencyCallHelper(mMockContext, mDefaultDialerCache, mTimeoutsAdapter); when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter); when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( mMockPermissionInfo); mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager, mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter, mEmergencyCallHelper, mCarModeTracker, mClockProxy); Loading Loading @@ -197,6 +204,8 @@ public class InCallControllerTests extends TelecomTestCase { return new String[] { CAR2_PKG }; case NONUI_UID: return new String[] { NONUI_PKG }; case APPOP_NONUI_UID: return new String[] { APPOP_NONUI_PKG }; } return null; }).when(mMockPackageManager).getPackagesForUid(anyInt()); Loading @@ -212,6 +221,9 @@ public class InCallControllerTests extends TelecomTestCase { when(mMockPackageManager.checkPermission( matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), matches(NONUI_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED); when(mMockPackageManager.checkPermission( matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), matches(APPOP_NONUI_PKG))).thenReturn(PackageManager.PERMISSION_DENIED); } @Override Loading Loading @@ -818,6 +830,49 @@ public class InCallControllerTests extends TelecomTestCase { verifyBinding(bindIntentCaptor, 0, DEF_PKG, DEF_CLASS); } /** * Ensures that the {@link InCallController} will bind to an {@link InCallService} which * supports third party app */ @MediumTest @Test public void testBindToService_ThirdPartyApp() throws Exception { 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*/); // Enable Third Party Companion App when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( mMockPermissionInfo); when(mMockPermissionInfo.isAppOp()).thenReturn(true); when(mMockAppOpsManager.unsafeCheckOpRawNoThrow(matches( AppOpsManager.OPSTR_MANAGE_ONGOING_CALLS), eq(APPOP_NONUI_UID), matches(APPOP_NONUI_PKG))).thenReturn(AppOpsManager.MODE_ALLOWED); // 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); } @MediumTest @Test public void testSanitizeContactName() throws Exception { Loading Loading @@ -930,8 +985,8 @@ public class InCallControllerTests extends TelecomTestCase { nullable(ContentResolver.class))).thenReturn(500L); when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall)); setupMockPackageManager(true /* default */, true /* nonui */, true /* system */, false /* external calls */, setupMockPackageManager(true /* default */, true /* nonui */, false /* appop_nonui */ , true /* system */, false /* external calls */, false /* self mgd in default*/, false /* self mgd in car*/); mInCallController.bindToServices(mMockCall); Loading Loading @@ -1191,9 +1246,21 @@ public class InCallControllerTests extends TelecomTestCase { }}; } private ResolveInfo getAppOpNonUiResolveinfo() { return new ResolveInfo() {{ serviceInfo = new ServiceInfo(); serviceInfo.packageName = APPOP_NONUI_PKG; serviceInfo.name = APPOP_NONUI_CLASS; serviceInfo.applicationInfo = new ApplicationInfo(); serviceInfo.applicationInfo.uid = APPOP_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, false, useSystemDialer, includeExternalCalls, setupMockPackageManager(useDefaultDialer, false, false, useSystemDialer, includeExternalCalls, false /* self mgd */, false /* self mgd */); } Loading @@ -1201,13 +1268,13 @@ public class InCallControllerTests extends TelecomTestCase { final boolean useSystemDialer, final boolean includeExternalCalls, final boolean includeSelfManagedCallsInDefaultDialer, final boolean includeSelfManagedCallsInCarModeDialer) { setupMockPackageManager(useDefaultDialer, false /* nonui */, useSystemDialer, includeExternalCalls, includeSelfManagedCallsInDefaultDialer, setupMockPackageManager(useDefaultDialer, false /* nonui */, false /* appop_nonui */, useSystemDialer, includeExternalCalls, includeSelfManagedCallsInDefaultDialer, includeSelfManagedCallsInCarModeDialer); } private void setupMockPackageManager(final boolean useDefaultDialer, final boolean useNonUiInCalls, final boolean useNonUiInCalls, final boolean useAppOpNonUiInCalls, final boolean useSystemDialer, final boolean includeExternalCalls, final boolean includeSelfManagedCallsInDefaultDialer, final boolean includeSelfManagedCallsInCarModeDialer) { Loading Loading @@ -1250,6 +1317,10 @@ public class InCallControllerTests extends TelecomTestCase { if (useNonUiInCalls) { resolveInfo.add(getNonUiResolveinfo()); } // InCallController uses a blank package name when querying for App Op non-ui incalls if (useAppOpNonUiInCalls) { resolveInfo.add(getAppOpNonUiResolveinfo()); } } return resolveInfo; Loading Loading
src/com/android/server/telecom/InCallController.java +17 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; Loading Loading @@ -1542,9 +1543,17 @@ public class InCallController extends CallsManagerListenerBase { p -> packageManager.checkPermission( Manifest.permission.CONTROL_INCALL_EXPERIENCE, p) == PackageManager.PERMISSION_GRANTED); boolean hasAppOpsPermittedManageOngoingCalls = false; if (isAppOpsPermittedManageOngoingCalls(serviceInfo.applicationInfo.uid, serviceInfo.packageName)) { hasAppOpsPermittedManageOngoingCalls = true; } boolean isCarModeUIService = serviceInfo.metaData != null && serviceInfo.metaData.getBoolean( TelecomManager.METADATA_IN_CALL_SERVICE_CAR_MODE_UI, false); if (isCarModeUIService && hasControlInCallPermission) { return IN_CALL_SERVICE_TYPE_CAR_MODE_UI; } Loading @@ -1559,7 +1568,8 @@ public class InCallController extends CallsManagerListenerBase { // Also allow any in-call service that has the control-experience permission (to ensure // that it is a system app) and doesn't claim to show any UI. if (!isUIService && !isCarModeUIService && hasControlInCallPermission) { if (!isUIService && !isCarModeUIService && (hasControlInCallPermission || hasAppOpsPermittedManageOngoingCalls)) { return IN_CALL_SERVICE_TYPE_NON_UI; } Loading Loading @@ -1903,6 +1913,12 @@ public class InCallController extends CallsManagerListenerBase { } } private boolean isAppOpsPermittedManageOngoingCalls(int uid, String callingPackage) { return PermissionChecker.checkPermissionForPreflight(mContext, Manifest.permission.MANAGE_ONGOING_CALLS, PermissionChecker.PID_UNKNOWN, uid, callingPackage) == PermissionChecker.PERMISSION_GRANTED; } private void sendCrashedInCallServiceNotification(String packageName) { PackageManager packageManager = mContext.getPackageManager(); CharSequence appName; Loading
tests/src/com/android/server/telecom/tests/ComponentContextFixture.java +10 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; Loading Loading @@ -485,6 +486,7 @@ public class ComponentContextFixture implements TestFixture<Context> { private final RoleManager mRoleManager = mock(RoleManager.class); private final TelephonyRegistryManager mTelephonyRegistryManager = mock(TelephonyRegistryManager.class); private final PermissionInfo mPermissionInfo = mock(PermissionInfo.class); private TelecomManager mTelecomManager = mock(TelecomManager.class); Loading Loading @@ -539,6 +541,14 @@ public class ComponentContextFixture implements TestFixture<Context> { matches(Manifest.permission.CALL_COMPANION_APP), anyString())) .thenReturn(PackageManager.PERMISSION_DENIED); try { when(mPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( mPermissionInfo); } catch (PackageManager.NameNotFoundException ex) { } when(mPermissionInfo.isAppOp()).thenReturn(true); // Used in CreateConnectionProcessor to rank emergency numbers by viability. // For the test, make them all equal to INVALID so that the preferred PhoneAccount will be // chosen. Loading
tests/src/com/android/server/telecom/tests/InCallControllerTests.java +77 −6 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; Loading Loading @@ -121,6 +122,7 @@ public class InCallControllerTests extends TelecomTestCase { @Mock ClockProxy mClockProxy; @Mock Analytics.CallInfoImpl mCallInfo; @Mock NotificationManager mNotificationManager; @Mock PermissionInfo mMockPermissionInfo; private static final int CURRENT_USER_ID = 900973; private static final String DEF_PKG = "defpkg"; Loading @@ -141,6 +143,9 @@ public class InCallControllerTests extends TelecomTestCase { 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 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; private static final PhoneAccountHandle PA_HANDLE = new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"), "pa_id"); Loading Loading @@ -170,6 +175,8 @@ public class InCallControllerTests extends TelecomTestCase { mEmergencyCallHelper = new EmergencyCallHelper(mMockContext, mDefaultDialerCache, mTimeoutsAdapter); when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter); when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( mMockPermissionInfo); mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager, mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter, mEmergencyCallHelper, mCarModeTracker, mClockProxy); Loading Loading @@ -197,6 +204,8 @@ public class InCallControllerTests extends TelecomTestCase { return new String[] { CAR2_PKG }; case NONUI_UID: return new String[] { NONUI_PKG }; case APPOP_NONUI_UID: return new String[] { APPOP_NONUI_PKG }; } return null; }).when(mMockPackageManager).getPackagesForUid(anyInt()); Loading @@ -212,6 +221,9 @@ public class InCallControllerTests extends TelecomTestCase { when(mMockPackageManager.checkPermission( matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), matches(NONUI_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED); when(mMockPackageManager.checkPermission( matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), matches(APPOP_NONUI_PKG))).thenReturn(PackageManager.PERMISSION_DENIED); } @Override Loading Loading @@ -818,6 +830,49 @@ public class InCallControllerTests extends TelecomTestCase { verifyBinding(bindIntentCaptor, 0, DEF_PKG, DEF_CLASS); } /** * Ensures that the {@link InCallController} will bind to an {@link InCallService} which * supports third party app */ @MediumTest @Test public void testBindToService_ThirdPartyApp() throws Exception { 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*/); // Enable Third Party Companion App when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( mMockPermissionInfo); when(mMockPermissionInfo.isAppOp()).thenReturn(true); when(mMockAppOpsManager.unsafeCheckOpRawNoThrow(matches( AppOpsManager.OPSTR_MANAGE_ONGOING_CALLS), eq(APPOP_NONUI_UID), matches(APPOP_NONUI_PKG))).thenReturn(AppOpsManager.MODE_ALLOWED); // 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); } @MediumTest @Test public void testSanitizeContactName() throws Exception { Loading Loading @@ -930,8 +985,8 @@ public class InCallControllerTests extends TelecomTestCase { nullable(ContentResolver.class))).thenReturn(500L); when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall)); setupMockPackageManager(true /* default */, true /* nonui */, true /* system */, false /* external calls */, setupMockPackageManager(true /* default */, true /* nonui */, false /* appop_nonui */ , true /* system */, false /* external calls */, false /* self mgd in default*/, false /* self mgd in car*/); mInCallController.bindToServices(mMockCall); Loading Loading @@ -1191,9 +1246,21 @@ public class InCallControllerTests extends TelecomTestCase { }}; } private ResolveInfo getAppOpNonUiResolveinfo() { return new ResolveInfo() {{ serviceInfo = new ServiceInfo(); serviceInfo.packageName = APPOP_NONUI_PKG; serviceInfo.name = APPOP_NONUI_CLASS; serviceInfo.applicationInfo = new ApplicationInfo(); serviceInfo.applicationInfo.uid = APPOP_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, false, useSystemDialer, includeExternalCalls, setupMockPackageManager(useDefaultDialer, false, false, useSystemDialer, includeExternalCalls, false /* self mgd */, false /* self mgd */); } Loading @@ -1201,13 +1268,13 @@ public class InCallControllerTests extends TelecomTestCase { final boolean useSystemDialer, final boolean includeExternalCalls, final boolean includeSelfManagedCallsInDefaultDialer, final boolean includeSelfManagedCallsInCarModeDialer) { setupMockPackageManager(useDefaultDialer, false /* nonui */, useSystemDialer, includeExternalCalls, includeSelfManagedCallsInDefaultDialer, setupMockPackageManager(useDefaultDialer, false /* nonui */, false /* appop_nonui */, useSystemDialer, includeExternalCalls, includeSelfManagedCallsInDefaultDialer, includeSelfManagedCallsInCarModeDialer); } private void setupMockPackageManager(final boolean useDefaultDialer, final boolean useNonUiInCalls, final boolean useNonUiInCalls, final boolean useAppOpNonUiInCalls, final boolean useSystemDialer, final boolean includeExternalCalls, final boolean includeSelfManagedCallsInDefaultDialer, final boolean includeSelfManagedCallsInCarModeDialer) { Loading Loading @@ -1250,6 +1317,10 @@ public class InCallControllerTests extends TelecomTestCase { if (useNonUiInCalls) { resolveInfo.add(getNonUiResolveinfo()); } // InCallController uses a blank package name when querying for App Op non-ui incalls if (useAppOpNonUiInCalls) { resolveInfo.add(getAppOpNonUiResolveinfo()); } } return resolveInfo; Loading