diff options
-rw-r--r-- | cmds/am/src/com/android/commands/am/Am.java | 21 | ||||
-rw-r--r-- | core/java/android/app/Activity.java | 16 | ||||
-rw-r--r-- | core/java/android/app/ActivityManager.java | 38 | ||||
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 79 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 16 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 93 | ||||
-rwxr-xr-x | services/core/java/com/android/server/am/ActivityStack.java | 45 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ActivityStackSupervisor.java | 71 |
8 files changed, 339 insertions, 40 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 01e7615..92cb52c 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -114,6 +114,8 @@ public class Am extends BaseCommand { " am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" + " am stack list\n" + " am stack info <STACK_ID>\n" + + " am lock-task <TASK_ID>\n" + + " am lock-task stop\n" + "\n" + "am start: start an Activity. Options are:\n" + " -D: enable debugging\n" + @@ -218,6 +220,8 @@ public class Am extends BaseCommand { "\n" + "am stack info: display the information about activity stack <STACK_ID>.\n" + "\n" + + "am lock-task: bring <TASK_ID> to the front and don't allow other tasks to run\n" + + "\n" + "<INTENT> specifications include these flags and arguments:\n" + " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + @@ -309,6 +313,8 @@ public class Am extends BaseCommand { runStopUser(); } else if (op.equals("stack")) { runStack(); + } else if (op.equals("lock-task")) { + runLockTask(); } else { showError("Error: unknown command '" + op + "'"); } @@ -1641,4 +1647,19 @@ public class Am extends BaseCommand { } catch (RemoteException e) { } } + + private void runLockTask() throws Exception { + String taskIdStr = nextArgRequired(); + try { + if (taskIdStr.equals("stop")) { + mAm.stopLockTaskMode(); + } else { + int taskId = Integer.valueOf(taskIdStr); + mAm.startLockTaskMode(taskId); + } + System.err.println("Activity manager is " + (mAm.isInLockTaskMode() ? "" : "not ") + + "in lockTaskMode"); + } catch (RemoteException e) { + } + } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 287c463..606d803 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5606,6 +5606,22 @@ public class Activity extends ContextThemeWrapper } } + /** @hide */ + public void startLockTask() { + try { + ActivityManagerNative.getDefault().startLockTaskMode(mToken); + } catch (RemoteException e) { + } + } + + /** @hide */ + public void stopLockTask() { + try { + ActivityManagerNative.getDefault().stopLockTaskMode(); + } catch (RemoteException e) { + } + } + /** * Interface for informing a translucent {@link Activity} once all visible activities below it * have completed drawing. This is necessary only after an {@link Activity} has been made diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 7f7616f..a2183e6 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -155,6 +155,13 @@ public class ActivityManager { public static final int START_SWITCHES_CANCELED = 4; /** + * Result for IActivityManaqer.startActivity: a new activity was attempted to be started + * while in Lock Task Mode. + * @hide + */ + public static final int START_RETURN_LOCK_TASK_MODE_VIOLATION = 5; + + /** * Flag for IActivityManaqer.startActivity: do special start mode where * a new activity is launched only if it is needed. * @hide @@ -2232,4 +2239,35 @@ public class ActivityManager { e.printStackTrace(pw); } } + + /** + * @hide + */ + public void startLockTaskMode(int taskId) { + try { + ActivityManagerNative.getDefault().startLockTaskMode(taskId); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public void stopLockTaskMode() { + try { + ActivityManagerNative.getDefault().stopLockTaskMode(); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public boolean isInLockTaskMode() { + try { + return ActivityManagerNative.getDefault().isInLockTaskMode(); + } catch (RemoteException e) { + return false; + } + } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index c7c81dd..373a8a3 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2097,6 +2097,37 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeStrongBinder(homeActivityToken); return true; } + + case START_LOCK_TASK_BY_TASK_ID_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final int taskId = data.readInt(); + startLockTaskMode(taskId); + reply.writeNoException(); + return true; + } + + case START_LOCK_TASK_BY_TOKEN_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + startLockTaskMode(token); + reply.writeNoException(); + return true; + } + + case STOP_LOCK_TASK_MODE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + stopLockTaskMode(); + reply.writeNoException(); + return true; + } + + case IS_IN_LOCK_TASK_MODE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final boolean isInLockTaskMode = isInLockTaskMode(); + reply.writeNoException(); + reply.writeInt(isInLockTaskMode ? 1 : 0); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -4820,5 +4851,53 @@ class ActivityManagerProxy implements IActivityManager return res; } + @Override + public void startLockTaskMode(int taskId) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(taskId); + mRemote.transact(START_LOCK_TASK_BY_TASK_ID_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + @Override + public void startLockTaskMode(IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(START_LOCK_TASK_BY_TOKEN_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + @Override + public void stopLockTaskMode() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(STOP_LOCK_TASK_MODE_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + @Override + public boolean isInLockTaskMode() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(IS_IN_LOCK_TASK_MODE_TRANSACTION, data, reply, 0); + reply.readException(); + boolean isInLockTaskMode = reply.readInt() == 1; + data.recycle(); + reply.recycle(); + return isInLockTaskMode; + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index f2cabf4..cb06a42 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -424,6 +424,18 @@ public interface IActivityManager extends IInterface { public IBinder getHomeActivityToken() throws RemoteException; + /** @hide */ + public void startLockTaskMode(int taskId) throws RemoteException; + + /** @hide */ + public void startLockTaskMode(IBinder token) throws RemoteException; + + /** @hide */ + public void stopLockTaskMode() throws RemoteException; + + /** @hide */ + public boolean isInLockTaskMode() throws RemoteException; + /* * Private non-Binder interfaces */ @@ -719,4 +731,8 @@ public interface IActivityManager extends IInterface { int GET_TAG_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+210; int START_USER_IN_BACKGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+211; int IS_IN_HOME_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+212; + int START_LOCK_TASK_BY_TASK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+213; + int START_LOCK_TASK_BY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+214; + int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215; + int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 128f636..cd9c920 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7047,11 +7047,11 @@ public final class ActivityManagerService extends ActivityManagerNative * TODO: Add mController hook */ @Override - public void moveTaskToFront(int task, int flags, Bundle options) { + public void moveTaskToFront(int taskId, int flags, Bundle options) { enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()"); - if (DEBUG_STACK) Slog.d(TAG, "moveTaskToFront: moving task=" + task); + if (DEBUG_STACK) Slog.d(TAG, "moveTaskToFront: moving taskId=" + taskId); synchronized(this) { if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), Binder.getCallingUid(), "Task to front")) { @@ -7060,6 +7060,14 @@ public final class ActivityManagerService extends ActivityManagerNative } final long origId = Binder.clearCallingIdentity(); try { + final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + if (task == null) { + return; + } + if (mStackSupervisor.isLockTaskModeViolation(task)) { + Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode"); + return; + } mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options); } finally { Binder.restoreCallingIdentity(origId); @@ -7269,6 +7277,85 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private boolean isLockTaskAuthorized(ComponentName name) { +// enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, +// "startLockTaskMode()"); +// DevicePolicyManager dpm = (DevicePolicyManager) +// mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); +// return dpm != null && dpm.isLockTaskPermitted(name); + return true; + } + + private void startLockTaskMode(TaskRecord task) { + if (!isLockTaskAuthorized(task.intent.getComponent())) { + return; + } + long ident = Binder.clearCallingIdentity(); + try { + synchronized (this) { + // Since we lost lock on task, make sure it is still there. + task = mStackSupervisor.anyTaskForIdLocked(task.taskId); + if (task != null) { + mStackSupervisor.setLockTaskModeLocked(task); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void startLockTaskMode(int taskId) { + long ident = Binder.clearCallingIdentity(); + try { + final TaskRecord task; + synchronized (this) { + task = mStackSupervisor.anyTaskForIdLocked(taskId); + } + if (task != null) { + startLockTaskMode(task); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void startLockTaskMode(IBinder token) { + long ident = Binder.clearCallingIdentity(); + try { + final TaskRecord task; + synchronized (this) { + final ActivityRecord r = ActivityRecord.forToken(token); + if (r == null) { + return; + } + task = r.task; + } + if (task != null) { + startLockTaskMode(task); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void stopLockTaskMode() { +// enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, +// "stopLockTaskMode()"); + synchronized (this) { + mStackSupervisor.setLockTaskModeLocked(null); + } + } + + @Override + public boolean isInLockTaskMode() { + synchronized (this) { + return mStackSupervisor.isInLockTaskMode(); + } + } + // ========================================================= // THUMBNAILS // ========================================================= @@ -16186,6 +16273,8 @@ public final class ActivityManagerService extends ActivityManagerNative return true; } + mStackSupervisor.setLockTaskModeLocked(null); + final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId); if (userInfo == null) { Slog.w(TAG, "No user info for user #" + userId); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 087ad83c..34cc22a 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -36,8 +36,6 @@ import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES; import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; -import android.os.Trace; -import android.util.Log; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService.ItemMatcher; @@ -2478,13 +2476,14 @@ final class ActivityStack { } r.makeFinishing(); + final TaskRecord task = r.task; EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.userId, System.identityHashCode(r), - r.task.taskId, r.shortComponentName, reason); - final ArrayList<ActivityRecord> activities = r.task.mActivities; + task.taskId, r.shortComponentName, reason); + final ArrayList<ActivityRecord> activities = task.mActivities; final int index = activities.indexOf(r); if (index < (activities.size() - 1)) { - r.task.setFrontOfTask(); + task.setFrontOfTask(); if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { // If the caller asked that this activity (and all above it) // be cleared when the task is reset, don't lose that information, @@ -2524,6 +2523,9 @@ final class ActivityStack { startPausingLocked(false, false); } + if (endTask) { + mStackSupervisor.endLockTaskModeIfTaskEnding(task); + } } else if (r.state != ActivityState.PAUSING) { // If the activity is PAUSING, we will complete the finish once // it is done pausing; else we can just directly finish it here. @@ -3088,23 +3090,6 @@ final class ActivityStack { } } - final boolean findTaskToMoveToFrontLocked(int taskId, int flags, Bundle options) { - final TaskRecord task = taskForIdLocked(taskId); - if (task != null) { - if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { - mStackSupervisor.mUserLeaving = true; - } - if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) { - // Caller wants the home activity moved with it. To accomplish this, - // we'll just indicate that this task returns to the home task. - task.mOnTopOfHome = true; - } - moveTaskToFrontLocked(task, null, options); - return true; - } - return false; - } - final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) { if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr); @@ -3162,7 +3147,15 @@ final class ActivityStack { * @return Returns true if the move completed, false if not. */ final boolean moveTaskToBackLocked(int taskId, ActivityRecord reason) { - Slog.i(TAG, "moveTaskToBack: " + taskId); + final TaskRecord tr = taskForIdLocked(taskId); + if (tr == null) { + Slog.i(TAG, "moveTaskToBack: bad taskId=" + taskId); + return false; + } + + Slog.i(TAG, "moveTaskToBack: " + tr); + + mStackSupervisor.endLockTaskModeIfTaskEnding(tr); // If we have a watcher, preflight the move before committing to it. First check // for *other* available tasks, but if none are available, then try again allowing the @@ -3190,11 +3183,6 @@ final class ActivityStack { if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to back transition: task=" + taskId); - final TaskRecord tr = taskForIdLocked(taskId); - if (tr == null) { - return false; - } - mTaskHistory.remove(tr); mTaskHistory.add(0, tr); @@ -3678,6 +3666,7 @@ final class ActivityStack { } void removeTask(TaskRecord task) { + mStackSupervisor.endLockTaskModeIfTaskEnding(task); mWindowManager.removeTask(task.taskId); final ActivityRecord r = mResumedActivity; if (r != null && r.task == task) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index b2cf846..9315648 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -231,6 +231,10 @@ public final class ActivityStackSupervisor implements DisplayListener { InputManagerInternal mInputManagerInternal; + /** If non-null then the task specified remains in front and no other tasks may be started + * until the task exits or #stopLockTaskMode() is called. */ + private TaskRecord mLockTaskModeTask; + public ActivityStackSupervisor(ActivityManagerService service) { mService = service; PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE); @@ -1505,6 +1509,10 @@ public final class ActivityStackSupervisor implements DisplayListener { ? findTaskLocked(r) : findActivityLocked(intent, r.info); if (intentActivity != null) { + if (isLockTaskModeViolation(intentActivity.task)) { + Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode"); + return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; + } if (r.task == null) { r.task = intentActivity.task; } @@ -1715,6 +1723,10 @@ public final class ActivityStackSupervisor implements DisplayListener { // Should this be considered a new task? if (r.resultTo == null && !addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + if (isLockTaskModeViolation(reuseTask)) { + Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r); + return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; + } targetStack = adjustStackFocus(r); targetStack.moveToFront(); if (reuseTask == null) { @@ -1739,6 +1751,10 @@ public final class ActivityStackSupervisor implements DisplayListener { } } else if (sourceRecord != null) { TaskRecord sourceTask = sourceRecord.task; + if (isLockTaskModeViolation(sourceTask)) { + Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r); + return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; + } targetStack = sourceTask.stack; targetStack.moveToFront(); if (!addingToTask && @@ -1782,6 +1798,10 @@ public final class ActivityStackSupervisor implements DisplayListener { // An existing activity is starting this new activity, so we want // to keep the new one in the same task as the one that is starting // it. + if (isLockTaskModeViolation(sourceTask)) { + Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r); + return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; + } r.setTask(sourceTask, sourceRecord.thumbHolder, false); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in existing task " + r.task + " from source " + sourceRecord); @@ -2098,17 +2118,18 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - void findTaskToMoveToFrontLocked(int taskId, int flags, Bundle options) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; - for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { - if (stacks.get(stackNdx).findTaskToMoveToFrontLocked(taskId, flags, options)) { - if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack=" - + stacks.get(stackNdx)); - return; - } - } + void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options) { + if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { + mUserLeaving = true; } + if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) { + // Caller wants the home activity moved with it. To accomplish this, + // we'll just indicate that this task returns to the home task. + task.mOnTopOfHome = true; + } + task.stack.moveTaskToFrontLocked(task, null, options); + if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack=" + + task.stack); } ActivityStack getStack(int stackId) { @@ -2288,6 +2309,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } } checkReadyForSleepLocked(); + setLockTaskModeLocked(null); } boolean shutdownLocked(int timeout) { @@ -2872,6 +2894,35 @@ public final class ActivityStackSupervisor implements DisplayListener { return list; } + void setLockTaskModeLocked(TaskRecord task) { + if (task == null) { + // Take out of lock task mode. + mLockTaskModeTask = null; + return; + } + if (isLockTaskModeViolation(task)) { + Slog.e(TAG, "setLockTaskMode: Attempt to start a second Lock Task Mode task."); + return; + } + mLockTaskModeTask = task; + findTaskToMoveToFrontLocked(task, 0, null); + resumeTopActivitiesLocked(); + } + + boolean isLockTaskModeViolation(TaskRecord task) { + return mLockTaskModeTask != null && mLockTaskModeTask != task; + } + + void endLockTaskModeIfTaskEnding(TaskRecord task) { + if (mLockTaskModeTask != null && mLockTaskModeTask == task) { + mLockTaskModeTask = null; + } + } + + boolean isInLockTaskMode() { + return mLockTaskModeTask != null; + } + private final class ActivityStackSupervisorHandler extends Handler { public ActivityStackSupervisorHandler(Looper looper) { |