Loading src/com/android/server/telecom/TelecomServiceImpl.java +20 −28 Original line number Diff line number Diff line Loading @@ -1506,7 +1506,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 @@ -1515,33 +1514,29 @@ public class TelecomServiceImpl { extras.remove(TelecomManager.EXTRA_IS_HANDOVER); } } boolean isSelfManaged = phoneAccountHandle != null && isSelfManagedConnectionService(phoneAccountHandle); if (isSelfManaged) { try { 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; } ComponentName componentName = phoneAccountHandle != null ? phoneAccountHandle.getComponentName() : null; String packageName = componentName != null ? componentName.getPackageName() : null; if (!clearPhoneAccountHandleExtra && !callingPackage.equals( phoneAccountHandle.getComponentName().getPackageName()) && !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; // Two cases here: the app calling this API is trying to place a call on another // ConnectionService or the app calling this API implements a self-managed // ConnectionService and is trying to place a call on their own ConnectionService. // Case 1: If the app does not implement the ConnectionService they are requesting // the call be placed on, ensure they have the correct CALL_PHONE permissions. if (!callingPackage.equals(packageName) && !canCallPhone(callingPackage, callingFeatureId, "CALL_PHONE permission required to place calls.")) { throw new SecurityException("CALL_PHONE permission required to place calls."); } } else if (!canCallPhone(callingPackage, callingFeatureId, "placeCall")) { throw new SecurityException("Package " + callingPackage + " is not allowed to place phone calls"); // Case 2: The package name of the caller matches the package name of the // PhoneAccountHandle, so ensure the app has MANAGE_OWN_CALLS permission. if (callingPackage.equals(packageName)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_OWN_CALLS, "Self-managed ConnectionServices require MANAGE_OWN_CALLS permission."); } boolean isSelfManaged = isSelfManagedConnectionService(phoneAccountHandle); // Note: we can still get here for the default/system dialer, even if the Phone // permission is turned off. This is because the default/system dialer is always // allowed to attempt to place a call (regardless of permission state), in case Loading Loading @@ -1570,9 +1565,6 @@ 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); } Loading tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +136 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,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 @@ -756,12 +757,139 @@ public class TelecomServiceImplTest extends TelecomTestCase { } } @SmallTest @Test public void testPlaceCallNoPermission_SelfManaged() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn( makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed // ConnectionService. extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( eq(Manifest.permission.MANAGE_OWN_CALLS), anyString()); try { mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null); fail("Expected SecurityException because MANAGE_OWN_CALLS is not set"); } catch(SecurityException e) { // expected } } @SmallTest @Test public void testPlaceCallNoCallPhonePermission_Managed() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn( makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage doesn't match the PhoneAccountHandle, so this app is not managing the //ConnectionService extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); // Since the packages do not match, the caller needs CALL_PHONE permission doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( eq(CALL_PHONE), anyString()); try { mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null); fail("Expected SecurityException because CALL_PHONE is not set"); } catch(SecurityException e) { // expected } } @SmallTest @Test public void testPlaceCallNoCallPhoneAppOp_Managed() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn( makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage doesn't match the PhoneAccountHandle, so this app is not managing the //ConnectionService extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); // Since the packages do not match, the caller needs CALL_PHONE app op when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_IGNORED); try { mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null); fail("Expected SecurityException because CALL_PHONE is not set"); } catch(SecurityException e) { // expected } } @SmallTest @Test public void testPlaceCallWithNonEmergencyPermission_SelfManaged() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn( makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed // ConnectionService. extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); // enforceCallingOrSelfPermission is implicitly granted for MANAGE_OWN_CALLS here and // CALL_PHONE is not required. when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_IGNORED); doReturn(PackageManager.PERMISSION_DENIED) .when(mContext).checkCallingPermission(CALL_PHONE); doReturn(PackageManager.PERMISSION_DENIED) .when(mContext).checkCallingPermission(CALL_PRIVILEGED); mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null); placeCallTestHelper(handle, extras, true); } @SmallTest @Test public void testPlaceCallWithNonEmergencyPermission_Managed() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage doesn't match the PhoneAccountHandle, so this app does not have a // self-managed ConnectionService extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_ALLOWED); doReturn(PackageManager.PERMISSION_GRANTED) .when(mContext).checkCallingPermission(CALL_PHONE); doReturn(PackageManager.PERMISSION_DENIED) .when(mContext).checkCallingPermission(CALL_PRIVILEGED); mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null); placeCallTestHelper(handle, extras, true); } @SmallTest @Test public void testPlaceCallWithNonEmergencyPermission() throws Exception { Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // We are assumed to be default dialer in this test, so canCallPhone is always true. when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_ALLOWED); Loading @@ -780,6 +908,7 @@ public class TelecomServiceImplTest extends TelecomTestCase { Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // We are assumed to be default dialer in this test, so canCallPhone is always true. when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_IGNORED); Loading @@ -798,6 +927,7 @@ public class TelecomServiceImplTest extends TelecomTestCase { Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // We are assumed to be default dialer in this test, so canCallPhone is always true. when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_ALLOWED); Loading Loading @@ -1284,6 +1414,12 @@ public class TelecomServiceImplTest extends TelecomTestCase { return paBuilder; } private PhoneAccount.Builder makeSelfManagedPhoneAccount(PhoneAccountHandle paHandle) { PhoneAccount.Builder paBuilder = makePhoneAccount(paHandle); paBuilder.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED); return paBuilder; } private PhoneAccount.Builder makePhoneAccount(PhoneAccountHandle paHandle) { return new PhoneAccount.Builder(paHandle, "testLabel"); } Loading Loading
src/com/android/server/telecom/TelecomServiceImpl.java +20 −28 Original line number Diff line number Diff line Loading @@ -1506,7 +1506,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 @@ -1515,33 +1514,29 @@ public class TelecomServiceImpl { extras.remove(TelecomManager.EXTRA_IS_HANDOVER); } } boolean isSelfManaged = phoneAccountHandle != null && isSelfManagedConnectionService(phoneAccountHandle); if (isSelfManaged) { try { 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; } ComponentName componentName = phoneAccountHandle != null ? phoneAccountHandle.getComponentName() : null; String packageName = componentName != null ? componentName.getPackageName() : null; if (!clearPhoneAccountHandleExtra && !callingPackage.equals( phoneAccountHandle.getComponentName().getPackageName()) && !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; // Two cases here: the app calling this API is trying to place a call on another // ConnectionService or the app calling this API implements a self-managed // ConnectionService and is trying to place a call on their own ConnectionService. // Case 1: If the app does not implement the ConnectionService they are requesting // the call be placed on, ensure they have the correct CALL_PHONE permissions. if (!callingPackage.equals(packageName) && !canCallPhone(callingPackage, callingFeatureId, "CALL_PHONE permission required to place calls.")) { throw new SecurityException("CALL_PHONE permission required to place calls."); } } else if (!canCallPhone(callingPackage, callingFeatureId, "placeCall")) { throw new SecurityException("Package " + callingPackage + " is not allowed to place phone calls"); // Case 2: The package name of the caller matches the package name of the // PhoneAccountHandle, so ensure the app has MANAGE_OWN_CALLS permission. if (callingPackage.equals(packageName)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_OWN_CALLS, "Self-managed ConnectionServices require MANAGE_OWN_CALLS permission."); } boolean isSelfManaged = isSelfManagedConnectionService(phoneAccountHandle); // Note: we can still get here for the default/system dialer, even if the Phone // permission is turned off. This is because the default/system dialer is always // allowed to attempt to place a call (regardless of permission state), in case Loading Loading @@ -1570,9 +1565,6 @@ 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); } Loading
tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +136 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,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 @@ -756,12 +757,139 @@ public class TelecomServiceImplTest extends TelecomTestCase { } } @SmallTest @Test public void testPlaceCallNoPermission_SelfManaged() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn( makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed // ConnectionService. extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( eq(Manifest.permission.MANAGE_OWN_CALLS), anyString()); try { mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null); fail("Expected SecurityException because MANAGE_OWN_CALLS is not set"); } catch(SecurityException e) { // expected } } @SmallTest @Test public void testPlaceCallNoCallPhonePermission_Managed() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn( makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage doesn't match the PhoneAccountHandle, so this app is not managing the //ConnectionService extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); // Since the packages do not match, the caller needs CALL_PHONE permission doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission( eq(CALL_PHONE), anyString()); try { mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null); fail("Expected SecurityException because CALL_PHONE is not set"); } catch(SecurityException e) { // expected } } @SmallTest @Test public void testPlaceCallNoCallPhoneAppOp_Managed() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn( makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage doesn't match the PhoneAccountHandle, so this app is not managing the //ConnectionService extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); // Since the packages do not match, the caller needs CALL_PHONE app op when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_IGNORED); try { mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null); fail("Expected SecurityException because CALL_PHONE is not set"); } catch(SecurityException e) { // expected } } @SmallTest @Test public void testPlaceCallWithNonEmergencyPermission_SelfManaged() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn( makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed // ConnectionService. extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); // enforceCallingOrSelfPermission is implicitly granted for MANAGE_OWN_CALLS here and // CALL_PHONE is not required. when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_IGNORED); doReturn(PackageManager.PERMISSION_DENIED) .when(mContext).checkCallingPermission(CALL_PHONE); doReturn(PackageManager.PERMISSION_DENIED) .when(mContext).checkCallingPermission(CALL_PRIVILEGED); mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null); placeCallTestHelper(handle, extras, true); } @SmallTest @Test public void testPlaceCallWithNonEmergencyPermission_Managed() throws Exception { doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer( eq(DEFAULT_DIALER_PACKAGE), anyInt()); Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // callingPackage doesn't match the PhoneAccountHandle, so this app does not have a // self-managed ConnectionService extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT); when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_ALLOWED); doReturn(PackageManager.PERMISSION_GRANTED) .when(mContext).checkCallingPermission(CALL_PHONE); doReturn(PackageManager.PERMISSION_DENIED) .when(mContext).checkCallingPermission(CALL_PRIVILEGED); mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null); placeCallTestHelper(handle, extras, true); } @SmallTest @Test public void testPlaceCallWithNonEmergencyPermission() throws Exception { Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // We are assumed to be default dialer in this test, so canCallPhone is always true. when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_ALLOWED); Loading @@ -780,6 +908,7 @@ public class TelecomServiceImplTest extends TelecomTestCase { Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // We are assumed to be default dialer in this test, so canCallPhone is always true. when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_IGNORED); Loading @@ -798,6 +927,7 @@ public class TelecomServiceImplTest extends TelecomTestCase { Uri handle = Uri.parse("tel:6505551234"); Bundle extras = createSampleExtras(); // We are assumed to be default dialer in this test, so canCallPhone is always true. when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(), nullable(String.class), nullable(String.class))) .thenReturn(AppOpsManager.MODE_ALLOWED); Loading Loading @@ -1284,6 +1414,12 @@ public class TelecomServiceImplTest extends TelecomTestCase { return paBuilder; } private PhoneAccount.Builder makeSelfManagedPhoneAccount(PhoneAccountHandle paHandle) { PhoneAccount.Builder paBuilder = makePhoneAccount(paHandle); paBuilder.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED); return paBuilder; } private PhoneAccount.Builder makePhoneAccount(PhoneAccountHandle paHandle) { return new PhoneAccount.Builder(paHandle, "testLabel"); } Loading