Loading core/java/android/content/Intent.java +5 −0 Original line number Diff line number Diff line Loading @@ -1020,6 +1020,11 @@ public class Intent implements Parcelable, Cloneable { * <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"; Loading core/java/android/provider/MediaStore.java +13 −1 Original line number Diff line number Diff line Loading @@ -283,6 +283,12 @@ public final class MediaStore { * 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 */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) Loading Loading @@ -331,6 +337,12 @@ public final class MediaStore { * 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 Loading services/core/java/com/android/server/am/ActivityStackSupervisor.java +91 −4 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; 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; Loading @@ -39,11 +38,13 @@ import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; 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; Loading @@ -62,6 +63,7 @@ import android.content.Intent; 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; Loading Loading @@ -90,9 +92,11 @@ import android.os.SystemClock; 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; Loading @@ -108,6 +112,7 @@ import com.android.internal.app.IVoiceInteractor; 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; Loading Loading @@ -170,6 +175,25 @@ public final class ActivityStackSupervisor implements DisplayListener { 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; Loading Loading @@ -1519,14 +1543,23 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 + ")" Loading @@ -1541,7 +1574,19 @@ public final class ActivityStackSupervisor implements DisplayListener { 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) { Loading Loading @@ -1619,6 +1664,48 @@ public final class ActivityStackSupervisor implements DisplayListener { 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; Loading Loading
core/java/android/content/Intent.java +5 −0 Original line number Diff line number Diff line Loading @@ -1020,6 +1020,11 @@ public class Intent implements Parcelable, Cloneable { * <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"; Loading
core/java/android/provider/MediaStore.java +13 −1 Original line number Diff line number Diff line Loading @@ -283,6 +283,12 @@ public final class MediaStore { * 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 */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) Loading Loading @@ -331,6 +337,12 @@ public final class MediaStore { * 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 Loading
services/core/java/com/android/server/am/ActivityStackSupervisor.java +91 −4 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; 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; Loading @@ -39,11 +38,13 @@ import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; 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; Loading @@ -62,6 +63,7 @@ import android.content.Intent; 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; Loading Loading @@ -90,9 +92,11 @@ import android.os.SystemClock; 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; Loading @@ -108,6 +112,7 @@ import com.android.internal.app.IVoiceInteractor; 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; Loading Loading @@ -170,6 +175,25 @@ public final class ActivityStackSupervisor implements DisplayListener { 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; Loading Loading @@ -1519,14 +1543,23 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 + ")" Loading @@ -1541,7 +1574,19 @@ public final class ActivityStackSupervisor implements DisplayListener { 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) { Loading Loading @@ -1619,6 +1664,48 @@ public final class ActivityStackSupervisor implements DisplayListener { 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; Loading