OSDN Git Service

Show work challenge in if user in docked stack is locked
authorTony Mak <tonymak@google.com>
Mon, 18 Apr 2016 14:17:41 +0000 (15:17 +0100)
committerTony Mak <tonymak@google.com>
Mon, 18 Apr 2016 15:35:36 +0000 (15:35 +0000)
Register docked stack listener in ActivityManagerService.
If the docked stack is leaving minimized state, check whether the user
of the docked stack is locked. If yes, show credential confirmation.
Also, we now show work challenge in home task.

And we now scan the entire top task to handle the case the work app is
somewhere in the middle of the task. (eg: open personal camera in work app)

Bug: 27565539
Bug: 28094505

Change-Id: Iaf0738f43ae916a535b17949ec0f322bbfb194dc

core/java/android/app/ActivityManagerInternal.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/ActivityStackSupervisor.java
services/core/java/com/android/server/am/ActivityStartInterceptor.java
services/core/java/com/android/server/am/ActivityStarter.java
services/core/java/com/android/server/wm/DockedStackDividerController.java
services/core/java/com/android/server/wm/WindowManagerService.java

index 374c4f6..ee4c2f7 100644 (file)
@@ -133,4 +133,10 @@ public abstract class ActivityManagerInternal {
      * the focused activity.
      */
     public abstract List<IBinder> getTopVisibleActivities();
+
+    /**
+     * Callback for window manager to let activity manager know that docked stack changes its
+     * minimized state.
+     */
+    public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
 }
index 40430b4..f126e4c 100644 (file)
@@ -97,6 +97,7 @@ import android.app.IUiAutomationConnection;
 import android.app.IUidObserver;
 import android.app.IUserSwitchObserver;
 import android.app.Instrumentation;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -11471,20 +11472,13 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
 
         synchronized (this) {
-            if (mStackSupervisor.isFocusedUserLockedProfile()) {
+            if (mStackSupervisor.isUserLockedProfile(userId)) {
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     final int currentUserId = mUserController.getCurrentUserIdLocked();
-                    // Get the focused task before launching launcher.
-
                     if (mUserController.isLockScreenDisabled(currentUserId)) {
-
                         // If there is no device lock, we will show the profile's credential page.
-                        // startActivityFromRecentsInner is intercepted and will forward user to it.
-                        if (mFocusedActivity != null) {
-                            mStackSupervisor.startActivityFromRecentsInner(
-                                    mFocusedActivity.task.taskId, null);
-                        }
+                        mActivityStarter.showConfirmDeviceCredential(userId);
                     } else {
                         // Showing launcher to avoid user entering credential twice.
                         startHomeActivityLocked(currentUserId, "notifyLockedProfile");
@@ -21006,6 +21000,13 @@ public final class ActivityManagerService extends ActivityManagerNative
                 return mStackSupervisor.getTopVisibleActivities();
             }
         }
+
+        @Override
+        public void notifyDockedStackMinimizedChanged(boolean minimized) {
+            synchronized (ActivityManagerService.this) {
+                mStackSupervisor.setDockedStackMinimized(minimized);
+            }
+        }
     }
 
     private final class SleepTokenImpl extends SleepToken {
index 6e2cebb..7e1662e 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.Manifest;
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -29,6 +30,8 @@ import android.app.IActivityContainer;
 import android.app.IActivityContainerCallback;
 import android.app.IActivityManager;
 import android.app.IActivityManager.WaitResult;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.app.StatusBarManager;
@@ -37,6 +40,7 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -166,6 +170,7 @@ import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -433,6 +438,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
     private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
 
     /**
+     * Is dock currently minimized.
+     */
+    boolean mIsDockMinimized;
+
+    /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
      */
@@ -716,10 +726,44 @@ public final class ActivityStackSupervisor implements DisplayListener {
         return null;
     }
 
-    boolean isFocusedUserLockedProfile() {
-        final int userId = mFocusedStack.topRunningActivityLocked().userId;
-        return userId != UserHandle.myUserId()
-                && mService.mUserController.shouldConfirmCredentials(userId);
+    /**
+     * TODO: Handle freefom mode.
+     * @return true when credential confirmation is needed for the user and there is any
+     *         activity started by the user in any visible stack.
+     */
+    boolean isUserLockedProfile(@UserIdInt int userId) {
+        if (!mService.mUserController.shouldConfirmCredentials(userId)) {
+            return false;
+        }
+        final ActivityStack fullScreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+        final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
+        final ActivityStack[] activityStacks = new ActivityStack[] {fullScreenStack, dockedStack};
+        for (final ActivityStack activityStack : activityStacks) {
+            if (activityStack == null) {
+                continue;
+            }
+            if (activityStack.topRunningActivityLocked() == null) {
+                continue;
+            }
+            if (activityStack.getStackVisibilityLocked(null) == STACK_INVISIBLE) {
+                continue;
+            }
+            if (activityStack.isDockedStack() && mIsDockMinimized) {
+                continue;
+            }
+            final TaskRecord topTask = activityStack.topTask();
+            if (topTask == null) {
+                continue;
+            }
+            // To handle the case that work app is in the task but just is not the top one.
+            for (int i = topTask.mActivities.size() - 1; i >= 0; i--) {
+                final ActivityRecord activityRecord = topTask.mActivities.get(i);
+                if (activityRecord.userId == userId) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     void setNextTaskIdForUserLocked(int taskId, int userId) {
@@ -3590,6 +3634,22 @@ public final class ActivityStackSupervisor implements DisplayListener {
         }
     }
 
+    void setDockedStackMinimized(boolean minimized) {
+        mIsDockMinimized = minimized;
+        if (minimized) {
+            // Docked stack is not visible, no need to confirm credentials for its top activity.
+            return;
+        }
+        final ActivityStack dockedStack = getStack(StackId.DOCKED_STACK_ID);
+        if (dockedStack == null) {
+            return;
+        }
+        final ActivityRecord top = dockedStack.topRunningActivityLocked();
+        if (top != null && mService.mUserController.shouldConfirmCredentials(top.userId)) {
+            mService.mActivityStarter.showConfirmDeviceCredential(top.userId);
+        }
+    }
+
     private final class ActivityStackSupervisorHandler extends Handler {
 
         public ActivityStackSupervisorHandler(Looper looper) {
index 785dd47..566d8d9 100644 (file)
@@ -29,6 +29,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
+import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.IIntentSender;
@@ -76,6 +77,7 @@ class ActivityStartInterceptor {
     ActivityInfo mAInfo;
     String mResolvedType;
     TaskRecord mInTask;
+    ActivityOptions mActivityOptions;
 
     ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
         mService = service;
@@ -92,7 +94,7 @@ class ActivityStartInterceptor {
     }
 
     void intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
-            TaskRecord inTask, int callingPid, int callingUid) {
+            TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
         mUserManager = UserManager.get(mService.mContext);
         mIntent = intent;
         mCallingPid = callingPid;
@@ -101,6 +103,7 @@ class ActivityStartInterceptor {
         mAInfo = aInfo;
         mResolvedType = resolvedType;
         mInTask = inTask;
+        mActivityOptions = activityOptions;
         if (interceptSuspendPackageIfNeed()) {
             // Skip the rest of interceptions as the package is suspended by device admin so
             // no user action can undo this.
@@ -177,6 +180,12 @@ class ActivityStartInterceptor {
             mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId);
             mInTask = null;
         }
+        if (mActivityOptions == null) {
+            mActivityOptions = ActivityOptions.makeBasic();
+        }
+        // Showing credential confirmation activity in home task to avoid stopping multi-windowed
+        // mode after showing the full-screen credential confirmation activity.
+        mActivityOptions.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
 
         final UserInfo parent = mUserManager.getProfileParent(mUserId);
         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
index 4075230..9c93f2c 100644 (file)
@@ -82,9 +82,11 @@ import android.app.AppGlobals;
 import android.app.IActivityContainer;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
+import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.ProfilerInfo;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -380,7 +382,8 @@ class ActivityStarter {
         }
 
         mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
-        mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid);
+        mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid,
+                options);
         intent = mInterceptor.mIntent;
         rInfo = mInterceptor.mRInfo;
         aInfo = mInterceptor.mAInfo;
@@ -388,7 +391,7 @@ class ActivityStarter {
         inTask = mInterceptor.mInTask;
         callingPid = mInterceptor.mCallingPid;
         callingUid = mInterceptor.mCallingUid;
-
+        options = mInterceptor.mActivityOptions;
         if (abort) {
             if (resultRecord != null) {
                 resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
@@ -589,6 +592,53 @@ class ActivityStarter {
         }
     }
 
+    void showConfirmDeviceCredential(int userId) {
+        // First, retrieve the stack that we want to resume after credential is confirmed.
+        ActivityStack targetStack;
+        ActivityStack fullscreenStack =
+                mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+        if (fullscreenStack != null &&
+                fullscreenStack.getStackVisibilityLocked(null) != ActivityStack.STACK_INVISIBLE) {
+            // Single window case and the case that the docked stack is shown with fullscreen stack.
+            targetStack = fullscreenStack;
+        } else {
+            // The case that the docked stack is shown with recent.
+            targetStack = mSupervisor.getStack(HOME_STACK_ID);
+        }
+        if (targetStack == null) {
+            return;
+        }
+        final KeyguardManager km = (KeyguardManager) mService.mContext
+                .getSystemService(Context.KEYGUARD_SERVICE);
+        final Intent credential =
+                km.createConfirmDeviceCredentialIntent(null, null, userId);
+        credential.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+                Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
+        final ActivityRecord activityRecord = targetStack.topRunningActivityLocked();
+        if (activityRecord != null) {
+            final IIntentSender target = mService.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_ACTIVITY,
+                    activityRecord.launchedFromPackage,
+                    activityRecord.launchedFromUid,
+                    activityRecord.userId,
+                    null, null, 0,
+                    new Intent[] { activityRecord.intent },
+                    new String[] { activityRecord.resolvedType },
+                    PendingIntent.FLAG_CANCEL_CURRENT |
+                            PendingIntent.FLAG_ONE_SHOT |
+                            PendingIntent.FLAG_IMMUTABLE,
+                    null);
+            credential.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+            // Show confirm credentials activity.
+            mService.mContext.startActivityAsUser(credential, options.toBundle(),
+                    UserHandle.CURRENT);
+        }
+    }
+
+
     final int startActivityMayWait(IApplicationThread caller, int callingUid,
             String callingPackage, Intent intent, String resolvedType,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
index 3bd7a96..595a68d 100644 (file)
@@ -27,6 +27,7 @@ import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -276,6 +277,9 @@ public class DockedStackDividerController implements DimLayerUser {
     }
 
     void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
+        mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
+        mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
+                minimizedDock ? 1 : 0, 0).sendToTarget();
         final int size = mDockedStackListeners.beginBroadcast();
         for (int i = 0; i < size; ++i) {
             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
@@ -355,6 +359,12 @@ public class DockedStackDividerController implements DimLayerUser {
             return;
         }
 
+        // If the app that having visibility change is not the top visible one in the task,
+        // it does not affect whether the docked stack is minimized, ignore it.
+        if (task.getTopVisibleAppToken() == null || task.getTopVisibleAppToken() != wtoken) {
+            return;
+        }
+
         // If the stack is completely offscreen, this might just be an intermediate state when
         // docking a task/launching recents at the same time, but home doesn't actually get
         // visible after the state settles in.
@@ -644,4 +654,4 @@ public class DockedStackDividerController implements DimLayerUser {
     public String toShortString() {
         return TAG;
     }
-}
+}
\ No newline at end of file
index 2e0c187..d8497de 100644 (file)
@@ -7800,6 +7800,8 @@ public class WindowManagerService extends IWindowManager.Stub
         public static final int UPDATE_ANIMATION_SCALE = 51;
         public static final int WINDOW_REMOVE_TIMEOUT = 52;
 
+        public static final int NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED = 53;
+
         /**
          * Used to denote that an integer field in a message will not be used.
          */
@@ -8423,6 +8425,10 @@ public class WindowManagerService extends IWindowManager.Stub
                     }
                 }
                 break;
+                case NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED: {
+                    mAmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG_WM, "handleMessage: exit");