Merge "Prevent certain actions of app has revoked permissions" into mnc-dev
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5190037..83d6cb0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1020,6 +1020,11 @@
* <p>Note: this Intent <strong>cannot</strong> be used to call emergency
* numbers. Applications can <strong>dial</strong> emergency numbers using
* {@link #ACTION_DIAL}, however.
+ *
+ * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#MNC MNC}
+ * and above and declares as using the {@link android.Manifest.permission#CALL_PHONE}
+ * permission which is not granted, then atempting to use this action will
+ * result in a {@link java.lang.SecurityException}.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CALL = "android.intent.action.CALL";
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 51dbdee..e63fb04 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -283,7 +283,13 @@
* supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
* If you don't set a ClipData, it will be copied there for you when calling
* {@link Context#startActivity(Intent)}.
- * @see #EXTRA_OUTPUT
+ *
+ * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#MNC MNC} and above
+ * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
+ * is not granted, then atempting to use this action will result in a {@link
+ * java.lang.SecurityException}.
+ *
+ * @see #EXTRA_OUTPUT
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
@@ -331,6 +337,12 @@
* supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
* If you don't set a ClipData, it will be copied there for you when calling
* {@link Context#startActivity(Intent)}.
+ *
+ * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#MNC MNC} and above
+ * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
+ * is not granted, then atempting to use this action will result in a {@link
+ * java.lang.SecurityException}.
+ *
* @see #EXTRA_OUTPUT
* @see #EXTRA_VIDEO_QUALITY
* @see #EXTRA_SIZE_LIMIT
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f967aef..4ce5c7e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -25,7 +25,6 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
@@ -39,11 +38,13 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.IActivityContainer;
import android.app.IActivityContainerCallback;
import android.app.IActivityManager;
@@ -62,6 +63,7 @@
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -90,9 +92,11 @@
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.service.voice.IVoiceInteractionSession;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
@@ -108,6 +112,7 @@
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.TransferPipe;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.am.ActivityStack.ActivityState;
@@ -170,6 +175,25 @@
private static final String LOCK_TASK_TAG = "Lock-to-App";
+ // Activity actions an app cannot start if it uses a permission which is not granted.
+ private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
+ new ArrayMap<>();
+ static {
+ ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
+ Manifest.permission.CAMERA);
+ ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
+ Manifest.permission.CAMERA);
+ ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
+ Manifest.permission.CALL_PHONE);
+ }
+
+ /** Action not restricted for the calling package. */
+ private static final int ACTION_RESTRICTION_NONE = 0;
+ /** Action restricted for the calling package by not granting a used permission. */
+ private static final int ACTION_RESTRICTION_PERMISSION = 1;
+ /** Action restricted for the calling package by not allowing a used permission's app op. */
+ private static final int ACTION_RESTRICTION_APPOP = 2;
+
/** Status Bar Service **/
private IBinder mToken = new Binder();
private IStatusBarService mStatusBarService;
@@ -1519,14 +1543,23 @@
START_ANY_ACTIVITY, callingPid, callingUid);
final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
callingUid, aInfo.applicationInfo.uid, aInfo.exported);
- if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
+ final int actionRestriction = getActionRestrictionForCallingPackage(
+ intent.getAction(), callingPackage, callingPid, callingUid);
+
+ if (startAnyPerm != PERMISSION_GRANTED && (componentPerm != PERMISSION_GRANTED
+ || actionRestriction == ACTION_RESTRICTION_PERMISSION)) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1,
resultRecord, resultWho, requestCode,
Activity.RESULT_CANCELED, null);
}
String msg;
- if (!aInfo.exported) {
+ if (actionRestriction == ACTION_RESTRICTION_PERMISSION) {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")" + " with revoked permission "
+ + ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
+ } else if (!aInfo.exported) {
msg = "Permission Denial: starting " + intent.toString()
+ " from " + callerApp + " (pid=" + callingPid
+ ", uid=" + callingUid + ")"
@@ -1541,7 +1574,19 @@
throw new SecurityException(msg);
}
- boolean abort = !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
+ boolean abort = false;
+
+ if (startAnyPerm != PERMISSION_GRANTED
+ && actionRestriction == ACTION_RESTRICTION_APPOP) {
+ String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires " + aInfo.permission;
+ Slog.w(TAG, msg);
+ abort = true;
+ }
+
+ abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
if (mService.mController != null) {
@@ -1619,6 +1664,48 @@
return err;
}
+ private int getActionRestrictionForCallingPackage(String action,
+ String callingPackage, int callingPid, int callingUid) {
+ if (action == null) {
+ return ACTION_RESTRICTION_NONE;
+ }
+
+ String permission = ACTION_TO_RUNTIME_PERMISSION.get(action);
+ if (permission == null) {
+ return ACTION_RESTRICTION_NONE;
+ }
+
+ final PackageInfo packageInfo;
+ try {
+ packageInfo = mService.mContext.getPackageManager()
+ .getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.i(TAG, "Cannot find package info for " + callingPackage);
+ return ACTION_RESTRICTION_NONE;
+ }
+
+ if (!ArrayUtils.contains(packageInfo.requestedPermissions, permission)) {
+ return ACTION_RESTRICTION_NONE;
+ }
+
+ if (mService.checkPermission(permission, callingPid, callingUid) ==
+ PackageManager.PERMISSION_DENIED) {
+ return ACTION_RESTRICTION_PERMISSION;
+ }
+
+ final int opCode = AppOpsManager.permissionToOpCode(permission);
+ if (opCode == AppOpsManager.OP_NONE) {
+ return ACTION_RESTRICTION_NONE;
+ }
+
+ if (mService.mAppOpsService.noteOperation(opCode, callingUid,
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return ACTION_RESTRICTION_APPOP;
+ }
+
+ return ACTION_RESTRICTION_NONE;
+ }
+
ActivityStack computeStackFocus(ActivityRecord r, boolean newTask) {
final TaskRecord task = r.task;