Disallow non-emergency outgoing calls without CALL_PHONE permission
Bug: 21925398
Change-Id: I9cef6cd2c11f767740d03ebce99b8b54efd1f68a
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b6eb307..8f73377 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -81,7 +81,11 @@
<!-- Message indicating that the user is not allowed to make non-emergency outgoing phone calls
due to a user restriction -->
- <string name="outgoing_call_not_allowed">Only emergency calls are allowed by the device owner.</string>
+ <string name="outgoing_call_not_allowed_user_restriction">Only emergency calls are allowed by the device owner.</string>
+
+ <!-- Message indicating that the user is not allowed to make non-emergency outgoing phone calls
+ due to the lack of the CALL_PHONE permission -->
+ <string name="outgoing_call_not_allowed_no_permission">This application cannot make outgoing calls without the Phone permission.</string>
<!-- Call failure message displayed in an error dialog used to indicate that a phone number was not provided -->
<string name="outgoing_call_error_no_phone_number_supplied">To place a call, enter a valid number.</string>
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 17f18b1..b11c8ec 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -795,6 +795,9 @@
+ " is not allowed to place phone calls");
}
+ final boolean hasCallAppOp = mAppOpsManager.noteOp(AppOpsManager.OP_CALL_PHONE,
+ Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
+
synchronized (mLock) {
final UserHandle userHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
@@ -802,7 +805,7 @@
final Intent intent = new Intent(Intent.ACTION_CALL, handle);
intent.putExtras(extras);
new UserCallIntentProcessor(mContext, userHandle).processIntent(intent,
- callingPackage);
+ callingPackage, hasCallAppOp);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/src/com/android/server/telecom/components/UserCallActivity.java b/src/com/android/server/telecom/components/UserCallActivity.java
index ae9004c..d88e09e 100644
--- a/src/com/android/server/telecom/components/UserCallActivity.java
+++ b/src/com/android/server/telecom/components/UserCallActivity.java
@@ -58,8 +58,12 @@
verifyCallAction(intent);
final UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
final UserHandle userHandle = new UserHandle(userManager.getUserHandle());
+ // Once control flow has passed to this activity, it is no longer guaranteed that we can
+ // accurately determine whether the calling package has the CALL_PHONE runtime permission.
+ // At this point in time we trust that the ActivityManager has already performed this
+ // validation before starting this activity.
new UserCallIntentProcessor(this, userHandle).processIntent(getIntent(),
- getCallingPackage());
+ getCallingPackage(), true /* hasCallAppOp*/);
finish();
}
diff --git a/src/com/android/server/telecom/components/UserCallIntentProcessor.java b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
index 52708ac..5cef4e8 100644
--- a/src/com/android/server/telecom/components/UserCallIntentProcessor.java
+++ b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
@@ -21,8 +21,10 @@
import com.android.server.telecom.R;
import com.android.server.telecom.TelephonyUtil;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
@@ -68,7 +70,7 @@
*
* @param intent The intent.
*/
- public void processIntent(Intent intent, String callingPackageName) {
+ public void processIntent(Intent intent, String callingPackageName, boolean hasCallAppOp) {
// Ensure call intents are not processed on devices that are not capable of calling.
if (!isVoiceCapable()) {
return;
@@ -79,11 +81,12 @@
if (Intent.ACTION_CALL.equals(action) ||
Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
Intent.ACTION_CALL_EMERGENCY.equals(action)) {
- processOutgoingCallIntent(intent, callingPackageName);
+ processOutgoingCallIntent(intent, callingPackageName, hasCallAppOp);
}
}
- private void processOutgoingCallIntent(Intent intent, String callingPackageName) {
+ private void processOutgoingCallIntent(Intent intent, String callingPackageName,
+ boolean hasCallAppOp) {
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
@@ -93,17 +96,31 @@
PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);
}
- UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ final UserManager userManager =
+ (UserManager) mContext.getSystemService(Context.USER_SERVICE);
if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, mUserHandle)
&& !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
// Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
// restriction.
- showErrorDialogForRestrictedOutgoingCall(mContext);
+ showErrorDialogForRestrictedOutgoingCall(mContext,
+ R.string.outgoing_call_not_allowed_user_restriction);
Log.w(this, "Rejecting non-emergency phone call due to DISALLOW_OUTGOING_CALLS "
+ "restriction");
return;
}
+ PackageManager packageManager = mContext.getPackageManager();
+ final boolean callDisallowed = !hasCallAppOp ||
+ packageManager.checkPermission(android.Manifest.permission.CALL_PHONE,
+ callingPackageName) != PackageManager.PERMISSION_GRANTED;
+ if (callDisallowed && !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
+ showErrorDialogForRestrictedOutgoingCall(mContext,
+ R.string.outgoing_call_not_allowed_no_permission);
+ Log.w(this, "Rejecting non-emergency phone call because "
+ + android.Manifest.permission.CALL_PHONE + " permission is not granted.");
+ return;
+ }
+
int videoState = intent.getIntExtra(
TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
@@ -174,11 +191,10 @@
return true;
}
- private static void showErrorDialogForRestrictedOutgoingCall(Context context) {
+ private static void showErrorDialogForRestrictedOutgoingCall(Context context, int stringId) {
final Intent intent = new Intent(context, ErrorDialogActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA,
- R.string.outgoing_call_not_allowed);
+ intent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, stringId);
context.startActivityAsUser(intent, UserHandle.CURRENT);
}
}