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

Commit fc2858f9 authored by Tyler Gunn's avatar Tyler Gunn Committed by Android (Google) Code Review
Browse files

Merge "Update call state type APIs to be multi-user aware."

parents b58abbb5 2b02a601
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -486,19 +486,20 @@ public class CallAudioManager extends CallsManagerListenerBase {
                CallAudioRouteStateMachine.INCLUDE_BLUETOOTH_IN_BASELINE);
    }

    Set<UserHandle> silenceRingers(Context context, UserHandle callingUser) {
    Set<UserHandle> silenceRingers(Context context, UserHandle callingUser,
            boolean hasCrossUserPermission) {
        // Store all users from calls that were silenced so that we can silence the
        // InCallServices which are associated with those users.
        Set<UserHandle> userHandles = new HashSet<>();
        boolean allCallSilenced = true;
        synchronized (mCallsManager.getLock()) {
            for (Call call : mRingingCalls) {
                PhoneAccount targetPhoneAccount = call.getPhoneAccountFromHandle();
                UserHandle userFromCall = call.getUserHandleFromTargetPhoneAccount();
                // Do not try to silence calls when calling user is different from the phone account
                // user and the account does not have CAPABILITY_MULTI_USER enabled.
                if (!callingUser.equals(userFromCall) && !targetPhoneAccount.
                        hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
                // user, the account does not have CAPABILITY_MULTI_USER enabled, or if the user
                // does not have the INTERACT_ACROSS_USERS permission enabled.
                if (!hasCrossUserPermission && !mCallsManager
                        .isCallVisibleForUser(call, callingUser)) {
                    allCallSilenced = false;
                    continue;
                }
+76 −15
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ import android.media.MediaPlayer;
import android.media.ToneGenerator;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -3678,10 +3679,6 @@ public class CallsManager extends Call.ListenerBase
        return false;
    }

    boolean hasActiveOrHoldingCall() {
        return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
    }

    boolean hasRingingCall() {
        return getFirstCallWithState(CallState.RINGING, CallState.ANSWERED) != null;
    }
@@ -3803,15 +3800,6 @@ public class CallsManager extends Call.ListenerBase
        return getFirstCallWithState(CallState.ACTIVE);
    }

    Call getDialingCall() {
        return getFirstCallWithState(CallState.DIALING);
    }

    @VisibleForTesting
    public Call getHeldCall() {
        return getFirstCallWithState(CallState.ON_HOLD);
    }

    public Call getHeldCallByConnectionService(PhoneAccountHandle targetPhoneAccount) {
        Optional<Call> heldCall = mCalls.stream()
                .filter(call -> PhoneAccountHandle.areFromSamePackage(call.getTargetPhoneAccount(),
@@ -4424,6 +4412,60 @@ public class CallsManager extends Call.ListenerBase
        return (int) callsStream.count();
    }

    /**
     * Determines the number of calls (visible to the calling user) matching the specified criteria.
     * This is an overloaded method which is being used in a security patch to fix up the call
     * state type APIs which are acting across users when they should not be.
     *
     * See {@link TelecomManager#isInCall()} and {@link TelecomManager#isInManagedCall()}.
     *
     * @param callFilter indicates whether to include just managed calls
     *                   ({@link #CALL_FILTER_MANAGED}), self-managed calls
     *                   ({@link #CALL_FILTER_SELF_MANAGED}), or all calls
     *                   ({@link #CALL_FILTER_ALL}).
     * @param excludeCall Where {@code non-null}, this call is excluded from the count.
     * @param callingUser Where {@code non-null}, call visibility is scoped to this
     *                    {@link UserHandle}.
     * @param hasCrossUserAccess indicates if calling user has the INTERACT_ACROSS_USERS permission.
     * @param phoneAccountHandle Where {@code non-null}, calls for this {@link PhoneAccountHandle}
     *                           are excluded from the count.
     * @param states The list of {@link CallState}s to include in the count.
     * @return Count of calls matching criteria.
     */
    @VisibleForTesting
    public int getNumCallsWithState(final int callFilter, Call excludeCall,
            UserHandle callingUser, boolean hasCrossUserAccess,
            PhoneAccountHandle phoneAccountHandle, int... states) {

        Set<Integer> desiredStates = IntStream.of(states).boxed().collect(Collectors.toSet());

        Stream<Call> callsStream = mCalls.stream()
                .filter(call -> desiredStates.contains(call.getState()) &&
                        call.getParentCall() == null && !call.isExternalCall());

        if (callFilter == CALL_FILTER_MANAGED) {
            callsStream = callsStream.filter(call -> !call.isSelfManaged());
        } else if (callFilter == CALL_FILTER_SELF_MANAGED) {
            callsStream = callsStream.filter(call -> call.isSelfManaged());
        }

        // If a call to exclude was specified, filter it out.
        if (excludeCall != null) {
            callsStream = callsStream.filter(call -> call != excludeCall);
        }

        // If a phone account handle was specified, only consider calls for that phone account.
        if (phoneAccountHandle != null) {
            callsStream = callsStream.filter(
                    call -> phoneAccountHandle.equals(call.getTargetPhoneAccount()));
        }

        callsStream = callsStream.filter(
                call -> hasCrossUserAccess || isCallVisibleForUser(call, callingUser));

        return (int) callsStream.count();
    }

    private boolean hasMaximumLiveCalls(Call exceptCall) {
        return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL,
                exceptCall, null /* phoneAccountHandle*/, LIVE_CALL_STATES);
@@ -4518,23 +4560,29 @@ public class CallsManager extends Call.ListenerBase
    /**
     * Determines if there are any ongoing managed or self-managed calls.
     * Note: The {@link #ONGOING_CALL_STATES} are
     * @param callingUser The user to scope the calls to.
     * @param hasCrossUserAccess indicates if user has the INTERACT_ACROSS_USERS permission.
     * @return {@code true} if there are ongoing managed or self-managed calls, {@code false}
     *      otherwise.
     */
    public boolean hasOngoingCalls() {
    public boolean hasOngoingCalls(UserHandle callingUser, boolean hasCrossUserAccess) {
        return getNumCallsWithState(
                CALL_FILTER_ALL, null /* excludeCall */,
                callingUser, hasCrossUserAccess,
                null /* phoneAccountHandle */,
                ONGOING_CALL_STATES) > 0;
    }

    /**
     * Determines if there are any ongoing managed calls.
     * @param callingUser The user to scope the calls to.
     * @param hasCrossUserAccess indicates if user has the INTERACT_ACROSS_USERS permission.
     * @return {@code true} if there are ongoing managed calls, {@code false} otherwise.
     */
    public boolean hasOngoingManagedCalls() {
    public boolean hasOngoingManagedCalls(UserHandle callingUser, boolean hasCrossUserAccess) {
        return getNumCallsWithState(
                CALL_FILTER_MANAGED, null /* excludeCall */,
                callingUser, hasCrossUserAccess,
                null /* phoneAccountHandle */,
                ONGOING_CALL_STATES) > 0;
    }
@@ -6014,6 +6062,19 @@ public class CallsManager extends Call.ListenerBase
                .forEach(c -> c.setVideoCallingSupportedByPhoneAccount(isVideoNowSupported));
    }

    /**
     * Determines if a {@link Call} is visible to the calling user. If the {@link PhoneAccount} has
     * CAPABILITY_MULTI_USER, or the user handle associated with the {@link PhoneAccount} is the
     * same as the calling user, the call is visible to the user.
     * @param call
     * @return {@code true} if call is visible to the calling user
     */
    boolean isCallVisibleForUser(Call call, UserHandle userHandle) {
        return call.getUserHandleFromTargetPhoneAccount().equals(userHandle)
                || call.getPhoneAccountFromHandle()
                .hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER);
    }

    /**
     * Determines if two {@link Call} instances originated from either the same target
     * {@link PhoneAccountHandle} or connection manager {@link PhoneAccountHandle}.
+15 −11
Original line number Diff line number Diff line
@@ -362,15 +362,12 @@ public class TelecomServiceImpl {
                }
                synchronized (mLock) {
                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                    boolean hasCrossUserAccess = mContext.checkCallingOrSelfPermission(
                            Manifest.permission.INTERACT_ACROSS_USERS)
                            == PackageManager.PERMISSION_GRANTED;
                    long token = Binder.clearCallingIdentity();
                    try {
                        return new ParceledListSlice<>(
                                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null,
                                        includeDisabledAccounts, callingUserHandle,
                                        hasCrossUserAccess));
                                        hasInAppCrossUserPermission()));
                    } catch (Exception e) {
                        Log.e(this, e, "getCallCapablePhoneAccounts");
                        mAnomalyReporter.reportAnomaly(GET_CALL_CAPABLE_ACCOUNTS_ERROR_UUID,
@@ -640,13 +637,11 @@ public class TelecomServiceImpl {

                synchronized (mLock) {
                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                    boolean hasCrossUserAccess = mContext.checkCallingOrSelfPermission(
                            Manifest.permission.INTERACT_ACROSS_USERS)
                            == PackageManager.PERMISSION_GRANTED;
                    long token = Binder.clearCallingIdentity();
                    try {
                        return new ParceledListSlice<>(mPhoneAccountRegistrar
                                .getAllPhoneAccountHandles(callingUserHandle, hasCrossUserAccess));
                                .getAllPhoneAccountHandles(callingUserHandle,
                                        hasInAppCrossUserPermission()));
                    } catch (Exception e) {
                        Log.e(this, e, "getAllPhoneAccounts");
                        throw e;
@@ -954,7 +949,8 @@ public class TelecomServiceImpl {
                    try {
                        Log.i(this, "Silence Ringer requested by %s", callingPackage);
                        Set<UserHandle> userHandles = mCallsManager.getCallAudioManager().
                                silenceRingers(mContext, callingUserHandle);
                                silenceRingers(mContext, callingUserHandle,
                                        hasInAppCrossUserPermission());
                        mCallsManager.getInCallController().silenceRinger(userHandles);
                    } finally {
                        Binder.restoreCallingIdentity(token);
@@ -1071,7 +1067,8 @@ public class TelecomServiceImpl {
                }

                synchronized (mLock) {
                    return mCallsManager.hasOngoingCalls();
                    return mCallsManager.hasOngoingCalls(Binder.getCallingUserHandle(),
                            hasInAppCrossUserPermission());
                }
            } finally {
                Log.endSession();
@@ -1112,7 +1109,8 @@ public class TelecomServiceImpl {
                }

                synchronized (mLock) {
                    return mCallsManager.hasOngoingManagedCalls();
                    return mCallsManager.hasOngoingManagedCalls(Binder.getCallingUserHandle(),
                            hasInAppCrossUserPermission());
                }
            } finally {
                Log.endSession();
@@ -2802,6 +2800,12 @@ public class TelecomServiceImpl {
                        + " INTERACT_ACROSS_USERS permission");
    }

    private boolean hasInAppCrossUserPermission() {
        return mContext.checkCallingOrSelfPermission(
                Manifest.permission.INTERACT_ACROSS_USERS)
                == PackageManager.PERMISSION_GRANTED;
    }

    // to be used for TestApi methods that can only be called with SHELL UID.
    private void enforceShellOnly(int callingUid, String message) {
        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+32 −0
Original line number Diff line number Diff line
@@ -42,10 +42,12 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.Manifest;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -162,6 +164,13 @@ public class CallsManagerTest extends TelecomTestCase {
                    | PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
            .setIsEnabled(true)
            .build();
    private static final PhoneAccount SIM_1_ACCOUNT_SECONDARY = new PhoneAccount
            .Builder(SIM_1_HANDLE_SECONDARY, "Sim1")
            .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
                    | PhoneAccount.CAPABILITY_CALL_PROVIDER
                    | PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
            .setIsEnabled(true)
            .build();
    private static final PhoneAccount SIM_2_ACCOUNT = new PhoneAccount.Builder(SIM_2_HANDLE, "Sim2")
            .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
                    | PhoneAccount.CAPABILITY_CALL_PROVIDER
@@ -2228,6 +2237,29 @@ public class CallsManagerTest extends TelecomTestCase {
        waitForCountDownLatch(latch);
    }

    @SmallTest
    @Test
    public void testGetNumCallsWithState_MultiUser() throws Exception {
        when(mContext.checkCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS))
                .thenReturn(PackageManager.PERMISSION_GRANTED);
        // Add call under secondary user
        Call call = addSpyCall(SIM_1_HANDLE_SECONDARY, CallState.ACTIVE);
        when(call.getPhoneAccountFromHandle()).thenReturn(SIM_1_ACCOUNT_SECONDARY);
        // Verify that call is visible to primary user
        assertEquals(mCallsManager.getNumCallsWithState(0, null,
                UserHandle.CURRENT_OR_SELF, true,
                null, CallState.ACTIVE), 1);
        // Verify that call is not visible to primary user
        // when a different phone account handle is specified.
        assertEquals(mCallsManager.getNumCallsWithState(0, null,
                UserHandle.CURRENT_OR_SELF, true,
                SIM_1_HANDLE, CallState.ACTIVE), 0);
        // Deny INTERACT_ACROSS_USERS permission and verify that call is not visible to primary user
        assertEquals(mCallsManager.getNumCallsWithState(0, null,
                UserHandle.CURRENT_OR_SELF, false,
                null, CallState.ACTIVE), 0);
    }

    public void waitForCountDownLatch(CountDownLatch latch) throws InterruptedException {
            boolean success = latch.await(5000, TimeUnit.MILLISECONDS);
            if (!success) {
+14 −8
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.Manifest.permission.READ_PHONE_NUMBERS;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;

import android.Manifest;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.ComponentName;
@@ -219,6 +220,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        doReturn(mContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
        doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class),
                anyString());
        when(mContext.checkCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS))
                .thenReturn(PackageManager.PERMISSION_GRANTED);
        doAnswer(invocation -> {
            mDefaultDialerObserver = invocation.getArgument(1);
            return null;
@@ -1463,8 +1466,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
    public void testEndCallWithNoForegroundCall() throws Exception {
        Call call = mock(Call.class);
        when(call.getState()).thenReturn(CallState.ACTIVE);
        when(mFakeCallsManager.getFirstCallWithState(any()))
                .thenReturn(call);
        when(mFakeCallsManager.getFirstCallWithState(any())).thenReturn(call);
        assertTrue(mTSIBinder.endCall(TEST_PACKAGE));
        verify(mFakeCallsManager).disconnectCall(eq(call));
    }
@@ -1505,14 +1507,16 @@ public class TelecomServiceImplTest extends TelecomTestCase {
    @SmallTest
    @Test
    public void testIsInCall() throws Exception {
        when(mFakeCallsManager.hasOngoingCalls()).thenReturn(true);
        when(mFakeCallsManager.hasOngoingCalls(any(UserHandle.class), anyBoolean()))
                .thenReturn(true);
        assertTrue(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE, null));
    }

    @SmallTest
    @Test
    public void testNotIsInCall() throws Exception {
        when(mFakeCallsManager.hasOngoingCalls()).thenReturn(false);
        when(mFakeCallsManager.hasOngoingCalls(any(UserHandle.class), anyBoolean()))
                .thenReturn(false);
        assertFalse(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE, null));
    }

@@ -1527,20 +1531,22 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        } catch (SecurityException e) {
            // desired result
        }
        verify(mFakeCallsManager, never()).hasOngoingCalls();
        verify(mFakeCallsManager, never()).hasOngoingCalls(any(UserHandle.class), anyBoolean());
    }

    @SmallTest
    @Test
    public void testIsInManagedCall() throws Exception {
        when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(true);
        when(mFakeCallsManager.hasOngoingManagedCalls(any(UserHandle.class), anyBoolean()))
                .thenReturn(true);
        assertTrue(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE, null));
    }

    @SmallTest
    @Test
    public void testNotIsInManagedCall() throws Exception {
        when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(false);
        when(mFakeCallsManager.hasOngoingManagedCalls(any(UserHandle.class), anyBoolean()))
                .thenReturn(false);
        assertFalse(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE, null));
    }

@@ -1555,7 +1561,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        } catch (SecurityException e) {
            // desired result
        }
        verify(mFakeCallsManager, never()).hasOngoingCalls();
        verify(mFakeCallsManager, never()).hasOngoingCalls(any(UserHandle.class), anyBoolean());
    }

    /**