* 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);
}
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;
}
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");
return mStackSupervisor.getTopVisibleActivities();
}
}
+
+ @Override
+ public void notifyDockedStackMinimizedChanged(boolean minimized) {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.setDockedStackMinimized(minimized);
+ }
+ }
}
private final class SleepTokenImpl extends SleepToken {
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;
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;
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;
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;
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.
*/
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) {
}
}
+ 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) {
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;
ActivityInfo mAInfo;
String mResolvedType;
TaskRecord mInTask;
+ ActivityOptions mActivityOptions;
ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
mService = service;
}
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;
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.
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);
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;
}
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;
inTask = mInterceptor.mInTask;
callingPid = mInterceptor.mCallingPid;
callingUid = mInterceptor.mCallingUid;
-
+ options = mInterceptor.mActivityOptions;
if (abort) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
}
}
+ 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,
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;
}
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);
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.
public String toShortString() {
return TAG;
}
-}
+}
\ No newline at end of file
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.
*/
}
}
break;
+ case NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED: {
+ mAmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");