.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
- .setMayWait(bOptions, userId)
+ .setActivityOptions(bOptions)
+ .setMayWait(userId)
.execute();
}
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
- .setMayWait(bOptions, userId)
+ .setActivityOptions(bOptions)
+ .setMayWait(userId)
.setIgnoreTargetSecurity(ignoreTargetSecurity)
.execute();
} catch (SecurityException e) {
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
- .setMayWait(bOptions, userId)
+ .setActivityOptions(bOptions)
+ .setMayWait(userId)
.setProfilerInfo(profilerInfo)
.setWaitResult(res)
.execute();
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setGlobalConfiguration(config)
- .setMayWait(bOptions, userId)
+ .setActivityOptions(bOptions)
+ .setMayWait(userId)
.execute();
}
.setVoiceInteractor(interactor)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
- .setMayWait(bOptions, userId)
+ .setActivityOptions(bOptions)
+ .setMayWait(userId)
.execute();
}
.setCallingUid(callingUid)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
- .setMayWait(bOptions, userId)
+ .setActivityOptions(bOptions)
+ .setMayWait(userId)
.execute();
}
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();
return mActivityStartController.obtainStarter(intent, "startRecentsActivity")
.setCallingUid(recentsUid)
.setCallingPackage(recentsPackage)
- .setMayWait(activityOptions, userId)
+ .setActivityOptions(safeOptions)
+ .setMayWait(userId)
.execute();
}
} finally {
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);
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;
}
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);
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;
}
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<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
@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");
}
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();
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();
} finally {
Binder.restoreCallingIdentity(origId);
}
- ActivityOptions.abort(options);
+ SafeActivityOptions.abort(options);
}
/**
@Override
public boolean convertToTranslucent(IBinder token, Bundle options) {
+ SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
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) {
synchronized (ActivityManagerService.this) {
return mActivityStartController.startActivitiesInPackage(packageUid, packageName,
- intents, resolvedTypes, /*resultTo*/ null, bOptions, userId);
+ intents, resolvedTypes, null /* resultTo */,
+ SafeActivityOptions.fromBundle(bOptions), userId);
}
}
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) {
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;
}
}
}
- 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="
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();
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,
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);
}
}
- 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");
}
"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])
.setCallingPackage(callingPackage)
.setRealCallingPid(realCallingPid)
.setRealCallingUid(realCallingUid)
- .setActivityOptions(options)
+ .setActivityOptions(checkedOptions)
.setComponentSpecified(componentSpecified)
.setOutActivity(outActivity)
.execute();
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;
int realCallingPid;
int realCallingUid;
int startFlags;
- ActivityOptions activityOptions;
+ SafeActivityOptions activityOptions;
boolean ignoreTargetSecurity;
boolean componentSpecified;
ActivityRecord[] outActivity;
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;
reason = null;
profilerInfo = null;
globalConfig = null;
- waitOptions = null;
userId = 0;
waitResult = null;
mayWait = false;
reason = request.reason;
profilerInfo = request.profilerInfo;
globalConfig = request.globalConfig;
- waitOptions = request.waitOptions;
userId = request.userId;
waitResult = request.waitResult;
mayWait = request.mayWait;
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,
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)) {
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
// 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;
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
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;
inTask = mInterceptor.mInTask;
callingPid = mInterceptor.mCallingPid;
callingUid = mInterceptor.mCallingUid;
- options = mInterceptor.mActivityOptions;
+ checkedOptions = mInterceptor.mActivityOptions;
}
if (abort) {
}
// 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;
}
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;
}
}
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;
}
}
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.
*/
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");
// 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();
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;
}
}
}
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);
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;
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;
final Set<String> 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");
}
}
}
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;
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);
.setCaller(appThread)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
- .setMayWait(bOptions, callingUser)
+ .setActivityOptions(bOptions)
+ .setMayWait(callingUser)
.setInTask(tr)
.execute();
}
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;
final int requestCode;
final Intent requestIntent;
final String requestResolvedType;
- final Bundle options;
+ final SafeActivityOptions options;
Intent[] allIntents;
String[] allResolvedTypes;
final int flags;
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;
int res = 0;
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];
allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
owner.getActivityStartController().startActivitiesInPackage(uid,
key.packageName, allIntents, allResolvedTypes, resultTo,
- options, userId);
+ mergedOptions, userId);
} else {
owner.getActivityStartController().startActivityInPackage(uid,
- key.packageName, finalIntent, resolvedType, resultTo,
- resultWho, requestCode, 0, options, userId, null,
- "PendingIntentRecord");
+ 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);
--- /dev/null
+/*
+ * 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)";
+ }
+}
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);
Slog.d(TAG, "SecurityException while launching intent", e);
}
return false;
- } finally {
- injectRestoreCallingIdentity(ident);
}
}
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 {
.setResultTo(resultTo)
.setRequestCode(requestCode)
.setReason("testLaunchActivityPermissionDenied")
- .setActivityOptions(options)
+ .setActivityOptions(new SafeActivityOptions(options))
.execute();
verify(options, times(1)).abort();
}
--- /dev/null
+/*
+ * 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());
+ }
+}