Loading src/com/android/server/telecom/InCallController.java +18 −2 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 @@ -1594,9 +1595,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 @@ -1611,7 +1620,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 @@ -2028,6 +2038,12 @@ public class InCallController extends CallsManagerListenerBase { return mCallsManager.getAudioState().isMuted(); } 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 @@ -122,6 +123,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 @@ -142,6 +144,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 @@ -173,6 +178,8 @@ public class InCallControllerTests extends TelecomTestCase { when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter); when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE))) .thenReturn(mNotificationManager); when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( mMockPermissionInfo); mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager, mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter, mEmergencyCallHelper, mCarModeTracker, mClockProxy); Loading @@ -198,6 +205,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 @@ -213,6 +222,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); when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0)); } Loading Loading @@ -822,6 +834,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 @@ -934,8 +989,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 @@ -1195,9 +1250,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 @@ -1205,13 +1272,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 @@ -1254,6 +1321,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 +18 −2 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 @@ -1594,9 +1595,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 @@ -1611,7 +1620,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 @@ -2028,6 +2038,12 @@ public class InCallController extends CallsManagerListenerBase { return mCallsManager.getAudioState().isMuted(); } 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 @@ -122,6 +123,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 @@ -142,6 +144,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 @@ -173,6 +178,8 @@ public class InCallControllerTests extends TelecomTestCase { when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter); when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE))) .thenReturn(mNotificationManager); when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( mMockPermissionInfo); mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager, mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter, mEmergencyCallHelper, mCarModeTracker, mClockProxy); Loading @@ -198,6 +205,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 @@ -213,6 +222,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); when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0)); } Loading Loading @@ -822,6 +834,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 @@ -934,8 +989,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 @@ -1195,9 +1250,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 @@ -1205,13 +1272,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 @@ -1254,6 +1321,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