Loading src/com/android/server/telecom/CallAudioManager.java +6 −5 Original line number Diff line number Diff line Loading @@ -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; } Loading src/com/android/server/telecom/CallsManager.java +76 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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(), Loading Loading @@ -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); Loading Loading @@ -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; } Loading Loading @@ -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}. Loading src/com/android/server/telecom/TelecomServiceImpl.java +15 −11 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -1071,7 +1067,8 @@ public class TelecomServiceImpl { } synchronized (mLock) { return mCallsManager.hasOngoingCalls(); return mCallsManager.hasOngoingCalls(Binder.getCallingUserHandle(), hasInAppCrossUserPermission()); } } finally { Log.endSession(); Loading Loading @@ -1112,7 +1109,8 @@ public class TelecomServiceImpl { } synchronized (mLock) { return mCallsManager.hasOngoingManagedCalls(); return mCallsManager.hasOngoingManagedCalls(Binder.getCallingUserHandle(), hasInAppCrossUserPermission()); } } finally { Log.endSession(); Loading Loading @@ -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) { Loading tests/src/com/android/server/telecom/tests/CallsManagerTest.java +32 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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) { Loading tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +14 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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)); } Loading Loading @@ -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)); } Loading @@ -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)); } Loading @@ -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()); } /** Loading Loading
src/com/android/server/telecom/CallAudioManager.java +6 −5 Original line number Diff line number Diff line Loading @@ -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; } Loading
src/com/android/server/telecom/CallsManager.java +76 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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(), Loading Loading @@ -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); Loading Loading @@ -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; } Loading Loading @@ -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}. Loading
src/com/android/server/telecom/TelecomServiceImpl.java +15 −11 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -1071,7 +1067,8 @@ public class TelecomServiceImpl { } synchronized (mLock) { return mCallsManager.hasOngoingCalls(); return mCallsManager.hasOngoingCalls(Binder.getCallingUserHandle(), hasInAppCrossUserPermission()); } } finally { Log.endSession(); Loading Loading @@ -1112,7 +1109,8 @@ public class TelecomServiceImpl { } synchronized (mLock) { return mCallsManager.hasOngoingManagedCalls(); return mCallsManager.hasOngoingManagedCalls(Binder.getCallingUserHandle(), hasInAppCrossUserPermission()); } } finally { Log.endSession(); Loading Loading @@ -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) { Loading
tests/src/com/android/server/telecom/tests/CallsManagerTest.java +32 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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) { Loading
tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +14 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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)); } Loading Loading @@ -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)); } Loading @@ -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)); } Loading @@ -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()); } /** Loading