Loading src/com/android/server/telecom/TelecomServiceImpl.java +35 −31 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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 Loading Loading @@ -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 { Loading tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +9 −19 Original line number Diff line number Diff line Loading @@ -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 } } Loading Loading @@ -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 } } Loading Loading @@ -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); } Loading Loading @@ -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 Loading Loading @@ -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; Loading Loading
src/com/android/server/telecom/TelecomServiceImpl.java +35 −31 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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 Loading Loading @@ -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 { Loading
tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +9 −19 Original line number Diff line number Diff line Loading @@ -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 } } Loading Loading @@ -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 } } Loading Loading @@ -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); } Loading Loading @@ -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 Loading Loading @@ -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; Loading