Merge "AM: introduce ActivityOptions.setLockTaskMode()."
diff --git a/api/current.txt b/api/current.txt
index 3ca021e..6504271 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4023,6 +4023,7 @@
public class ActivityOptions {
method public android.graphics.Rect getLaunchBounds();
method public int getLaunchDisplayId();
+ method public boolean getLockTaskMode();
method public static android.app.ActivityOptions makeBasic();
method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -4035,6 +4036,7 @@
method public android.app.ActivityOptions setAppVerificationBundle(android.os.Bundle);
method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
method public android.app.ActivityOptions setLaunchDisplayId(int);
+ method public android.app.ActivityOptions setLockTaskMode(boolean);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
diff --git a/api/system-current.txt b/api/system-current.txt
index 5c3e1c9..0d1e8f05 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4185,6 +4185,7 @@
public class ActivityOptions {
method public android.graphics.Rect getLaunchBounds();
method public int getLaunchDisplayId();
+ method public boolean getLockTaskMode();
method public static android.app.ActivityOptions makeBasic();
method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -4197,6 +4198,7 @@
method public android.app.ActivityOptions setAppVerificationBundle(android.os.Bundle);
method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
method public android.app.ActivityOptions setLaunchDisplayId(int);
+ method public android.app.ActivityOptions setLockTaskMode(boolean);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
diff --git a/api/test-current.txt b/api/test-current.txt
index 132a787..f55d9db 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4043,6 +4043,7 @@
public class ActivityOptions {
method public android.graphics.Rect getLaunchBounds();
method public int getLaunchDisplayId();
+ method public boolean getLockTaskMode();
method public static android.app.ActivityOptions makeBasic();
method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -4058,6 +4059,7 @@
method public android.app.ActivityOptions setLaunchDisplayId(int);
method public void setLaunchTaskId(int);
method public void setLaunchWindowingMode(int);
+ method public android.app.ActivityOptions setLockTaskMode(boolean);
method public void setTaskOverlay(boolean, boolean);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index f14831d..b62e4c2 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -158,6 +159,12 @@
private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
/**
+ * Whether the activity should be launched into LockTask mode.
+ * @see #setLockTaskMode(boolean)
+ */
+ private static final String KEY_LOCK_TASK_MODE = "android:activity.lockTaskMode";
+
+ /**
* The display id the activity should be launched into.
* @see #setLaunchDisplayId(int)
* @hide
@@ -278,6 +285,7 @@
private int mResultCode;
private int mExitCoordinatorIndex;
private PendingIntent mUsageTimeReport;
+ private boolean mLockTaskMode = false;
private int mLaunchDisplayId = INVALID_DISPLAY;
@WindowConfiguration.WindowingMode
private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED;
@@ -869,6 +877,7 @@
mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
break;
}
+ mLockTaskMode = opts.getBoolean(KEY_LOCK_TASK_MODE, false);
mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY);
mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
@@ -1055,6 +1064,37 @@
}
/**
+ * Gets whether the activity is to be launched into LockTask mode.
+ * @return {@code true} if the activity is to be launched into LockTask mode.
+ * @see Activity#startLockTask()
+ * @see android.app.admin.DevicePolicyManager#setLockTaskPackages(ComponentName, String[])
+ */
+ public boolean getLockTaskMode() {
+ return mLockTaskMode;
+ }
+
+ /**
+ * Sets whether the activity is to be launched into LockTask mode.
+ *
+ * Use this option to start an activity in LockTask mode. Note that only apps permitted by
+ * {@link android.app.admin.DevicePolicyManager} can run in LockTask mode. Therefore, if
+ * {@link android.app.admin.DevicePolicyManager#isLockTaskPermitted(String)} returns
+ * {@code false} for the package of the target activity, a {@link SecurityException} will be
+ * thrown during {@link Context#startActivity(Intent, Bundle)}.
+ *
+ * Defaults to {@code false} if not set.
+ *
+ * @param lockTaskMode {@code true} if the activity is to be launched into LockTask mode.
+ * @return {@code this} {@link ActivityOptions} instance.
+ * @see Activity#startLockTask()
+ * @see android.app.admin.DevicePolicyManager#setLockTaskPackages(ComponentName, String[])
+ */
+ public ActivityOptions setLockTaskMode(boolean lockTaskMode) {
+ mLockTaskMode = lockTaskMode;
+ return this;
+ }
+
+ /**
* Gets the id of the display where activity should be launched.
* @return The id of the display where activity should be launched,
* {@link android.view.Display#INVALID_DISPLAY} if not set.
@@ -1247,6 +1287,7 @@
mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
break;
}
+ mLockTaskMode = otherOptions.mLockTaskMode;
mAnimSpecs = otherOptions.mAnimSpecs;
mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
mSpecsFuture = otherOptions.mSpecsFuture;
@@ -1321,6 +1362,7 @@
b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
break;
}
+ b.putBoolean(KEY_LOCK_TASK_MODE, mLockTaskMode);
b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId);
b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
b.putInt(KEY_LAUNCH_ACTIVITY_TYPE, mLaunchActivityType);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index f544d37..790c9a3 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -58,6 +58,10 @@
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS;
import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
@@ -282,6 +286,7 @@
int configChangeFlags; // which config values have changed
private boolean keysPaused; // has key dispatching been paused for it?
int launchMode; // the launch mode activity attribute.
+ int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override
boolean visible; // does this activity's window need to be shown?
boolean visibleIgnoringKeyguard; // is this activity visible, ignoring the fact that Keyguard
// might hide this activity?
@@ -824,23 +829,6 @@
hasBeenLaunched = false;
mStackSupervisor = supervisor;
- mRotationAnimationHint = aInfo.rotationAnimation;
-
- if (options != null) {
- pendingOptions = options;
- mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind();
-
- final int rotationAnimation = pendingOptions.getRotationAnimationHint();
- // Only override manifest supplied option if set.
- if (rotationAnimation >= 0) {
- mRotationAnimationHint = rotationAnimation;
- }
- PendingIntent usageReport = pendingOptions.getUsageTimeReport();
- if (usageReport != null) {
- appTimeTracker = new AppTimeTracker(usageReport);
- }
- }
-
// This starts out true, since the initial state of an activity is that we have everything,
// and we shouldn't never consider it lacking in state to be removed if it dies.
haveState = true;
@@ -907,6 +895,32 @@
mShowWhenLocked = (aInfo.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
mTurnScreenOn = (aInfo.flags & FLAG_TURN_SCREEN_ON) != 0;
+
+ mRotationAnimationHint = aInfo.rotationAnimation;
+ lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
+ if (appInfo.isPrivilegedApp() && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
+ || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
+ lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+ }
+
+ if (options != null) {
+ pendingOptions = options;
+ mLaunchTaskBehind = options.getLaunchTaskBehind();
+
+ final int rotationAnimation = pendingOptions.getRotationAnimationHint();
+ // Only override manifest supplied option if set.
+ if (rotationAnimation >= 0) {
+ mRotationAnimationHint = rotationAnimation;
+ }
+ final PendingIntent usageReport = pendingOptions.getUsageTimeReport();
+ if (usageReport != null) {
+ appTimeTracker = new AppTimeTracker(usageReport);
+ }
+ final boolean useLockTask = pendingOptions.getLockTaskMode();
+ if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
+ lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+ }
+ }
}
AppWindowContainerController getWindowContainerController() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 2de6055..333d759 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1611,6 +1611,16 @@
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);
+ }
}
return true;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 7817f1a..e4a5980 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -43,7 +43,6 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -165,7 +164,6 @@
private static final String ATTR_CALLING_PACKAGE = "calling_package";
private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supports_picture_in_picture";
private static final String ATTR_RESIZE_MODE = "resize_mode";
- private static final String ATTR_PRIVILEGED = "privileged";
private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
private static final String ATTR_MIN_WIDTH = "min_width";
private static final String ATTR_MIN_HEIGHT = "min_height";
@@ -235,10 +233,6 @@
// of the root activity.
boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize
// changes on a temporary basis.
- private int mLockTaskMode; // Which tasklock mode to launch this task in. One of
- // ActivityManager.LOCK_TASK_LAUNCH_MODE_*
- private boolean mPrivileged; // The root activity application of this task holds
- // privileged permissions.
/** Can't be put in lockTask mode. */
final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
@@ -388,9 +382,8 @@
long lastTimeMoved, boolean neverRelinquishIdentity,
TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
- int resizeMode, boolean supportsPictureInPicture, boolean privileged,
- boolean _realActivitySuspended, boolean userSetupComplete, int minWidth,
- int minHeight) {
+ int resizeMode, boolean supportsPictureInPicture, boolean _realActivitySuspended,
+ boolean userSetupComplete, int minWidth, int minHeight) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
@@ -426,7 +419,6 @@
mCallingPackage = callingPackage;
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
- mPrivileged = privileged;
mMinWidth = minWidth;
mMinHeight = minHeight;
mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
@@ -802,6 +794,7 @@
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
setIntent(r.intent, r.info);
+ setLockTaskAuth(r);
}
/** Sets the original intent, _without_ updating the calling uid or package. */
@@ -885,14 +878,6 @@
}
mResizeMode = info.resizeMode;
mSupportsPictureInPicture = info.supportsPictureInPicture();
- mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
- mLockTaskMode = info.lockTaskLaunchMode;
- if (!mPrivileged && (mLockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
- || mLockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
- // Non-priv apps are not allowed to use always or never, fall back to default
- mLockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
- }
- setLockTaskAuth();
}
/** Sets the original minimal width and height. */
@@ -1429,8 +1414,17 @@
}
void setLockTaskAuth() {
+ setLockTaskAuth(getRootActivity());
+ }
+
+ private void setLockTaskAuth(@Nullable ActivityRecord r) {
+ if (r == null) {
+ mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
+ return;
+ }
+
final String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
- switch (mLockTaskMode) {
+ switch (r.lockTaskLaunchMode) {
case LOCK_TASK_LAUNCH_MODE_DEFAULT:
mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg)
? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
@@ -1671,7 +1665,6 @@
out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
String.valueOf(mSupportsPictureInPicture));
- out.attribute(null, ATTR_PRIVILEGED, String.valueOf(mPrivileged));
if (mLastNonFullscreenBounds != null) {
out.attribute(
null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
@@ -1739,7 +1732,6 @@
String callingPackage = "";
int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
boolean supportsPictureInPicture = false;
- boolean privileged = false;
Rect bounds = null;
int minWidth = INVALID_MIN_SIZE;
int minHeight = INVALID_MIN_SIZE;
@@ -1801,8 +1793,6 @@
resizeMode = Integer.parseInt(attrValue);
} else if (ATTR_SUPPORTS_PICTURE_IN_PICTURE.equals(attrName)) {
supportsPictureInPicture = Boolean.parseBoolean(attrValue);
- } else if (ATTR_PRIVILEGED.equals(attrName)) {
- privileged = Boolean.parseBoolean(attrValue);
} else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
bounds = Rect.unflattenFromString(attrValue);
} else if (ATTR_MIN_WIDTH.equals(attrName)) {
@@ -1889,8 +1879,8 @@
autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
- callingPackage, resizeMode, supportsPictureInPicture, privileged,
- realActivitySuspended, userSetupComplete, minWidth, minHeight);
+ callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
+ userSetupComplete, minWidth, minHeight);
task.updateOverrideConfiguration(bounds);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {