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

Commit 16f94ccc authored by Grant Menke's avatar Grant Menke
Browse files

Allow privileged apps to accept and end VOIP calls.

This change updates Telecom Service to allow privileged app invocations to accept and end VOIP calls. This functionality was requested by the accessibility team in order to support their talkback double tap gesture feature.

Bug: 353579043
Flag: com.android.server.telecom.flags.allow_system_apps_resolve_voip_calls
Test: atest TelecomServiceImplTest
Change-Id: I6da4ce827fc6c7fe732b52aaad67cff75494483d
parent 532b0220
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -73,3 +73,12 @@ flag {
  description: "Formalizes the getLastKnownCellIdentity API that Telecom reliees on as a system api"
  bug: "327454165"
}

# OWNER=grantmenke TARGET=25Q2
flag {
  name: "allow_system_apps_resolve_voip_calls"
  is_exported: true
  namespace: "telecom"
  description: "Allow system apps such as accessibility to accept and end VOIP calls."
  bug: "353579043"
}
+69 −23
Original line number Diff line number Diff line
@@ -160,6 +160,7 @@ public class TelecomServiceImpl {
    private final FeatureFlags mFeatureFlags;
    private final com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;
    private final TelecomMetricsController mMetricsController;
    private final String mSystemUiPackageName;
    private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
    private final Context mContext;
    private final AppOpsManager mAppOpsManager;
@@ -1437,7 +1438,7 @@ public class TelecomServiceImpl {

                // ensure the callingPackage is not spoofed
                // skip check for privileged UIDs and throw SE if package does not match records
                if (!isPrivilegedUid(callingPackage)
                if (!isPrivilegedUid()
                        && !callingUidMatchesPackageManagerRecords(callingPackage)) {
                    EventLog.writeEvent(0x534e4554, "236813210", Binder.getCallingUid(),
                            "getCallStateUsingPackage");
@@ -1470,17 +1471,36 @@ public class TelecomServiceImpl {
            }
        }

        private boolean isPrivilegedUid(String callingPackage) {
        private boolean isPrivilegedUid() {
            int callingUid = Binder.getCallingUid();
            boolean isPrivileged = false;
            switch (callingUid) {
                case Process.ROOT_UID:
                case Process.SYSTEM_UID:
                case Process.SHELL_UID:
                    isPrivileged = true;
                    break;
            return mFeatureFlags.allowSystemAppsResolveVoipCalls()
                    ? (UserHandle.isSameApp(callingUid, Process.ROOT_UID)
                            || UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
                            || UserHandle.isSameApp(callingUid, Process.SHELL_UID))
                    : (callingUid == Process.ROOT_UID
                            || callingUid == Process.SYSTEM_UID
                            || callingUid == Process.SHELL_UID);
        }

        private boolean isSysUiUid() {
            int callingUid = Binder.getCallingUid();
            int systemUiUid;
            if (mPackageManager != null && mSystemUiPackageName != null) {
                try {
                    systemUiUid = mPackageManager.getPackageUid(mSystemUiPackageName, 0);
                    Log.i(TAG, "isSysUiUid: callingUid = " + callingUid + "; systemUiUid = "
                            + systemUiUid);
                    return UserHandle.isSameApp(callingUid, systemUiUid);
                } catch (PackageManager.NameNotFoundException e) {
                    Log.w(TAG, "isSysUiUid: caught PackageManager NameNotFoundException = " + e);
                    return false;
                }
            } else {
                Log.w(TAG, "isSysUiUid: caught null check and returned false; "
                        + "mPackageManager = " + mPackageManager + "; mSystemUiPackageName = "
                        + mSystemUiPackageName);
            }
            return isPrivileged;
            return false;
        }

        /**
@@ -1496,11 +1516,18 @@ public class TelecomServiceImpl {
                    if (!enforceAnswerCallPermission(callingPackage, Binder.getCallingUid())) {
                        throw new SecurityException("requires ANSWER_PHONE_CALLS permission");
                    }

                    // Legacy behavior is to ignore whether the invocation is from a system app:
                    boolean isCallerPrivileged = false;
                    if (mFeatureFlags.allowSystemAppsResolveVoipCalls()) {
                        isCallerPrivileged = isPrivilegedUid() || isSysUiUid();
                        Log.i(TAG, "endCall: Binder.getCallingUid = [" +
                                Binder.getCallingUid() + "] isCallerPrivileged = " +
                                isCallerPrivileged);
                    }
                    long token = Binder.clearCallingIdentity();
                    event.setResult(ApiStats.RESULT_NORMAL);
                    try {
                        return endCallInternal(callingPackage);
                        return endCallInternal(callingPackage, isCallerPrivileged);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
@@ -1522,11 +1549,19 @@ public class TelecomServiceImpl {
                Log.startSession("TSI.aRC", Log.getPackageAbbreviation(packageName));
                synchronized (mLock) {
                    if (!enforceAnswerCallPermission(packageName, Binder.getCallingUid())) return;

                    // Legacy behavior is to ignore whether the invocation is from a system app:
                    boolean isCallerPrivileged = false;
                    if (mFeatureFlags.allowSystemAppsResolveVoipCalls()) {
                        isCallerPrivileged = isPrivilegedUid() || isSysUiUid();
                        Log.i(TAG, "acceptRingingCall: Binder.getCallingUid = [" +
                                Binder.getCallingUid() + "] isCallerPrivileged = " +
                                isCallerPrivileged);
                    }
                    long token = Binder.clearCallingIdentity();
                    event.setResult(ApiStats.RESULT_NORMAL);
                    try {
                        acceptRingingCallInternal(DEFAULT_VIDEO_STATE, packageName);
                        acceptRingingCallInternal(DEFAULT_VIDEO_STATE, packageName,
                                isCallerPrivileged);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
@@ -1549,11 +1584,18 @@ public class TelecomServiceImpl {
                Log.startSession("TSI.aRCWVS", Log.getPackageAbbreviation(packageName));
                synchronized (mLock) {
                    if (!enforceAnswerCallPermission(packageName, Binder.getCallingUid())) return;

                    // Legacy behavior is to ignore whether the invocation is from a system app:
                    boolean isCallerPrivileged = false;
                    if (mFeatureFlags.allowSystemAppsResolveVoipCalls()) {
                        isCallerPrivileged = isPrivilegedUid() || isSysUiUid();
                        Log.i(TAG, "acceptRingingCallWithVideoState: Binder.getCallingUid = "
                                + "[" + Binder.getCallingUid() + "] isCallerPrivileged = " +
                                isCallerPrivileged);
                    }
                    long token = Binder.clearCallingIdentity();
                    event.setResult(ApiStats.RESULT_NORMAL);
                    try {
                        acceptRingingCallInternal(videoState, packageName);
                        acceptRingingCallInternal(videoState, packageName, isCallerPrivileged);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
@@ -2918,7 +2960,8 @@ public class TelecomServiceImpl {
            SettingsSecureAdapter settingsSecureAdapter,
            FeatureFlags featureFlags,
            com.android.internal.telephony.flags.FeatureFlags telephonyFeatureFlags,
            TelecomSystem.SyncRoot lock, TelecomMetricsController metricsController) {
            TelecomSystem.SyncRoot lock, TelecomMetricsController metricsController,
            String sysUiPackageName) {
        mContext = context;
        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);

@@ -2940,6 +2983,7 @@ public class TelecomServiceImpl {
        mSubscriptionManagerAdapter = subscriptionManagerAdapter;
        mSettingsSecureAdapter = settingsSecureAdapter;
        mMetricsController = metricsController;
        mSystemUiPackageName = sysUiPackageName;

        mDefaultDialerCache.observeDefaultDialerApplication(mContext.getMainExecutor(), userId -> {
            String defaultDialer = mDefaultDialerCache.getDefaultDialerApplication(userId);
@@ -3054,13 +3098,14 @@ public class TelecomServiceImpl {
        return false;
    }

    private void acceptRingingCallInternal(int videoState, String packageName) {
    private void acceptRingingCallInternal(int videoState, String packageName,
            boolean isCallerPrivileged) {
        Call call = mCallsManager.getFirstCallWithState(CallState.RINGING,
                CallState.SIMULATED_RINGING);
        if (call != null) {
            if (call.isSelfManaged()) {
            if (call.isSelfManaged() && !isCallerPrivileged) {
                Log.addEvent(call, LogUtils.Events.REQUEST_ACCEPT,
                        "self-mgd accept ignored from " + packageName);
                        "self-mgd accept ignored from non-privileged app " + packageName);
                return;
            }

@@ -3075,7 +3120,7 @@ public class TelecomServiceImpl {
    // Supporting methods for the ITelecomService interface implementation.
    //

    private boolean endCallInternal(String callingPackage) {
    private boolean endCallInternal(String callingPackage, boolean isCallerPrivileged) {
        // Always operate on the foreground call if one exists, otherwise get the first call in
        // priority order by call-state.
        Call call = mCallsManager.getForegroundCall();
@@ -3095,9 +3140,10 @@ public class TelecomServiceImpl {
                return false;
            }

            if (call.isSelfManaged()) {
            if (call.isSelfManaged() && !isCallerPrivileged) {
                Log.addEvent(call, LogUtils.Events.REQUEST_DISCONNECT,
                        "self-mgd disconnect ignored from " + callingPackage);
                        "self-mgd disconnect ignored from non-privileged app " +
                                callingPackage);
                return false;
            }

+3 −1
Original line number Diff line number Diff line
@@ -224,6 +224,7 @@ public class TelecomSystem {
            RoleManagerAdapter roleManagerAdapter,
            ContactsAsyncHelper.Factory contactsAsyncHelperFactory,
            DeviceIdleControllerAdapter deviceIdleControllerAdapter,
            String sysUiPackageName,
            Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter,
            Executor asyncTaskExecutor,
            Executor asyncCallAudioTaskExecutor,
@@ -503,7 +504,8 @@ public class TelecomSystem {
                    featureFlags,
                    null,
                    mLock,
                    metricsController);
                    metricsController,
                    sysUiPackageName);
        } finally {
            Log.endSession();
        }
+5 −3
Original line number Diff line number Diff line
@@ -81,10 +81,11 @@ public class TelecomService extends Service implements TelecomSystem.Component {
        Log.d(this, "onBind");
        return new ITelecomLoader.Stub() {
            @Override
            public ITelecomService createTelecomService(IInternalServiceRetriever retriever) {
            public ITelecomService createTelecomService(IInternalServiceRetriever retriever,
                    String sysUiPackageName) {
                InternalServiceRetrieverAdapter adapter =
                        new InternalServiceRetrieverAdapter(retriever);
                initializeTelecomSystem(TelecomService.this, adapter);
                initializeTelecomSystem(TelecomService.this, adapter, sysUiPackageName);
                synchronized (getTelecomSystem().getLock()) {
                    return getTelecomSystem().getTelecomServiceImpl().getBinder();
                }
@@ -103,7 +104,7 @@ public class TelecomService extends Service implements TelecomSystem.Component {
     * @param context
     */
    static void initializeTelecomSystem(Context context,
            InternalServiceRetrieverAdapter internalServiceRetriever) {
            InternalServiceRetrieverAdapter internalServiceRetriever, String sysUiPackageName) {
        if (TelecomSystem.getInstance() == null) {
            FeatureFlags featureFlags = new FeatureFlagsImpl();
            NotificationChannelManager notificationChannelManager =
@@ -204,6 +205,7 @@ public class TelecomService extends Service implements TelecomSystem.Component {
                                    (RoleManager) context.getSystemService(Context.ROLE_SERVICE)),
                            new ContactsAsyncHelper.Factory(),
                            internalServiceRetriever.getDeviceIdleController(),
                            sysUiPackageName,
                            new Ringer.AccessibilityManagerAdapter() {
                                @Override
                                public boolean startFlashNotificationSequence(
+54 −3
Original line number Diff line number Diff line
@@ -209,6 +209,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {

    private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };

    private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
    private static final String DEFAULT_DIALER_PACKAGE = "com.google.android.dialer";
    private static final UserHandle USER_HANDLE_16 = new UserHandle(16);
    private static final UserHandle USER_HANDLE_17 = new UserHandle(17);
@@ -260,7 +261,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
                mFeatureFlags,
                mTelephonyFeatureFlags,
                mLock,
                mMockTelecomMetricsController);
                mMockTelecomMetricsController,
                SYSTEM_UI_PACKAGE);
        telecomServiceImpl.setTransactionManager(mTransactionManager);
        telecomServiceImpl.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
        mTSIBinder = telecomServiceImpl.getBinder();
@@ -2307,7 +2309,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
    }

    /**
     * Ensure self-managed calls cannot be ended using {@link TelecomManager#endCall()}.
     * Ensure self-managed calls cannot be ended using {@link TelecomManager#endCall()} when the
     * caller of this method is not considered privileged.
     * @throws Exception
     */
    @SmallTest
@@ -2324,7 +2327,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {

    /**
     * Ensure self-managed calls cannot be answered using {@link TelecomManager#acceptRingingCall()}
     * or {@link TelecomManager#acceptRingingCall(int)}.
     * or {@link TelecomManager#acceptRingingCall(int)} when the caller of these methods is not
     * considered privileged.
     * @throws Exception
     */
    @SmallTest
@@ -2339,6 +2343,53 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        verify(mFakeCallsManager, never()).answerCall(eq(call), anyInt());
    }

    /**
     * Ensure self-managed calls can be answered using {@link TelecomManager#acceptRingingCall()}
     * or {@link TelecomManager#acceptRingingCall(int)} if the caller of these methods is
     * privileged.
     * @throws Exception
     */
    @SmallTest
    @Test
    public void testCanAnswerSelfManagedCallIfPrivileged() throws Exception {
        when(mFeatureFlags.allowSystemAppsResolveVoipCalls()).thenReturn(true);
        // Configure the test so that the caller of acceptRingingCall is considered privileged:
        when(mPackageManager.getPackageUid(SYSTEM_UI_PACKAGE, 0))
                .thenReturn(Binder.getCallingUid());

        // Ensure that the call is successfully accepted:
        Call call = mock(Call.class);
        when(call.isSelfManaged()).thenReturn(true);
        when(call.getState()).thenReturn(CallState.ACTIVE);
        when(mFakeCallsManager.getFirstCallWithState(any()))
                .thenReturn(call);
        mTSIBinder.acceptRingingCall(TEST_PACKAGE);
        verify(mFakeCallsManager).answerCall(eq(call), anyInt());
    }

    /**
     * Ensure self-managed calls can be ended using {@link TelecomManager#endCall()} when the
     * caller of these methods is privileged.
     * @throws Exception
     */
    @SmallTest
    @Test
    public void testCanEndSelfManagedCallIfPrivileged() throws Exception {
        when(mFeatureFlags.allowSystemAppsResolveVoipCalls()).thenReturn(true);
        // Configure the test so that the caller of endCall is considered privileged:
        when(mPackageManager.getPackageUid(SYSTEM_UI_PACKAGE, 0))
                .thenReturn(Binder.getCallingUid());
        // Set up the call:
        Call call = mock(Call.class);
        when(call.isSelfManaged()).thenReturn(true);
        when(call.getState()).thenReturn(CallState.ACTIVE);
        when(mFakeCallsManager.getFirstCallWithState(any()))
                .thenReturn(call);
        // Ensure that the call is successfully ended:
        assertTrue(mTSIBinder.endCall(TEST_PACKAGE));
        verify(mFakeCallsManager).disconnectCall(eq(call));
    }

    @SmallTest
    @Test
    public void testGetAdnUriForPhoneAccount() throws Exception {
Loading