diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 8b76cc7c966b9..d6429ae9e5407 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -867,19 +867,30 @@ public final class PendingIntent implements Parcelable { @Nullable OnFinished onFinished, @Nullable Handler handler, @Nullable String requiredPermission, @Nullable Bundle options) throws CanceledException { + if (sendAndReturnResult(context, code, intent, onFinished, handler, requiredPermission, + options) < 0) { + throw new CanceledException(); + } + } + + /** + * Like {@link #send}, but returns the result + * @hide + */ + public int sendAndReturnResult(Context context, int code, @Nullable Intent intent, + @Nullable OnFinished onFinished, @Nullable Handler handler, + @Nullable String requiredPermission, @Nullable Bundle options) + throws CanceledException { try { String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) : null; - int res = ActivityManager.getService().sendIntentSender( + return ActivityManager.getService().sendIntentSender( mTarget, mWhitelistToken, code, intent, resolvedType, onFinished != null ? new FinishedDispatcher(this, onFinished, handler) : null, requiredPermission, options); - if (res < 0) { - throw new CanceledException(); - } } catch (RemoteException e) { throw new CanceledException(e); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index dc34567532bd8..1eec982a0a9af 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4711,7 +4711,8 @@ public class ActivityManagerService extends IActivityManager.Stub .setRequestCode(requestCode) .setStartFlags(startFlags) .setProfilerInfo(profilerInfo) - .setMayWait(bOptions, userId) + .setActivityOptions(bOptions) + .setMayWait(userId) .execute(); } @@ -4782,7 +4783,8 @@ public class ActivityManagerService extends IActivityManager.Stub .setResultWho(resultWho) .setRequestCode(requestCode) .setStartFlags(startFlags) - .setMayWait(bOptions, userId) + .setActivityOptions(bOptions) + .setMayWait(userId) .setIgnoreTargetSecurity(ignoreTargetSecurity) .execute(); } catch (SecurityException e) { @@ -4818,7 +4820,8 @@ public class ActivityManagerService extends IActivityManager.Stub .setResultWho(resultWho) .setRequestCode(requestCode) .setStartFlags(startFlags) - .setMayWait(bOptions, userId) + .setActivityOptions(bOptions) + .setMayWait(userId) .setProfilerInfo(profilerInfo) .setWaitResult(res) .execute(); @@ -4842,7 +4845,8 @@ public class ActivityManagerService extends IActivityManager.Stub .setRequestCode(requestCode) .setStartFlags(startFlags) .setGlobalConfiguration(config) - .setMayWait(bOptions, userId) + .setActivityOptions(bOptions) + .setMayWait(userId) .execute(); } @@ -4897,7 +4901,8 @@ public class ActivityManagerService extends IActivityManager.Stub .setVoiceInteractor(interactor) .setStartFlags(startFlags) .setProfilerInfo(profilerInfo) - .setMayWait(bOptions, userId) + .setActivityOptions(bOptions) + .setMayWait(userId) .execute(); } @@ -4912,7 +4917,8 @@ public class ActivityManagerService extends IActivityManager.Stub .setCallingUid(callingUid) .setCallingPackage(callingPackage) .setResolvedType(resolvedType) - .setMayWait(bOptions, userId) + .setActivityOptions(bOptions) + .setMayWait(userId) .execute(); } @@ -4927,6 +4933,7 @@ public class ActivityManagerService extends IActivityManager.Stub throw new SecurityException(msg); } + final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options); final int recentsUid = mRecentTasks.getRecentsComponentUid(); final ComponentName recentsComponent = mRecentTasks.getRecentsComponent(); final String recentsPackage = recentsComponent.getPackageName(); @@ -4957,7 +4964,8 @@ public class ActivityManagerService extends IActivityManager.Stub return mActivityStartController.obtainStarter(intent, "startRecentsActivity") .setCallingUid(recentsUid) .setCallingPackage(recentsPackage) - .setMayWait(activityOptions, userId) + .setActivityOptions(safeOptions) + .setMayWait(userId) .execute(); } } finally { @@ -5045,17 +5053,17 @@ public class ActivityManagerService extends IActivityManager.Stub if (intent != null && intent.hasFileDescriptors() == true) { throw new IllegalArgumentException("File descriptors passed in Intent"); } - ActivityOptions options = ActivityOptions.fromBundle(bOptions); + SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions); synchronized (this) { final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity); if (r == null) { - ActivityOptions.abort(options); + SafeActivityOptions.abort(options); return false; } if (r.app == null || r.app.thread == null) { // The caller is not running... d'oh! - ActivityOptions.abort(options); + SafeActivityOptions.abort(options); return false; } intent = new Intent(intent); @@ -5100,7 +5108,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (aInfo == null) { // Nobody who is next! - ActivityOptions.abort(options); + SafeActivityOptions.abort(options); if (debug) Slog.d(TAG, "Next matching activity: nothing found"); return false; } @@ -5162,10 +5170,13 @@ public class ActivityManagerService extends IActivityManager.Stub enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS, "startActivityFromRecents()"); + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { - return mStackSupervisor.startActivityFromRecents(taskId, bOptions); + return mStackSupervisor.startActivityFromRecents(callingPid, callingUid, taskId, + SafeActivityOptions.fromBundle(bOptions)); } } finally { Binder.restoreCallingIdentity(origId); @@ -5182,7 +5193,8 @@ public class ActivityManagerService extends IActivityManager.Stub userId, false, ALLOW_FULL_ONLY, reason, null); // TODO: Switch to user app stacks here. int ret = mActivityStartController.startActivities(caller, -1, callingPackage, - intents, resolvedTypes, resultTo, bOptions, userId, reason); + intents, resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId, + reason); return ret; } @@ -7941,9 +7953,9 @@ public class ActivityManagerService extends IActivityManager.Stub flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT |PendingIntent.FLAG_UPDATE_CURRENT); - PendingIntentRecord.Key key = new PendingIntentRecord.Key( - type, packageName, activity, resultWho, - requestCode, intents, resolvedTypes, flags, bOptions, userId); + PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, activity, + resultWho, requestCode, intents, resolvedTypes, flags, + SafeActivityOptions.fromBundle(bOptions), userId); WeakReference ref; ref = mIntentSenderRecords.get(key); PendingIntentRecord rec = ref != null ? ref.get() : null; @@ -10434,9 +10446,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void startInPlaceAnimationOnFrontMostApplication(Bundle opts) throws RemoteException { - final ActivityOptions activityOptions = ActivityOptions.fromBundle(opts); - if (activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE || - activityOptions.getCustomInPlaceResId() == 0) { + final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts); + final ActivityOptions activityOptions = safeOptions != null + ? safeOptions.getOptions(mStackSupervisor) + : null; + if (activityOptions == null + || activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE + || activityOptions.getCustomInPlaceResId() == 0) { throw new IllegalArgumentException("Expected in-place ActivityOption " + "with valid animation"); } @@ -10539,16 +10555,17 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId); synchronized(this) { - moveTaskToFrontLocked(taskId, flags, bOptions, false /* fromRecents */); + moveTaskToFrontLocked(taskId, flags, SafeActivityOptions.fromBundle(bOptions), + false /* fromRecents */); } } - void moveTaskToFrontLocked(int taskId, int flags, Bundle bOptions, boolean fromRecents) { - ActivityOptions options = ActivityOptions.fromBundle(bOptions); + void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options, + boolean fromRecents) { if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), Binder.getCallingUid(), -1, -1, "Task to front")) { - ActivityOptions.abort(options); + SafeActivityOptions.abort(options); return; } final long origId = Binder.clearCallingIdentity(); @@ -10562,7 +10579,10 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode"); return; } - mStackSupervisor.findTaskToMoveToFront(task, flags, options, "moveTaskToFront", + ActivityOptions realOptions = options != null + ? options.getOptions(mStackSupervisor) + : null; + mStackSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront", false /* forceNonResizable */); final ActivityRecord topActivity = task.getTopActivity(); @@ -10576,7 +10596,7 @@ public class ActivityManagerService extends IActivityManager.Stub } finally { Binder.restoreCallingIdentity(origId); } - ActivityOptions.abort(options); + SafeActivityOptions.abort(options); } /** @@ -13534,6 +13554,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean convertToTranslucent(IBinder token, Bundle options) { + SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options); final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -13545,7 +13566,7 @@ public class ActivityManagerService extends IActivityManager.Stub int index = task.mActivities.lastIndexOf(r); if (index > 0) { ActivityRecord under = task.mActivities.get(index - 1); - under.returningOptions = ActivityOptions.fromBundle(options); + under.returningOptions = safeOptions.getOptions(r); } final boolean translucentChanged = r.changeWindowTranslucency(false); if (translucentChanged) { @@ -24909,7 +24930,8 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (ActivityManagerService.this) { return mActivityStartController.startActivitiesInPackage(packageUid, packageName, - intents, resolvedTypes, /*resultTo*/ null, bOptions, userId); + intents, resolvedTypes, null /* resultTo */, + SafeActivityOptions.fromBundle(bOptions), userId); } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index eb14bbda4c4bd..bfb563fd93a80 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1591,7 +1591,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp, - ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) { + ActivityRecord resultRecord, ActivityStack resultStack) { final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid, callingUid); if (startAnyPerm == PERMISSION_GRANTED) { @@ -1645,57 +1645,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D Slog.w(TAG, message); return false; } - if (options != null) { - // If a launch task id is specified, then ensure that the caller is the recents - // component or has the START_TASKS_FROM_RECENTS permission - if (options.getLaunchTaskId() != INVALID_TASK_ID - && !mRecentTasks.isCallerRecents(callingUid)) { - final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS, - callingPid, callingUid); - if (startInTaskPerm == PERMISSION_DENIED) { - final String msg = "Permission Denial: starting " + intent.toString() - + " from " + callerApp + " (pid=" + callingPid - + ", uid=" + callingUid + ") with launchTaskId=" - + options.getLaunchTaskId(); - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - } - // Check if someone tries to launch an activity on a private display with a different - // owner. - final int launchDisplayId = options.getLaunchDisplayId(); - if (launchDisplayId != INVALID_DISPLAY && !isCallerAllowedToLaunchOnDisplay(callingPid, - callingUid, launchDisplayId, aInfo)) { - final String msg = "Permission Denial: starting " + intent.toString() - + " from " + callerApp + " (pid=" + callingPid - + ", uid=" + callingUid + ") with launchDisplayId=" - + launchDisplayId; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - // Check if someone tries to launch an unwhitelisted activity into LockTask mode. - final boolean lockTaskMode = options.getLockTaskMode(); - if (lockTaskMode && !mService.mLockTaskController.isPackageWhitelisted( - UserHandle.getUserId(callingUid), aInfo.packageName)) { - final String msg = "Permission Denial: starting " + intent.toString() - + " from " + callerApp + " (pid=" + callingPid - + ", uid=" + callingUid + ") with lockTaskMode=true"; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - - // Check permission for remote animations - final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter(); - if (adapter != null && mService.checkPermission( - CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid) - != PERMISSION_GRANTED) { - final String msg = "Permission Denial: starting " + intent.toString() - + " from " + callerApp + " (pid=" + callingPid - + ", uid=" + callingUid + ") with remoteAnimationAdapter"; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - } return true; } @@ -2166,8 +2115,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, - String reason, boolean forceNonResizeable) { + void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason, + boolean forceNonResizeable) { final ActivityStack currentStack = task.getStack(); if (currentStack == null) { Slog.e(TAG, "findTaskToMoveToFront: can't move task=" @@ -4546,16 +4495,17 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D task.setTaskDockedResizing(true); } - int startActivityFromRecents(int taskId, Bundle bOptions) { + int startActivityFromRecents(int callingPid, int callingUid, int taskId, + SafeActivityOptions options) { final TaskRecord task; - final int callingUid; final String callingPackage; final Intent intent; final int userId; int activityType = ACTIVITY_TYPE_UNDEFINED; int windowingMode = WINDOWING_MODE_UNDEFINED; - final ActivityOptions activityOptions = (bOptions != null) - ? new ActivityOptions(bOptions) : null; + final ActivityOptions activityOptions = options != null + ? options.getOptions(this) + : null; if (activityOptions != null) { activityType = activityOptions.getLaunchActivityType(); windowingMode = activityOptions.getLaunchWindowingMode(); @@ -4603,7 +4553,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity); mActivityMetricsLogger.notifyActivityLaunching(); try { - mService.moveTaskToFrontLocked(task.taskId, 0, bOptions, + mService.moveTaskToFrontLocked(task.taskId, 0, options, true /* fromRecents */); } finally { mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, @@ -4622,13 +4572,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D task.getStack()); return ActivityManager.START_TASK_TO_FRONT; } - callingUid = task.mCallingUid; callingPackage = task.mCallingPackage; intent = task.intent; intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); userId = task.userId; - int result = mService.getActivityStartController().startActivityInPackage(callingUid, - callingPackage, intent, null, null, null, 0, 0, bOptions, userId, task, + int result = mService.getActivityStartController().startActivityInPackage( + task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null, + null, 0, 0, options, userId, task, "startActivityFromRecents"); if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { setResizingDuringAnimation(task); diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java index aed49e0037e19..f9932b20fb5b3 100644 --- a/services/core/java/com/android/server/am/ActivityStartController.java +++ b/services/core/java/com/android/server/am/ActivityStartController.java @@ -220,43 +220,44 @@ public class ActivityStartController { } } - final int startActivityInPackage(int uid, String callingPackage, - Intent intent, String resolvedType, IBinder resultTo, - String resultWho, int requestCode, int startFlags, Bundle bOptions, int userId, - TaskRecord inTask, String reason) { + final int startActivityInPackage(int uid, int realCallingUid, int realCallingPid, + String callingPackage, Intent intent, String resolvedType, IBinder resultTo, + String resultWho, int requestCode, int startFlags, SafeActivityOptions options, + int userId, TaskRecord inTask, String reason) { - userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(), - Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "startActivityInPackage", - null); + userId = mService.mUserController.handleIncomingUser(realCallingPid, realCallingUid, userId, + false, ALLOW_FULL_ONLY, "startActivityInPackage", null); // TODO: Switch to user app stacks here. return obtainStarter(intent, reason) .setCallingUid(uid) + .setRealCallingPid(realCallingPid) + .setRealCallingUid(realCallingUid) .setCallingPackage(callingPackage) .setResolvedType(resolvedType) .setResultTo(resultTo) .setResultWho(resultWho) .setRequestCode(requestCode) .setStartFlags(startFlags) - .setMayWait(bOptions, userId) + .setActivityOptions(options) + .setMayWait(userId) .setInTask(inTask) .execute(); } final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents, - String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId) { + String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId) { final String reason = "startActivityInPackage"; userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, reason, null); // TODO: Switch to user app stacks here. - int ret = startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo, - bOptions, userId, reason); - return ret; + return startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo, options, + userId, reason); } int startActivities(IApplicationThread caller, int callingUid, String callingPackage, - Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId, - String reason) { + Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, + int userId, String reason) { if (intents == null) { throw new NullPointerException("intents is null"); } @@ -312,9 +313,9 @@ public class ActivityStartController { "FLAG_CANT_SAVE_STATE not supported here"); } - ActivityOptions options = ActivityOptions.fromBundle( - i == intents.length - 1 ? bOptions : null); - + final SafeActivityOptions checkedOptions = i == intents.length - 1 + ? options + : null; final int res = obtainStarter(intent, reason) .setCaller(caller) .setResolvedType(resolvedTypes[i]) @@ -326,7 +327,7 @@ public class ActivityStartController { .setCallingPackage(callingPackage) .setRealCallingPid(realCallingPid) .setRealCallingUid(realCallingUid) - .setActivityOptions(options) + .setActivityOptions(checkedOptions) .setComponentSpecified(componentSpecified) .setOutActivity(outActivity) .execute(); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 3a13155d88e18..8595aa3948005 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -74,6 +74,7 @@ import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.IApplicationThread; @@ -298,7 +299,7 @@ class ActivityStarter { int realCallingPid; int realCallingUid; int startFlags; - ActivityOptions activityOptions; + SafeActivityOptions activityOptions; boolean ignoreTargetSecurity; boolean componentSpecified; ActivityRecord[] outActivity; @@ -306,13 +307,12 @@ class ActivityStarter { String reason; ProfilerInfo profilerInfo; Configuration globalConfig; - Bundle waitOptions; int userId; WaitResult waitResult; /** * Indicates that we should wait for the result of the start request. This flag is set when - * {@link ActivityStarter#setMayWait(Bundle, int)} is called. + * {@link ActivityStarter#setMayWait(int)} is called. * {@see ActivityStarter#startActivityMayWait}. */ boolean mayWait; @@ -353,7 +353,6 @@ class ActivityStarter { reason = null; profilerInfo = null; globalConfig = null; - waitOptions = null; userId = 0; waitResult = null; mayWait = false; @@ -388,7 +387,6 @@ class ActivityStarter { reason = request.reason; profilerInfo = request.profilerInfo; globalConfig = request.globalConfig; - waitOptions = request.waitOptions; userId = request.userId; waitResult = request.waitResult; mayWait = request.mayWait; @@ -473,7 +471,7 @@ class ActivityStarter { mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, mRequest.resultWho, mRequest.requestCode, mRequest.startFlags, mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig, - mRequest.waitOptions, mRequest.ignoreTargetSecurity, mRequest.userId, + mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId, mRequest.inTask, mRequest.reason); } else { return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, @@ -513,7 +511,7 @@ class ActivityStarter { IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int realCallingPid, int realCallingUid, int startFlags, - ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, + SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity, TaskRecord inTask, String reason) { if (TextUtils.isEmpty(reason)) { @@ -555,8 +553,9 @@ class ActivityStarter { IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int realCallingPid, int realCallingUid, int startFlags, - ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, - ActivityRecord[] outActivity, TaskRecord inTask) { + SafeActivityOptions options, + boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity, + TaskRecord inTask) { int err = ActivityManager.START_SUCCESS; // Pull the optional Ephemeral Installer-only bundle out of the options early. final Bundle verificationBundle @@ -603,7 +602,7 @@ class ActivityStarter { // Transfer the result target from the source activity to the new // one being started, including any failures. if (requestCode >= 0) { - ActivityOptions.abort(options); + SafeActivityOptions.abort(options); return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT; } resultRecord = sourceRecord.resultTo; @@ -691,16 +690,20 @@ class ActivityStarter { resultStack.sendActivityResultLocked( -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null); } - ActivityOptions.abort(options); + SafeActivityOptions.abort(options); return err; } boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, - requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp, - resultRecord, resultStack, options); + requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, + callerApp, resultRecord, resultStack); abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); + // Merge the two options bundles, while realCallerOptions takes precedence. + ActivityOptions checkedOptions = options != null + ? options.getOptions(intent, aInfo, callerApp, mSupervisor) + : null; if (mService.mController != null) { try { // The Intent we give to the watcher has the extra data @@ -715,7 +718,7 @@ class ActivityStarter { mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage); if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, - callingUid, options)) { + callingUid, checkedOptions)) { // activity start was intercepted, e.g. because the target user is currently in quiet // mode (turn off work) or the target application is suspended intent = mInterceptor.mIntent; @@ -725,7 +728,7 @@ class ActivityStarter { inTask = mInterceptor.mInTask; callingPid = mInterceptor.mCallingPid; callingUid = mInterceptor.mCallingUid; - options = mInterceptor.mActivityOptions; + checkedOptions = mInterceptor.mActivityOptions; } if (abort) { @@ -735,7 +738,7 @@ class ActivityStarter { } // We pretend to the caller that it was really started, but // they will just get a cancel result. - ActivityOptions.abort(options); + ActivityOptions.abort(checkedOptions); return START_ABORTED; } @@ -796,7 +799,7 @@ class ActivityStarter { ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null, - mSupervisor, options, sourceRecord); + mSupervisor, checkedOptions, sourceRecord); if (outActivity != null) { outActivity[0] = r; } @@ -808,13 +811,16 @@ class ActivityStarter { } final ActivityStack stack = mSupervisor.mFocusedStack; + + // If we are starting an activity that is not from the same uid as the currently resumed + // one, check whether app switches are allowed. if (voiceSession == null && (stack.mResumedActivity == null - || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) { + || stack.mResumedActivity.info.applicationInfo.uid != realCallingUid)) { if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, realCallingPid, realCallingUid, "Activity start")) { mController.addPendingActivityLaunch(new PendingActivityLaunch(r, sourceRecord, startFlags, stack, callerApp)); - ActivityOptions.abort(options); + ActivityOptions.abort(checkedOptions); return ActivityManager.START_SWITCHES_CANCELED; } } @@ -833,9 +839,10 @@ class ActivityStarter { mController.doPendingActivityLaunches(false); return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, - true /* doResume */, options, inTask, outActivity); + true /* doResume */, checkedOptions, inTask, outActivity); } + /** * Creates a launch intent for the given auxiliary resolution data. */ @@ -900,8 +907,8 @@ class ActivityStarter { IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, WaitResult outResult, - Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId, - TaskRecord inTask, String reason) { + Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity, + int userId, TaskRecord inTask, String reason) { // Refuse possible leaked file descriptors if (intent != null && intent.hasFileDescriptors()) { throw new IllegalArgumentException("File descriptors passed in Intent"); @@ -953,7 +960,6 @@ class ActivityStarter { // Collect information about the target of the Intent. ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo); - ActivityOptions options = ActivityOptions.fromBundle(bOptions); synchronized (mService) { final int realCallingPid = Binder.getCallingPid(); final int realCallingUid = Binder.getCallingUid(); @@ -993,7 +999,7 @@ class ActivityStarter { Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid + ") when starting: " + intent.toString()); - ActivityOptions.abort(options); + SafeActivityOptions.abort(options); return ActivityManager.START_PERMISSION_DENIED; } } @@ -1039,12 +1045,10 @@ class ActivityStarter { } final ActivityRecord[] outRecord = new ActivityRecord[1]; - int res = startActivity(caller, intent, ephemeralIntent, resolvedType, - aInfo, rInfo, voiceSession, voiceInteractor, - resultTo, resultWho, requestCode, callingPid, - callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, - options, ignoreTargetSecurity, componentSpecified, outRecord, inTask, - reason); + int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo, + voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, + callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, + ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason); Binder.restoreCallingIdentity(origId); @@ -1248,7 +1252,7 @@ class ActivityStarter { outActivity[0] = reusedActivity; } - return START_TASK_TO_FRONT; + return START_DELIVERED_TO_TOP; } } @@ -2447,11 +2451,15 @@ class ActivityStarter { return this; } - ActivityStarter setActivityOptions(ActivityOptions options) { + ActivityStarter setActivityOptions(SafeActivityOptions options) { mRequest.activityOptions = options; return this; } + ActivityStarter setActivityOptions(Bundle bOptions) { + return setActivityOptions(SafeActivityOptions.fromBundle(bOptions)); + } + ActivityStarter setIgnoreTargetSecurity(boolean ignoreTargetSecurity) { mRequest.ignoreTargetSecurity = ignoreTargetSecurity; return this; @@ -2487,19 +2495,13 @@ class ActivityStarter { return this; } - ActivityStarter setWaitOptions(Bundle options) { - mRequest.waitOptions = options; - return this; - } - ActivityStarter setUserId(int userId) { mRequest.userId = userId; return this; } - ActivityStarter setMayWait(Bundle options, int userId) { + ActivityStarter setMayWait(int userId) { mRequest.mayWait = true; - mRequest.waitOptions = options; mRequest.userId = userId; return this; diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 26d65bc7414c3..0da7e0ec2f6d0 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -408,9 +408,11 @@ class AppErrors { final Set cats = task.intent.getCategories(); if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) { mService.getActivityStartController().startActivityInPackage( - task.mCallingUid, task.mCallingPackage, task.intent, null, null, - null, 0, 0, ActivityOptions.makeBasic().toBundle(), task.userId, - null, "AppErrors"); + task.mCallingUid, callingPid, callingUid, task.mCallingPackage, + task.intent, null, null, null, 0, 0, + new SafeActivityOptions(ActivityOptions.makeBasic()), + task.userId, null, + "AppErrors"); } } } diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java index f821f6bdb9257..5f5a504bb0147 100644 --- a/services/core/java/com/android/server/am/AppTaskImpl.java +++ b/services/core/java/com/android/server/am/AppTaskImpl.java @@ -20,6 +20,7 @@ import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.IAppTask; import android.app.IApplicationThread; import android.content.Intent; @@ -93,10 +94,13 @@ class AppTaskImpl extends IAppTask.Stub { public void moveToFront() { checkCaller(); // Will bring task to front if it already has a root activity. + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { - mService.mStackSupervisor.startActivityFromRecents(mTaskId, null); + mService.mStackSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId, + null); } } finally { Binder.restoreCallingIdentity(origId); @@ -127,7 +131,8 @@ class AppTaskImpl extends IAppTask.Stub { .setCaller(appThread) .setCallingPackage(callingPackage) .setResolvedType(resolvedType) - .setMayWait(bOptions, callingUser) + .setActivityOptions(bOptions) + .setMayWait(callingUser) .setInTask(tr) .execute(); } diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index c26e7703a1ca7..8e9d85d60479b 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -16,10 +16,12 @@ package com.android.server.am; +import static android.app.ActivityManager.START_SUCCESS; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.content.IIntentSender; import android.content.IIntentReceiver; import android.app.PendingIntent; @@ -65,7 +67,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { final int requestCode; final Intent requestIntent; final String requestResolvedType; - final Bundle options; + final SafeActivityOptions options; Intent[] allIntents; String[] allResolvedTypes; final int flags; @@ -75,7 +77,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { private static final int ODD_PRIME_NUMBER = 37; Key(int _t, String _p, ActivityRecord _a, String _w, - int _r, Intent[] _i, String[] _it, int _f, Bundle _o, int _userId) { + int _r, Intent[] _i, String[] _it, int _f, SafeActivityOptions _o, int _userId) { type = _t; packageName = _p; activity = _a; @@ -310,17 +312,16 @@ final class PendingIntentRecord extends IIntentSender.Stub { if (userId == UserHandle.USER_CURRENT) { userId = owner.mUserController.getCurrentOrTargetUserId(); } - int res = 0; + int res = START_SUCCESS; switch (key.type) { case ActivityManager.INTENT_SENDER_ACTIVITY: - if (options == null) { - options = key.options; - } else if (key.options != null) { - Bundle opts = new Bundle(key.options); - opts.putAll(options); - options = opts; - } try { + SafeActivityOptions mergedOptions = key.options; + if (mergedOptions == null) { + mergedOptions = SafeActivityOptions.fromBundle(options); + } else { + mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options)); + } if (key.allIntents != null && key.allIntents.length > 1) { Intent[] allIntents = new Intent[key.allIntents.length]; String[] allResolvedTypes = new String[key.allIntents.length]; @@ -332,14 +333,14 @@ final class PendingIntentRecord extends IIntentSender.Stub { } allIntents[allIntents.length-1] = finalIntent; allResolvedTypes[allResolvedTypes.length-1] = resolvedType; - owner.getActivityStartController().startActivitiesInPackage(uid, - key.packageName, allIntents, allResolvedTypes, resultTo, - options, userId); + res = owner.getActivityStartController().startActivitiesInPackage( + uid, key.packageName, allIntents, allResolvedTypes, + resultTo, mergedOptions, userId); } else { - owner.getActivityStartController().startActivityInPackage(uid, - key.packageName, finalIntent, resolvedType, resultTo, - resultWho, requestCode, 0, options, userId, null, - "PendingIntentRecord"); + res = owner.getActivityStartController().startActivityInPackage(uid, + callingPid, callingUid, key.packageName, finalIntent, + resolvedType, resultTo, resultWho, requestCode, 0, + mergedOptions, userId, null, "PendingIntentRecord"); } } catch (RuntimeException e) { Slog.w(TAG, "Unable to send startActivity intent", e); diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java new file mode 100644 index 0000000000000..d08111ec0aa5d --- /dev/null +++ b/services/core/java/com/android/server/am/SafeActivityOptions.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.view.Display.INVALID_DISPLAY; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.TaskRecord.INVALID_TASK_ID; + +import android.annotation.Nullable; +import android.app.ActivityOptions; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.os.Binder; +import android.os.Bundle; +import android.os.UserHandle; +import android.util.Slog; +import android.view.RemoteAnimationAdapter; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Wraps {@link ActivityOptions}, records binder identity, and checks permission when retrieving + * the inner options. Also supports having two set of options: Once from the original caller, and + * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}. + */ +class SafeActivityOptions { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_AM; + + private final int mOriginalCallingPid; + private final int mOriginalCallingUid; + private int mRealCallingPid; + private int mRealCallingUid; + private final @Nullable ActivityOptions mOriginalOptions; + private @Nullable ActivityOptions mCallerOptions; + + /** + * Constructs a new instance from a bundle and records {@link Binder#getCallingPid}/ + * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing + * this object. + * + * @param bOptions The {@link ActivityOptions} as {@link Bundle}. + */ + static SafeActivityOptions fromBundle(Bundle bOptions) { + return bOptions != null + ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions)) + : null; + } + + /** + * Constructs a new instance and records {@link Binder#getCallingPid}/ + * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing + * this object. + * + * @param options The options to wrap. + */ + SafeActivityOptions(@Nullable ActivityOptions options) { + mOriginalCallingPid = Binder.getCallingPid(); + mOriginalCallingUid = Binder.getCallingUid(); + mOriginalOptions = options; + } + + /** + * Overrides options with options from a caller and records {@link Binder#getCallingPid}/ + * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this + * method. + */ + void setCallerOptions(@Nullable ActivityOptions options) { + mRealCallingPid = Binder.getCallingPid(); + mRealCallingUid = Binder.getCallingUid(); + mCallerOptions = options; + } + + /** + * Performs permission check and retrieves the options. + * + * @param r The record of the being started activity. + */ + ActivityOptions getOptions(ActivityRecord r) throws SecurityException { + return getOptions(r.intent, r.info, r.app, r.mStackSupervisor); + } + + /** + * Performs permission check and retrieves the options when options are not being used to launch + * a specific activity (i.e. a task is moved to front). + */ + ActivityOptions getOptions(ActivityStackSupervisor supervisor) throws SecurityException { + return getOptions(null, null, null, supervisor); + } + + /** + * Performs permission check and retrieves the options. + * + * @param intent The intent that is being launched. + * @param aInfo The info of the activity being launched. + * @param callerApp The record of the caller. + */ + ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo, + @Nullable ProcessRecord callerApp, + ActivityStackSupervisor supervisor) throws SecurityException { + if (mOriginalOptions != null) { + checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions, + mOriginalCallingPid, mOriginalCallingUid); + } + if (mCallerOptions != null) { + checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions, + mRealCallingPid, mRealCallingUid); + } + return mergeActivityOptions(mOriginalOptions, mCallerOptions); + } + + /** + * @see ActivityOptions#popAppVerificationBundle + */ + Bundle popAppVerificationBundle() { + return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null; + } + + private void abort() { + if (mOriginalOptions != null) { + ActivityOptions.abort(mOriginalOptions); + } + if (mCallerOptions != null) { + ActivityOptions.abort(mCallerOptions); + } + } + + static void abort(@Nullable SafeActivityOptions options) { + if (options != null) { + options.abort(); + } + } + + /** + * Merges two activity options into one, with {@code options2} taking precedence in case of a + * conflict. + */ + @VisibleForTesting + @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1, + @Nullable ActivityOptions options2) { + if (options1 == null) { + return options2; + } + if (options2 == null) { + return options1; + } + final Bundle b1 = options1.toBundle(); + final Bundle b2 = options2.toBundle(); + b1.putAll(b2); + return ActivityOptions.fromBundle(b1); + } + + private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo, + @Nullable ProcessRecord callerApp, ActivityStackSupervisor supervisor, + ActivityOptions options, int callingPid, int callingUid) { + // If a launch task id is specified, then ensure that the caller is the recents + // component or has the START_TASKS_FROM_RECENTS permission + if (options.getLaunchTaskId() != INVALID_TASK_ID + && !supervisor.mRecentTasks.isCallerRecents(callingUid)) { + final int startInTaskPerm = supervisor.mService.checkPermission( + START_TASKS_FROM_RECENTS, callingPid, callingUid); + if (startInTaskPerm == PERMISSION_DENIED) { + final String msg = "Permission Denial: starting " + getIntentString(intent) + + " from " + callerApp + " (pid=" + callingPid + + ", uid=" + callingUid + ") with launchTaskId=" + + options.getLaunchTaskId(); + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } + // Check if someone tries to launch an activity on a private display with a different + // owner. + final int launchDisplayId = options.getLaunchDisplayId(); + if (aInfo != null && launchDisplayId != INVALID_DISPLAY + && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, + launchDisplayId, aInfo)) { + final String msg = "Permission Denial: starting " + getIntentString(intent) + + " from " + callerApp + " (pid=" + callingPid + + ", uid=" + callingUid + ") with launchDisplayId=" + + launchDisplayId; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + // Check if someone tries to launch an unwhitelisted activity into LockTask mode. + final boolean lockTaskMode = options.getLockTaskMode(); + if (aInfo != null && lockTaskMode + && !supervisor.mService.mLockTaskController.isPackageWhitelisted( + UserHandle.getUserId(callingUid), aInfo.packageName)) { + final String msg = "Permission Denial: starting " + getIntentString(intent) + + " from " + callerApp + " (pid=" + callingPid + + ", uid=" + callingUid + ") with lockTaskMode=true"; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + + // Check permission for remote animations + final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter(); + if (adapter != null && supervisor.mService.checkPermission( + CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid) + != PERMISSION_GRANTED) { + final String msg = "Permission Denial: starting " + getIntentString(intent) + + " from " + callerApp + " (pid=" + callingPid + + ", uid=" + callingUid + ") with remoteAnimationAdapter"; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } + + private String getIntentString(Intent intent) { + return intent != null ? intent.toString() : "(no intent)"; + } +} diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 1717b3d1a5f79..14995b3827e14 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -560,7 +560,6 @@ public class LauncherAppsService extends SystemService { private boolean startShortcutIntentsAsPublisher(@NonNull Intent[] intents, @NonNull String publisherPackage, Bundle startActivityOptions, int userId) { final int code; - final long ident = injectClearCallingIdentity(); try { code = mActivityManagerInternal.startActivitiesAsPackage(publisherPackage, userId, intents, startActivityOptions); @@ -575,8 +574,6 @@ public class LauncherAppsService extends SystemService { Slog.d(TAG, "SecurityException while launching intent", e); } return false; - } finally { - injectRestoreCallingIdentity(ident); } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java index 589a89bcbddf4..b58c7003f39e3 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java @@ -228,8 +228,8 @@ public class ActivityStarterTests extends ActivityTestsBase { if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) { doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission( - any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), anyBoolean(), - any(), any(), any(), any()); + any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), + anyBoolean(), any(), any(), any()); } try { @@ -278,7 +278,7 @@ public class ActivityStarterTests extends ActivityTestsBase { .setResultTo(resultTo) .setRequestCode(requestCode) .setReason("testLaunchActivityPermissionDenied") - .setActivityOptions(options) + .setActivityOptions(new SafeActivityOptions(options)) .execute(); verify(options, times(1)).abort(); } diff --git a/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java b/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java new file mode 100644 index 0000000000000..168bc1782d564 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import static org.junit.Assert.assertEquals; + +import android.app.ActivityOptions; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.FlakyTest; +import android.support.test.filters.MediumTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@MediumTest +@Presubmit +@FlakyTest +@RunWith(AndroidJUnit4.class) +public class SafeActivityOptionsTest { + + @Test + public void testMerge() { + final ActivityOptions opts1 = ActivityOptions.makeBasic(); + opts1.setLaunchDisplayId(5); + final ActivityOptions opts2 = ActivityOptions.makeBasic(); + opts2.setLaunchDisplayId(6); + final SafeActivityOptions options = new SafeActivityOptions(opts1); + final ActivityOptions result = options.mergeActivityOptions(opts1, opts2); + assertEquals(6, result.getLaunchDisplayId()); + } +}