From 2bf28320474702c979a06b34ca2c83daacc30c93 Mon Sep 17 00:00:00 2001 From: Charles He Date: Thu, 12 Oct 2017 22:24:49 +0100 Subject: [PATCH] AM: introduce ActivityOptions.setLockTaskMode(). This new API allows any activity to be launched into LockTask mode, as if the package of the activity has declared "lockTaskMode = if_whitelisted" in its manifest. The lockTaskLaunchMode logic is also moved from TaskRecord to ActivityRecord, in order to accommodate the new path from ActivityOptions. Bug: 66124180 Test: cts-tradefed run cts-dev --module DevicePolicyManager -t com.android.cts.devicepolicy.DeviceOwnerTest#testLockTask_deviceOwnerUser Test: bit FrameworksServicesTests:com.android.server.am.LockTaskControllerTest Test: manual, with TestDPC's kiosk mode Change-Id: I786bf194ed20a212bbad1f3cdb9394cc77aa4d77 --- api/current.txt | 2 + api/system-current.txt | 2 + api/test-current.txt | 2 + core/java/android/app/ActivityOptions.java | 42 ++++++++++++++++ .../com/android/server/am/ActivityRecord.java | 48 ++++++++++++------- .../server/am/ActivityStackSupervisor.java | 10 ++++ .../com/android/server/am/TaskRecord.java | 40 ++++++---------- 7 files changed, 104 insertions(+), 42 deletions(-) diff --git a/api/current.txt b/api/current.txt index 93cc84362fd50..b5e56b439f6b4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4023,6 +4023,7 @@ package android.app { 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 @@ package android.app { 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 9767e441453bd..6b3d0e3c7b506 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4185,6 +4185,7 @@ package android.app { 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 @@ package android.app { 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 702ec3bacf057..e72c7883d34b9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4043,6 +4043,7 @@ package android.app { 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 @@ package android.app { 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 f14831d1c658e..b62e4c2d0028b 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -23,6 +23,7 @@ import static android.view.Display.INVALID_DISPLAY; import android.annotation.Nullable; import android.annotation.TestApi; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; @@ -157,6 +158,12 @@ public class ActivityOptions { */ 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) @@ -278,6 +285,7 @@ public class ActivityOptions { 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 @@ public class ActivityOptions { 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); @@ -1054,6 +1063,37 @@ public class ActivityOptions { } } + /** + * 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, @@ -1247,6 +1287,7 @@ public class ActivityOptions { mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex; break; } + mLockTaskMode = otherOptions.mLockTaskMode; mAnimSpecs = otherOptions.mAnimSpecs; mAnimationFinishedListener = otherOptions.mAnimationFinishedListener; mSpecsFuture = otherOptions.mSpecsFuture; @@ -1321,6 +1362,7 @@ public class ActivityOptions { 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 f544d3771aba5..790c9a3cf2598 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_MULTIPLE; 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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 5221afd74fa9b..2ed79600c72a7 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 7817f1a8a30af..e4a5980b913e7 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_FORCE_RESIZEABLE; 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi // 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi mCallingPackage = callingPackage; mResizeMode = resizeMode; mSupportsPictureInPicture = supportsPictureInPicture; - mPrivileged = privileged; mMinWidth = minWidth; mMinHeight = minHeight; mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity); @@ -802,6 +794,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi 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 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi 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) {