Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40a5fa33 authored by Shuo Qian's avatar Shuo Qian Committed by Gerrit Code Review
Browse files

Merge "Binds to 3rd-party InCallService with MANAGE_ONGOING_CALL permission"

parents 4f7fa231 b159bc9b
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
        }
@@ -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;
        }

@@ -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;
+10 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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);

@@ -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.
+77 −6
Original line number Diff line number Diff line
@@ -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;
@@ -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";
@@ -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");
@@ -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);
@@ -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());
@@ -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
@@ -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 {
@@ -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);

@@ -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 */);
    }

@@ -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) {
@@ -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;