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

Commit be8c99ad authored by Brad Ebinger's avatar Brad Ebinger Committed by Android (Google) Code Review
Browse files

Merge "Ensure installed packages do not leak when calling placeCall" into udc-dev

parents d3fe87a8 199d5d93
Loading
Loading
Loading
Loading
+35 −31
Original line number Diff line number Diff line
@@ -1743,7 +1743,6 @@ public class TelecomServiceImpl {
                enforceCallingPackage(callingPackage, "placeCall");

                PhoneAccountHandle phoneAccountHandle = null;
                boolean clearPhoneAccountHandleExtra = false;
                if (extras != null) {
                    phoneAccountHandle = extras.getParcelable(
                            TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
@@ -1752,33 +1751,42 @@ public class TelecomServiceImpl {
                        extras.remove(TelecomManager.EXTRA_IS_HANDOVER);
                    }
                }
                boolean isSelfManaged = phoneAccountHandle != null &&
                ComponentName phoneAccountComponentName = phoneAccountHandle != null
                        ? phoneAccountHandle.getComponentName() : null;
                String phoneAccountPackageName = phoneAccountComponentName != null
                        ? phoneAccountComponentName.getPackageName() : null;
                boolean isCallerOwnerOfPhoneAccount =
                        callingPackage.equals(phoneAccountPackageName);
                boolean isSelfManagedPhoneAccount =
                        isSelfManagedConnectionService(phoneAccountHandle);
                if (isSelfManaged) {
                    try {
                // Ensure the app's calling package matches the PhoneAccount package name before
                // checking self-managed status so that we do not leak installed package
                // information.
                boolean isSelfManagedRequest = isCallerOwnerOfPhoneAccount &&
                        isSelfManagedPhoneAccount;
                if (isSelfManagedRequest) {
                    // The package name of the caller matches the package name of the
                    // PhoneAccountHandle, so ensure the app has MANAGE_OWN_CALLS permission if
                    // self-managed.
                    mContext.enforceCallingOrSelfPermission(
                            Manifest.permission.MANAGE_OWN_CALLS,
                                "Self-managed ConnectionServices require "
                                        + "MANAGE_OWN_CALLS permission.");
                    } catch (SecurityException e) {
                        // Fallback to use mobile network to avoid disclosing phone account handle
                        // package information
                        clearPhoneAccountHandleExtra = true;
                    }

                    if (!clearPhoneAccountHandleExtra && !callingPackage.equals(
                            phoneAccountHandle.getComponentName().getPackageName())
                            && !canCallPhone(callingPackage, callingFeatureId,
                            "Self-managed ConnectionServices require MANAGE_OWN_CALLS permission.");
                } else if (!canCallPhone(callingPackage, callingFeatureId,
                        "CALL_PHONE permission required to place calls.")) {
                        // The caller is not allowed to place calls, so fallback to use mobile
                        // network.
                        clearPhoneAccountHandleExtra = true;
                    }
                } else if (!canCallPhone(callingPackage, callingFeatureId, "placeCall")) {
                    // not self-managed, so CALL_PHONE is required.
                    mAnomalyReporter.reportAnomaly(PLACE_CALL_SECURITY_EXCEPTION_ERROR_UUID,
                            PLACE_CALL_SECURITY_EXCEPTION_ERROR_MSG);
                    throw new SecurityException("Package " + callingPackage
                            + " is not allowed to place phone calls");
                    throw new SecurityException(
                            "CALL_PHONE permission required to place calls.");
                }

                // An application can not place a call with a self-managed PhoneAccount that
                // they do not own. If this is the case (and the app has CALL_PHONE permission),
                // remove the PhoneAccount from the request and place the call as if it was a
                // managed call request with no PhoneAccount specified.
                if (!isCallerOwnerOfPhoneAccount && isSelfManagedPhoneAccount) {
                    // extras can not be null if isSelfManagedPhoneAccount is true
                    extras.remove(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
                }

                // Note: we can still get here for the default/system dialer, even if the Phone
@@ -1809,15 +1817,11 @@ public class TelecomServiceImpl {
                        final Intent intent = new Intent(hasCallPrivilegedPermission ?
                                Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
                        if (extras != null) {
                            if (clearPhoneAccountHandleExtra) {
                                extras.remove(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
                            }
                            extras.setDefusable(true);
                            intent.putExtras(extras);
                        }
                        mUserCallIntentProcessorFactory.create(mContext, userHandle)
                                .processIntent(
                                        intent, callingPackage, isSelfManaged,
                                .processIntent(intent, callingPackage, isSelfManagedRequest,
                                        (hasCallAppOp && hasCallPermission),
                                        true /* isLocalInvocation */);
                    } finally {
+9 −19
Original line number Diff line number Diff line
@@ -1214,20 +1214,14 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        // ConnectionService.
        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);

        doReturn(PackageManager.PERMISSION_DENIED)
                .when(mContext).checkCallingPermission(CALL_PHONE);
        doReturn(PackageManager.PERMISSION_DENIED)
                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());

        try {
            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
            placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
                    /*shouldNonEmergencyBeAllowed*/ false);
            fail("Expected SecurityException because MANAGE_OWN_CALLS is not set");
        } catch(SecurityException e) {
            fail("Unexpected SecurityException - MANAGE_OWN_CALLS is not set, so call should be "
                    + "placed with no PhoneAccount");
            // expected
        }
    }

@@ -1283,21 +1277,15 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        // pass MANAGE_OWN_CALLS check, but do not have CALL PHONE
        doNothing().when(mContext).enforceCallingOrSelfPermission(
                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
        doReturn(PackageManager.PERMISSION_GRANTED)
                .when(mContext).checkCallingPermission(CALL_PHONE);
        doReturn(PackageManager.PERMISSION_DENIED)
                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
        doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
        when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
                nullable(String.class), nullable(String.class)))
                .thenReturn(AppOpsManager.MODE_ERRORED);
        try {
            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
            placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
                    /*shouldNonEmergencyBeAllowed*/ false);
            fail("Expected a SecurityException - CALL_PHONE app op is denied");
        } catch(SecurityException e) {
            fail("Unexpected SecurityException - MANAGE_OWN_CALLS is set, so call should be "
                    + "placed with no PhoneAccount");
            // expected
        }
    }

@@ -1338,7 +1326,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
            fail("Unexpected SecurityException - CTS is default dialer and MANAGE_OWN_CALLS is not"
                    + " required. Exception: " + e);
        }
        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
                /*shouldNonEmergencyBeAllowed*/ true);
    }

@@ -1408,8 +1396,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
    }

    /**
     * Since this is a self-managed call being requested, even though we do not have CALL_PHONE
     * permissions, we still consider non-emergency allowed because this is a self-managed call.
     * Since this is a self-managed call being requested, so ensure we report the call as
     * self-managed and without non-emergency permissions.
     */
    @SmallTest
    @Test
@@ -2094,6 +2082,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
    }

    private static boolean areBundlesEqual(Bundle b1, Bundle b2) {
        if (b1.keySet().size() != b2.keySet().size()) return false;

        for (String key1 : b1.keySet()) {
            if (!b1.get(key1).equals(b2.get(key1))) {
                return false;