blob: 06e5ea20c2e073b4501ef5f76765f661f8207c08 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IActivityContainer;
import android.app.IActivityContainerCallback;
import android.app.IActivityManager;
import android.app.IActivityManager.WaitResult;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.StatusBarManager;
import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.VirtualDisplay;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Trace;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputEvent;
import android.view.Surface;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.TransferPipe;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.ActivityManager.RESIZE_MODE_FORCED;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.am.ActivityManagerService.NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG;
import static com.android.server.am.ActivityManagerService.NOTIFY_FORCED_RESIZABLE_MSG;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
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.REMOVE_TASK_MODE_MOVING;
import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import static com.android.server.am.ActivityStack.STACK_VISIBLE;
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;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
public final class ActivityStackSupervisor implements DisplayListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
private static final String TAG_VISIBLE_BEHIND = TAG + POSTFIX_VISIBLE_BEHIND;
/** How long we wait until giving up on the last activity telling us it is idle. */
static final int IDLE_TIMEOUT = 10 * 1000;
/** How long we can hold the sleep wake lock before giving up. */
static final int SLEEP_TIMEOUT = 5 * 1000;
// How long we can hold the launch wake lock before giving up.
static final int LAUNCH_TIMEOUT = 10 * 1000;
static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG;
static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1;
static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
static final int HANDLE_DISPLAY_ADDED = FIRST_SUPERVISOR_STACK_MSG + 5;
static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
static final int CONTAINER_CALLBACK_VISIBILITY = FIRST_SUPERVISOR_STACK_MSG + 8;
static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9;
static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
static final int SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
private static final String LOCK_TASK_TAG = "Lock-to-App";
// Used to indicate if an object (e.g. stack) that we are trying to get
// should be created if it doesn't exist already.
static final boolean CREATE_IF_NEEDED = true;
// Used to indicate that windows of activities should be preserved during the resize.
static final boolean PRESERVE_WINDOWS = true;
// Used to indicate if an object (e.g. task) should be moved/created
// at the top of its container (e.g. stack).
static final boolean ON_TOP = true;
// Used to indicate that an objects (e.g. task) removal from its container
// (e.g. stack) is due to it moving to another container.
static final boolean MOVING = true;
// Force the focus to change to the stack we are moving a task to..
static final boolean FORCE_FOCUS = true;
// Restore task from the saved recents if it can't be found in any live stack.
static final boolean RESTORE_FROM_RECENTS = true;
// Don't execute any calls to resume.
static final boolean DEFER_RESUME = true;
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
static {
ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
Manifest.permission.CAMERA);
ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
Manifest.permission.CAMERA);
ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
Manifest.permission.CALL_PHONE);
}
/** Action restriction: launching the activity is not restricted. */
private static final int ACTIVITY_RESTRICTION_NONE = 0;
/** Action restriction: launching the activity is restricted by a permission. */
private static final int ACTIVITY_RESTRICTION_PERMISSION = 1;
/** Action restriction: launching the activity is restricted by an app op. */
private static final int ACTIVITY_RESTRICTION_APPOP = 2;
// The height/width divide used when fitting a task within a bounds with method
// {@link #fitWithinBounds}.
// We always want the task to to be visible in the bounds without affecting its size when
// fitting. To make sure this is the case, we don't adjust the task left or top side pass
// the input bounds right or bottom side minus the width or height divided by this value.
private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
/** Status Bar Service **/
private IBinder mToken = new Binder();
private IStatusBarService mStatusBarService;
private IDevicePolicyManager mDevicePolicyManager;
// For debugging to make sure the caller when acquiring/releasing our
// wake lock is the system process.
static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
/** The number of distinct task ids that can be assigned to the tasks of a single user */
private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
final ActivityManagerService mService;
private RecentTasks mRecentTasks;
final ActivityStackSupervisorHandler mHandler;
/** Short cut */
WindowManagerService mWindowManager;
DisplayManager mDisplayManager;
/** Counter for next free stack ID to use for dynamic activity stacks. */
private int mNextFreeStackId = FIRST_DYNAMIC_STACK_ID;
/**
* Maps the task identifier that activities are currently being started in to the userId of the
* task. Each time a new task is created, the entry for the userId of the task is incremented
*/
private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);
/** The current user */
int mCurrentUser;
/** The stack containing the launcher app. Assumed to always be attached to
* Display.DEFAULT_DISPLAY. */
ActivityStack mHomeStack;
/** The stack currently receiving input or launching the next activity. */
ActivityStack mFocusedStack;
/** If this is the same as mFocusedStack then the activity on the top of the focused stack has
* been resumed. If stacks are changing position this will hold the old stack until the new
* stack becomes resumed after which it will be set to mFocusedStack. */
private ActivityStack mLastFocusedStack;
/** List of activities that are waiting for a new activity to become visible before completing
* whatever operation they are supposed to do. */
final ArrayList<ActivityRecord> mWaitingVisibleActivities = new ArrayList<>();
/** List of processes waiting to find out about the next visible activity. */
final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible = new ArrayList<>();
/** List of processes waiting to find out about the next launched activity. */
final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched = new ArrayList<>();
/** List of activities that are ready to be stopped, but waiting for the next activity to
* settle down before doing so. */
final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<>();
/** List of activities that are ready to be finished, but waiting for the previous activity to
* settle down before doing so. It contains ActivityRecord objects. */
final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<>();
/** List of activities that are in the process of going to sleep. */
final ArrayList<ActivityRecord> mGoingToSleepActivities = new ArrayList<>();
/** List of activities whose multi-window mode changed that we need to report to the
* application */
final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
/** List of activities whose picture-in-picture mode changed that we need to report to the
* application */
final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
/** Used on user changes */
final ArrayList<UserState> mStartingUsers = new ArrayList<>();
/** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
* is being brought in front of us. */
boolean mUserLeaving = false;
/** Set when we have taken too long waiting to go to sleep. */
boolean mSleepTimeout = false;
/**
* We don't want to allow the device to go to sleep while in the process
* of launching an activity. This is primarily to allow alarm intent
* receivers to launch an activity and get that to run before the device
* goes back to sleep.
*/
PowerManager.WakeLock mLaunchingActivity;
/**
* Set when the system is going to sleep, until we have
* successfully paused the current activity and released our wake lock.
* At that point the system is allowed to actually sleep.
*/
PowerManager.WakeLock mGoingToSleep;
/** Stack id of the front stack when user switched, indexed by userId. */
SparseIntArray mUserStackInFront = new SparseIntArray(2);
// TODO: Add listener for removal of references.
/** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<>();
/** Mapping from displayId to display current state */
private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
InputManagerInternal mInputManagerInternal;
/** The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
* may be finished until there is only one entry left. If this is empty the system is not
* in lockTask mode. */
ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
/** Store the current lock task mode. Possible values:
* {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
* {@link ActivityManager#LOCK_TASK_MODE_PINNED}
*/
private int mLockTaskModeState;
/**
* Notifies the user when entering/exiting lock-task.
*/
private LockTaskNotify mLockTaskNotify;
/** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
boolean inResumeTopActivity;
// temp. rects used during resize calculation so we don't need to create a new object each time.
private final Rect tempRect = new Rect();
private final Rect tempRect2 = new Rect();
private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>();
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
int mDefaultMinSizeOfResizeableTask = -1;
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
final ActivityMetricsLogger mActivityMetricsLogger;
private final ResizeDockedStackTimeout mResizeDockedStackTimeout;
static class FindTaskResult {
ActivityRecord r;
boolean matchedByRootAffinity;
}
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
/**
* Used to keep track whether app visibilities got changed since the last pause. Useful to
* determine whether to invoke the task stack change listener after pausing.
*/
boolean mAppVisibilitiesChangedSinceLastPause;
/**
* Set of tasks that are in resizing mode during an app transition to fill the "void".
*/
private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
/**
* If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked}
* will be ignored. Useful for the case where the caller is handling resizing of other stack and
* moving tasks around and doesn't want dock stack to be resized due to an automatic trigger
* like the docked stack going empty.
*/
private boolean mAllowDockedStackResize = true;
/**
* 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.
*/
static class PendingActivityLaunch {
final ActivityRecord r;
final ActivityRecord sourceRecord;
final int startFlags;
final ActivityStack stack;
final ProcessRecord callerApp;
PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
int _startFlags, ActivityStack _stack, ProcessRecord _callerApp) {
r = _r;
sourceRecord = _sourceRecord;
startFlags = _startFlags;
stack = _stack;
callerApp = _callerApp;
}
void sendErrorResult(String message) {
try {
if (callerApp.thread != null) {
callerApp.thread.scheduleCrash(message);
}
} catch (RemoteException e) {
Slog.e(TAG, "Exception scheduling crash of failed "
+ "activity launcher sourceRecord=" + sourceRecord, e);
}
}
}
public ActivityStackSupervisor(ActivityManagerService service) {
mService = service;
mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
mResizeDockedStackTimeout = new ResizeDockedStackTimeout(service, this, mHandler);
}
void setRecentTasks(RecentTasks recentTasks) {
mRecentTasks = recentTasks;
}
/**
* At the time when the constructor runs, the power manager has not yet been
* initialized. So we initialize our wakelocks afterwards.
*/
void initPowerManagement() {
PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*launch*");
mLaunchingActivity.setReferenceCounted(false);
}
// This function returns a IStatusBarService. The value is from ServiceManager.
// getService and is cached.
private IStatusBarService getStatusBarService() {
synchronized (mService) {
if (mStatusBarService == null) {
mStatusBarService = IStatusBarService.Stub.asInterface(
ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
if (mStatusBarService == null) {
Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
}
}
return mStatusBarService;
}
}
private IDevicePolicyManager getDevicePolicyManager() {
synchronized (mService) {
if (mDevicePolicyManager == null) {
mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
ServiceManager.checkService(Context.DEVICE_POLICY_SERVICE));
if (mDevicePolicyManager == null) {
Slog.w(TAG, "warning: no DEVICE_POLICY_SERVICE");
}
}
return mDevicePolicyManager;
}
}
void setWindowManager(WindowManagerService wm) {
synchronized (mService) {
mWindowManager = wm;
mDisplayManager =
(DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(this, null);
Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
final int displayId = displays[displayNdx].getDisplayId();
ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
if (activityDisplay.mDisplay == null) {
throw new IllegalStateException("Default Display does not exist");
}
mActivityDisplays.put(displayId, activityDisplay);
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
mHomeStack = mFocusedStack = mLastFocusedStack =
getStack(HOME_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
}
}
void notifyActivityDrawnForKeyguard() {
if (DEBUG_LOCKSCREEN) mService.logLockScreen("");
mWindowManager.notifyActivityDrawnForKeyguard();
}
ActivityStack getFocusedStack() {
return mFocusedStack;
}
ActivityStack getLastStack() {
return mLastFocusedStack;
}
boolean isFocusedStack(ActivityStack stack) {
if (stack == null) {
return false;
}
final ActivityRecord parent = stack.mActivityContainer.mParentActivity;
if (parent != null) {
stack = parent.task.stack;
}
return stack == mFocusedStack;
}
/** The top most stack. */
boolean isFrontStack(ActivityStack stack) {
if (stack == null) {
return false;
}
final ActivityRecord parent = stack.mActivityContainer.mParentActivity;
if (parent != null) {
stack = parent.task.stack;
}
return stack == mHomeStack.mStacks.get((mHomeStack.mStacks.size() - 1));
}
/** NOTE: Should only be called from {@link ActivityStack#moveToFront} */
void setFocusStackUnchecked(String reason, ActivityStack focusCandidate) {
if (!focusCandidate.isFocusable()) {
// The focus candidate isn't focusable. Move focus to the top stack that is focusable.
focusCandidate = focusCandidate.getNextFocusableStackLocked();
}
if (focusCandidate != mFocusedStack) {
mLastFocusedStack = mFocusedStack;
mFocusedStack = focusCandidate;
EventLogTags.writeAmFocusedStack(
mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
}
final ActivityRecord r = topRunningActivityLocked();
if (!mService.mDoingSetFocusedActivity && mService.mFocusedActivity != r) {
// The focus activity should always be the top activity in the focused stack.
// There will be chaos and anarchy if it isn't...
mService.setFocusedActivityLocked(r, reason + " setFocusStack");
}
if (mService.mBooting || !mService.mBooted) {
if (r != null && r.idle) {
checkFinishBootingLocked();
}
}
}
void moveHomeStackToFront(String reason) {
mHomeStack.moveToFront(reason);
}
/** Returns true if the focus activity was adjusted to the home stack top activity. */
boolean moveHomeStackTaskToTop(int homeStackTaskType, String reason) {
if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
mWindowManager.showRecentApps(false /* fromHome */);
return false;
}
mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
final ActivityRecord top = getHomeActivity();
if (top == null) {
return false;
}
mService.setFocusedActivityLocked(top, reason);
return true;
}
boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev, String reason) {
if (!mService.mBooting && !mService.mBooted) {
// Not ready yet!
return false;
}
if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
mWindowManager.showRecentApps(false /* fromHome */);
return false;
}
if (prev != null) {
prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
}
mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
ActivityRecord r = getHomeActivity();
final String myReason = reason + " resumeHomeStackTask";
// Only resume home activity if isn't finishing.
if (r != null && !r.finishing) {
mService.setFocusedActivityLocked(r, myReason);
return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
}
return mService.startHomeActivityLocked(mCurrentUser, myReason);
}
TaskRecord anyTaskForIdLocked(int id) {
return anyTaskForIdLocked(id, RESTORE_FROM_RECENTS, INVALID_STACK_ID);
}
/**
* Returns a {@link TaskRecord} for the input id if available. Null otherwise.
* @param id Id of the task we would like returned.
* @param restoreFromRecents If the id was not in the active list, but was found in recents,
* restore the task from recents to the active list.
* @param stackId The stack to restore the task to (default launch stack will be used if
* stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}).
*/
TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents, int stackId) {
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
ActivityStack stack = stacks.get(stackNdx);
TaskRecord task = stack.taskForIdLocked(id);
if (task != null) {
return task;
}
}
}
// Don't give up! Look in recents.
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
TaskRecord task = mRecentTasks.taskForIdLocked(id);
if (task == null) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
return null;
}
if (!restoreFromRecents) {
return task;
}
if (!restoreRecentTaskLocked(task, stackId)) {
if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
"Couldn't restore task id=" + id + " found in recents");
return null;
}
if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
return task;
}
ActivityRecord isInAnyStackLocked(IBinder token) {
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityRecord r = stacks.get(stackNdx).isInStackLocked(token);
if (r != null) {
return r;
}
}
}
return null;
}
/**
* 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) {
final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
if (taskId > currentTaskId) {
mCurTaskIdForUser.put(userId, taskId);
}
}
int getNextTaskIdForUserLocked(int userId) {
final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER);
// for a userId u, a taskId can only be in the range
// [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
// was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
int candidateTaskId = currentTaskId;
while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId)
|| anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS,
INVALID_STACK_ID) != null) {
candidateTaskId++;
if (candidateTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
// Wrap around as there will be smaller task ids that are available now.
candidateTaskId -= MAX_TASK_IDS_PER_USER;
}
if (candidateTaskId == currentTaskId) {
// Something wrong!
// All MAX_TASK_IDS_PER_USER task ids are taken up by running tasks for this user
throw new IllegalStateException("Cannot get an available task id."
+ " Reached limit of " + MAX_TASK_IDS_PER_USER
+ " running tasks per user.");
}
}
mCurTaskIdForUser.put(userId, candidateTaskId);
return candidateTaskId;
}
ActivityRecord resumedAppLocked() {
ActivityStack stack = mFocusedStack;
if (stack == null) {
return null;
}
ActivityRecord resumedActivity = stack.mResumedActivity;
if (resumedActivity == null || resumedActivity.app == null) {
resumedActivity = stack.mPausingActivity;
if (resumedActivity == null || resumedActivity.app == null) {
resumedActivity = stack.topRunningActivityLocked();
}
}
return resumedActivity;
}
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
final String processName = app.processName;
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!isFocusedStack(stack)) {
continue;
}
ActivityRecord hr = stack.topRunningActivityLocked();
if (hr != null) {
if (hr.app == null && app.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
if (realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting activity "
+ hr.intent.getComponent().flattenToShortString(), e);
throw e;
}
}
}
}
}
if (!didSomething) {
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
return didSomething;
}
boolean allResumedActivitiesIdle() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!isFocusedStack(stack) || stack.numActivities() == 0) {
continue;
}
final ActivityRecord resumedActivity = stack.mResumedActivity;
if (resumedActivity == null || !resumedActivity.idle) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
+ stack.mStackId + " " + resumedActivity + " not idle");
return false;
}
}
}
return true;
}
boolean allResumedActivitiesComplete() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (isFocusedStack(stack)) {
final ActivityRecord r = stack.mResumedActivity;
if (r != null && r.state != RESUMED) {
return false;
}
}
}
}
// TODO: Not sure if this should check if all Paused are complete too.
if (DEBUG_STACK) Slog.d(TAG_STACK,
"allResumedActivitiesComplete: mLastFocusedStack changing from=" +
mLastFocusedStack + " to=" + mFocusedStack);
mLastFocusedStack = mFocusedStack;
return true;
}
boolean allResumedActivitiesVisible() {
boolean foundResumed = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
final ActivityRecord r = stack.mResumedActivity;
if (r != null) {
if (!r.nowVisible || mWaitingVisibleActivities.contains(r)) {
return false;
}
foundResumed = true;
}
}
}
return foundResumed;
}
/**
* Pause all activities in either all of the stacks or just the back stacks.
* @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
* @return true if any activity was paused as a result of this call.
*/
boolean pauseBackStacks(boolean userLeaving, boolean resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!isFocusedStack(stack) && stack.mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.mResumedActivity);
someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
dontWait);
}
}
}
return someActivityPaused;
}
boolean allPausedActivitiesComplete() {
boolean pausing = true;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
final ActivityRecord r = stack.mPausingActivity;
if (r != null && r.state != PAUSED && r.state != STOPPED && r.state != STOPPING) {
if (DEBUG_STATES) {
Slog.d(TAG_STATES,
"allPausedActivitiesComplete: r=" + r + " state=" + r.state);
pausing = false;
} else {
return false;
}
}
}
}
return pausing;
}
void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping,
boolean resuming, boolean dontWait) {
// TODO: Put all stacks in supervisor and iterate through them instead.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack.mResumedActivity != null &&
stack.mActivityContainer.mParentActivity == parent) {
stack.startPausingLocked(userLeaving, uiSleeping, resuming, dontWait);
}
}
}
}
void cancelInitializingActivities() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
stacks.get(stackNdx).cancelInitializingActivities();
}
}
}
void reportActivityVisibleLocked(ActivityRecord r) {
sendWaitingVisibleReportLocked(r);
}
void sendWaitingVisibleReportLocked(ActivityRecord r) {
boolean changed = false;
for (int i = mWaitingActivityVisible.size()-1; i >= 0; i--) {
WaitResult w = mWaitingActivityVisible.get(i);
if (w.who == null) {
changed = true;
w.timeout = false;
if (r != null) {
w.who = new ComponentName(r.info.packageName, r.info.name);
}
w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
w.thisTime = w.totalTime;
}
}
if (changed) {
mService.notifyAll();
}
}
void reportTaskToFrontNoLaunch(ActivityRecord r) {
boolean changed = false;
for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
WaitResult w = mWaitingActivityLaunched.remove(i);
if (w.who == null) {
changed = true;
// Set result to START_TASK_TO_FRONT so that startActivityMayWait() knows that
// the starting activity ends up moving another activity to front, and it should
// wait for this new activity to become visible instead.
// Do not modify other fields.
w.result = START_TASK_TO_FRONT;
}
}
if (changed) {
mService.notifyAll();
}
}
void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
long thisTime, long totalTime) {
boolean changed = false;
for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
WaitResult w = mWaitingActivityLaunched.remove(i);
if (w.who == null) {
changed = true;
w.timeout = timeout;
if (r != null) {
w.who = new ComponentName(r.info.packageName, r.info.name);
}
w.thisTime = thisTime;
w.totalTime = totalTime;
// Do not modify w.result.
}
}
if (changed) {
mService.notifyAll();
}
}
ActivityRecord topRunningActivityLocked() {
final ActivityStack focusedStack = mFocusedStack;
ActivityRecord r = focusedStack.topRunningActivityLocked();
if (r != null) {
return r;
}
// Look in other non-focused and non-home stacks.
final ArrayList<ActivityStack> stacks = mHomeStack.mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack != focusedStack && isFrontStack(stack) && stack.isFocusable()) {
r = stack.topRunningActivityLocked();
if (r != null) {
return r;
}
}
}
return null;
}
void getTasksLocked(int maxNum, List<RunningTaskInfo> list, int callingUid, boolean allowed) {
// Gather all of the running tasks for each stack into runningTaskLists.
ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists =
new ArrayList<ArrayList<RunningTaskInfo>>();
final int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<>();
runningTaskLists.add(stackTaskList);
stack.getTasksLocked(stackTaskList, callingUid, allowed);
}
}
// The lists are already sorted from most recent to oldest. Just pull the most recent off
// each list and add it to list. Stop when all lists are empty or maxNum reached.
while (maxNum > 0) {
long mostRecentActiveTime = Long.MIN_VALUE;
ArrayList<RunningTaskInfo> selectedStackList = null;
final int numTaskLists = runningTaskLists.size();
for (int stackNdx = 0; stackNdx < numTaskLists; ++stackNdx) {
ArrayList<RunningTaskInfo> stackTaskList = runningTaskLists.get(stackNdx);
if (!stackTaskList.isEmpty()) {
final long lastActiveTime = stackTaskList.get(0).lastActiveTime;
if (lastActiveTime > mostRecentActiveTime) {
mostRecentActiveTime = lastActiveTime;
selectedStackList = stackTaskList;
}
}
}
if (selectedStackList != null) {
list.add(selectedStackList.remove(0));
--maxNum;
} else {
break;
}
}
}
ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
ProfilerInfo profilerInfo) {
final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
if (aInfo != null) {
// Store the found target back into the intent, because now that
// we have it we never want to do this again. For example, if the
// user navigates back to this point in the history, we should
// always restart the exact same activity.
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
// Don't debug things in the system process
if (!aInfo.processName.equals("system")) {
if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
mService.setDebugApp(aInfo.processName, true, false);
}
if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
mService.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
}
if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
mService.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
}
if (profilerInfo != null) {
mService.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
}
}
}
return aInfo;
}
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
return resolveIntent(intent, resolvedType, userId, 0);
}
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
try {
return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | flags
| ActivityManagerService.STOCK_PM_FLAGS, userId);
} catch (RemoteException e) {
}
return null;
}
ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
ProfilerInfo profilerInfo, int userId) {
final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId);
return resolveActivity(intent, rInfo, startFlags, profilerInfo);
}
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
if (!allPausedActivitiesComplete()) {
// While there are activities pausing we skipping starting any new activities until
// pauses are complete. NOTE: that we also do this for activities that are starting in
// the paused state because they will first be resumed then paused on the client side.
if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
"realStartActivityLocked: Skipping start of r=" + r
+ " some activities pausing...");
return false;
}
if (andResume) {
r.startFreezingScreenLocked(app, 0);
mWindowManager.setAppVisibility(r.appToken, true);
// schedule launch ticks to collect information about slow apps.
r.startLaunchTickingLocked();
}
// Have the window manager re-evaluate the orientation of
// the screen based on the new activity order. Note that
// as a result of this, it can call back into the activity
// manager with a new orientation. We don't care about that,
// because the activity is not currently running so we are
// just restarting it anyway.
if (checkConfig) {
Configuration config = mWindowManager.updateOrientationFromAppTokens(
mService.mConfiguration,
r.mayFreezeScreenLocked(app) ? r.appToken : null);
mService.updateConfigurationLocked(config, r, false);
}
r.app = app;
app.waitingToKill = null;
r.launchCount++;
r.lastLaunchTime = SystemClock.uptimeMillis();
if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
int idx = app.activities.indexOf(r);
if (idx < 0) {
app.activities.add(r);
}
mService.updateLruProcessLocked(app, true, null);
mService.updateOomAdjLocked();
final TaskRecord task = r.task;
if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE ||
task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) {
setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE", false);
}
final ActivityStack stack = task.stack;
try {
if (app.thread == null) {
throw new RemoteException();
}
List<ResultInfo> results = null;
List<ReferrerIntent> newIntents = null;
if (andResume) {
results = r.results;
newIntents = r.newIntents;
}
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
"Launching: " + r + " icicle=" + r.icicle + " with results=" + results
+ " newIntents=" + newIntents + " andResume=" + andResume);
if (andResume) {
EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
r.userId, System.identityHashCode(r),
task.taskId, r.shortComponentName);
}
if (r.isHomeActivity()) {
// Home process is the root process of the task.
mService.mHomeProcess = task.mActivities.get(0).app;
}
mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY);
r.sleeping = false;
r.forceNewConfig = false;
mService.showUnsupportedZoomDialogIfNeededLocked(r);
mService.showAskCompatModeDialogLocked(r);
r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
ProfilerInfo profilerInfo = null;
if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
if (mService.mProfileProc == null || mService.mProfileProc == app) {
mService.mProfileProc = app;
final String profileFile = mService.mProfileFile;
if (profileFile != null) {
ParcelFileDescriptor profileFd = mService.mProfileFd;
if (profileFd != null) {
try {
profileFd = profileFd.dup();
} catch (IOException e) {
if (profileFd != null) {
try {
profileFd.close();
} catch (IOException o) {
}
profileFd = null;
}
}
}
profilerInfo = new ProfilerInfo(profileFile, profileFd,
mService.mSamplingInterval, mService.mAutoStopProfiler);
}
}
}
if (andResume) {
app.hasShownUi = true;
app.pendingUiClean = true;
}
app.forceProcessStateUpTo(mService.mTopProcessState);
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
if ((app.info.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
// This may be a heavy-weight process! Note that the package
// manager will ensure that only activity can run in the main
// process of the .apk, which is the only thing that will be
// considered heavy-weight.
if (app.processName.equals(app.info.packageName)) {
if (mService.mHeavyWeightProcess != null
&& mService.mHeavyWeightProcess != app) {
Slog.w(TAG, "Starting new heavy weight process " + app
+ " when already running "
+ mService.mHeavyWeightProcess);
}
mService.mHeavyWeightProcess = app;
Message msg = mService.mHandler.obtainMessage(
ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
msg.obj = r;
mService.mHandler.sendMessage(msg);
}
}
} catch (RemoteException e) {
if (r.launchFailed) {
// This is the second time we failed -- finish activity
// and give up.
Slog.e(TAG, "Second failure launching "
+ r.intent.getComponent().flattenToShortString()
+ ", giving up", e);
mService.appDiedLocked(app);
stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
"2nd-crash", false);
return false;
}
// This is the first time we failed -- restart process and
// retry.
app.activities.remove(r);
throw e;
}
r.launchFailed = false;
if (stack.updateLRUListLocked(r)) {
Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
}
if (andResume) {
// As part of the process of launching, ActivityThread also performs
// a resume.
stack.minimalResumeActivityLocked(r);
} else {
// This activity is not starting in the resumed state... which should look like we asked
// it to pause+stop (but remain visible), and it has done so and reported back the
// current icicle and other state.
if (DEBUG_STATES) Slog.v(TAG_STATES,
"Moving to PAUSED: " + r + " (starting in paused state)");
r.state = PAUSED;
}
// Launch the new version setup screen if needed. We do this -after-
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
// switch back to it faster and look better.
if (isFocusedStack(stack)) {
mService.startSetupActivityLocked();
}
// Update any services we are bound to that might care about whether
// their client may have activities.
if (r.app != null) {
mService.mServices.updateServiceConnectionActivitiesLocked(r.app);
}
return true;
}
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,
ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) {
final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
callingUid);
if (startAnyPerm == PERMISSION_GRANTED) {
return true;
}
final int componentRestriction = getComponentRestrictionForCallingPackage(
aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
final int actionRestriction = getActionRestrictionForCallingPackage(
intent.getAction(), callingPackage, callingPid, callingUid);
if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
|| actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1,
resultRecord, resultWho, requestCode,
Activity.RESULT_CANCELED, null);
}
final String msg;
if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
msg = "Permission Denial: starting " + intent.toString()
+ " from " + callerApp + " (pid=" + callingPid
+ ", uid=" + callingUid + ")" + " with revoked permission "
+ ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
} else if (!aInfo.exported) {
msg = "Permission Denial: starting " + intent.toString()
+ " from " + callerApp + " (pid=" + callingPid
+ ", uid=" + callingUid + ")"
+ " not exported from uid " + aInfo.applicationInfo.uid;
} else {
msg = "Permission Denial: starting " + intent.toString()
+ " from " + callerApp + " (pid=" + callingPid
+ ", uid=" + callingUid + ")"
+ " requires " + aInfo.permission;
}
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
if (actionRestriction == ACTIVITY_RESTRICTION_APPOP) {
final String message = "Appop Denial: starting " + intent.toString()
+ " from " + callerApp + " (pid=" + callingPid
+ ", uid=" + callingUid + ")"
+ " requires " + AppOpsManager.permissionToOp(
ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction()));
Slog.w(TAG, message);
return false;
} else if (componentRestriction == ACTIVITY_RESTRICTION_APPOP) {
final String message = "Appop Denial: starting " + intent.toString()
+ " from " + callerApp + " (pid=" + callingPid
+ ", uid=" + callingUid + ")"
+ " requires appop " + AppOpsManager.permissionToOp(aInfo.permission);
Slog.w(TAG, message);
return false;
}
if (options != null && options.getLaunchTaskId() != -1) {
final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,
callingPid, callingUid);
if (startInTaskPerm != PERMISSION_GRANTED) {
final String msg = "Permission Denial: starting " + intent.toString()
+ " from " + callerApp + " (pid=" + callingPid
+ ", uid=" + callingUid + ") with launchTaskId="
+ options.getLaunchTaskId();
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
}
return true;
}
UserInfo getUserInfo(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
return UserManager.get(mService.mContext).getUserInfo(userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
== PackageManager.PERMISSION_DENIED) {
return ACTIVITY_RESTRICTION_PERMISSION;
}
if (activityInfo.permission == null) {
return ACTIVITY_RESTRICTION_NONE;
}
final int opCode = AppOpsManager.permissionToOpCode(activityInfo.permission);
if (opCode == AppOpsManager.OP_NONE) {
return ACTIVITY_RESTRICTION_NONE;
}
if (mService.mAppOpsService.noteOperation(opCode, callingUid,
callingPackage) != AppOpsManager.MODE_ALLOWED) {
if (!ignoreTargetSecurity) {
return ACTIVITY_RESTRICTION_APPOP;
}
}
return ACTIVITY_RESTRICTION_NONE;
}
private int getActionRestrictionForCallingPackage(String action,
String callingPackage, int callingPid, int callingUid) {
if (action == null) {
return ACTIVITY_RESTRICTION_NONE;
}
String permission = ACTION_TO_RUNTIME_PERMISSION.get(action);
if (permission == null) {
return ACTIVITY_RESTRICTION_NONE;
}
final PackageInfo packageInfo;
try {
packageInfo = mService.mContext.getPackageManager()
.getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS);
} catch (PackageManager.NameNotFoundException e) {
Slog.i(TAG, "Cannot find package info for " + callingPackage);
return ACTIVITY_RESTRICTION_NONE;
}
if (!ArrayUtils.contains(packageInfo.requestedPermissions, permission)) {
return ACTIVITY_RESTRICTION_NONE;
}
if (mService.checkPermission(permission, callingPid, callingUid) ==
PackageManager.PERMISSION_DENIED) {
return ACTIVITY_RESTRICTION_PERMISSION;
}
final int opCode = AppOpsManager.permissionToOpCode(permission);
if (opCode == AppOpsManager.OP_NONE) {
return ACTIVITY_RESTRICTION_NONE;
}
if (mService.mAppOpsService.noteOperation(opCode, callingUid,
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return ACTIVITY_RESTRICTION_APPOP;
}
return ACTIVITY_RESTRICTION_NONE;
}
boolean moveActivityStackToFront(ActivityRecord r, String reason) {
if (r == null) {
// Not sure what you are trying to do, but it is not going to work...
return false;
}
final TaskRecord task = r.task;
if (task == null || task.stack == null) {
Slog.w(TAG, "Can't move stack to front for r=" + r + " task=" + task);
return false;
}
task.stack.moveToFront(reason, task);
return true;
}
void setLaunchSource(int uid) {
mLaunchingActivity.setWorkSource(new WorkSource(uid));
}
void acquireLaunchWakelock() {
if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivity.acquire();
if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
// To be safe, don't allow the wake lock to be held for too long.
mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT);
}
}
/**
* Called when the frontmost task is idle.
* @return the state of mService.mBooting before this was called.
*/
private boolean checkFinishBootingLocked() {
final boolean booting = mService.mBooting;
boolean enableScreen = false;
mService.mBooting = false;
if (!mService.mBooted) {
mService.mBooted = true;
enableScreen = true;
}
if (booting || enableScreen) {
mService.postFinishBooting(booting, enableScreen);
}
return booting;
}
// Checked.
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
ArrayList<ActivityRecord> finishes = null;
ArrayList<UserState> startingUsers = null;
int NS = 0;
int NF = 0;
boolean booting = false;
boolean activityRemoved = false;
ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
+ Debug.getCallers(4));
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
r.finishLaunchTickingLocked();
if (fromTimeout) {
reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
}
// This is a hack to semi-deal with a race condition
// in the client where it can be constructed with a
// newer configuration from when we asked it to launch.
// We'll update with whatever configuration it now says
// it used to launch.
if (config != null) {
r.configuration = config;
}
// We are now idle. If someone is waiting for a thumbnail from
// us, we can now deliver.
r.idle = true;
//Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
if (isFocusedStack(r.task.stack) || fromTimeout) {
booting = checkFinishBootingLocked();
}
}
if (allResumedActivitiesIdle()) {
if (r != null) {
mService.scheduleAppGcsLocked();
}
if (mLaunchingActivity.isHeld()) {
mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
if (VALIDATE_WAKE_LOCK_CALLER &&
Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivity.release();
}
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
// Atomically retrieve all of the other things to do.
final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(true);
NS = stops != null ? stops.size() : 0;
if ((NF = mFinishingActivities.size()) > 0) {
finishes = new ArrayList<>(mFinishingActivities);
mFinishingActivities.clear();
}
if (mStartingUsers.size() > 0) {
startingUsers = new ArrayList<>(mStartingUsers);
mStartingUsers.clear();
}
// Stop any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (int i = 0; i < NS; i++) {
r = stops.get(i);
final ActivityStack stack = r.task.stack;
if (stack != null) {
if (r.finishing) {
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
} else {
stack.stopActivityLocked(r);
}
}
}
// Finish any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (int i = 0; i < NF; i++) {
r = finishes.get(i);
final ActivityStack stack = r.task.stack;
if (stack != null) {
activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
}
}
if (!booting) {
// Complete user switch
if (startingUsers != null) {
for (int i = 0; i < startingUsers.size(); i++) {
mService.mUserController.finishUserSwitch(startingUsers.get(i));
}
}
}
mService.trimApplications();
//dump();
//mWindowManager.dump();
if (activityRemoved) {
resumeFocusedStackTopActivityLocked();
}
return r;
}
boolean handleAppDiedLocked(ProcessRecord app) {
boolean hasVisibleActivities = false;
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) {
hasVisibleActivities |= stacks.get(stackNdx).handleAppDiedLocked(app);
}
}
return hasVisibleActivities;
}
void closeSystemDialogsLocked() {
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) {
stacks.get(stackNdx).closeSystemDialogsLocked();
}
}
}
void removeUserLocked(int userId) {
mUserStackInFront.delete(userId);
}
/**
* Update the last used stack id for non-current user (current user's last
* used stack is the focused stack)
*/
void updateUserStackLocked(int userId, ActivityStack stack) {
if (userId != mCurrentUser) {
mUserStackInFront.put(userId, stack != null ? stack.getStackId() : HOME_STACK_ID);
}
}
/**
* @return true if some activity was finished (or would have finished if doit were true).
*/
boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
boolean doit, boolean evenPersistent, int userId) {
boolean didSomething = false;
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) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack.finishDisabledPackageActivitiesLocked(
packageName, filterByClasses, doit, evenPersistent, userId)) {
didSomething = true;
}
}
}
return didSomething;
}
void updatePreviousProcessLocked(ActivityRecord r) {
// Now that this process has stopped, we may want to consider
// it to be the previous app to try to keep around in case
// the user wants to return to it.
// First, found out what is currently the foreground app, so that
// we don't blow away the previous app if this activity is being
// hosted by the process that is actually still the foreground.
ProcessRecord fgApp = null;
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) {
final ActivityStack stack = stacks.get(stackNdx);
if (isFocusedStack(stack)) {
if (stack.mResumedActivity != null) {
fgApp = stack.mResumedActivity.app;
} else if (stack.mPausingActivity != null) {
fgApp = stack.mPausingActivity.app;
}
break;
}
}
}
// Now set this one as the previous process, only if that really
// makes sense to.
if (r.app != null && fgApp != null && r.app != fgApp
&& r.lastVisibleTime > mService.mPreviousProcessVisibleTime
&& r.app != mService.mHomeProcess) {
mService.mPreviousProcess = r.app;
mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
}
}
boolean resumeFocusedStackTopActivityLocked() {
return resumeFocusedStackTopActivityLocked(null, null, null);
}
boolean resumeFocusedStackTopActivityLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if (targetStack != null && isFocusedStack(targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
if (r == null || r.state != RESUMED) {
mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
}
return false;
}
void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
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) {
stacks.get(stackNdx).updateActivityApplicationInfoLocked(aInfo);
}
}
}
TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) {
TaskRecord finishedTask = null;
ActivityStack focusedStack = getFocusedStack();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
TaskRecord t = stack.finishTopRunningActivityLocked(app, reason);
if (stack == focusedStack || finishedTask == null) {
finishedTask = t;
}
}
}
return finishedTask;
}
void finishVoiceTask(IVoiceInteractionSession session) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
stack.finishVoiceTask(session);
}
}
}
void findTaskToMoveToFrontLocked(TaskRecord task, int flags, ActivityOptions options,
String reason, boolean forceNonResizeable) {
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.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
if (task.stack == null) {
Slog.e(TAG, "findTaskToMoveToFrontLocked: can't move task="
+ task + " to front. Stack is null");
return;
}
if (task.isResizeable() && options != null) {
int stackId = options.getLaunchStackId();
if (canUseActivityOptionsLaunchBounds(options, stackId)) {
final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
task.updateOverrideConfiguration(bounds);
if (stackId == INVALID_STACK_ID) {
stackId = task.getLaunchStackId();
}
if (stackId != task.stack.mStackId) {
final ActivityStack stack = moveTaskToStackUncheckedLocked(
task, stackId, ON_TOP, !FORCE_FOCUS, reason);
stackId = stack.mStackId;
// moveTaskToStackUncheckedLocked() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
if (StackId.resizeStackWithLaunchBounds(stackId)) {
resizeStackLocked(stackId, bounds,
null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
!PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME);
} else {
// WM resizeTask must be done after the task is moved to the correct stack,
// because Task's setBounds() also updates dim layer's bounds, but that has
// dependency on the stack.
mWindowManager.resizeTask(task.taskId, task.mBounds, task.mOverrideConfig,
false /* relayout */, false /* forced */);
}
}
}
final ActivityRecord r = task.getTopActivity();
task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
r == null ? null : r.appTimeTracker, reason);
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId,
forceNonResizeable);
}
boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
// We use the launch bounds in the activity options is the device supports freeform
// window management or is launching into the pinned stack.
if (options.getLaunchBounds() == null) {
return false;
}
return (mService.mSupportsPictureInPicture && launchStackId == PINNED_STACK_ID)
|| mService.mSupportsFreeformWindowManagement;
}
ActivityStack getStack(int stackId) {
return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
}
ActivityStack getStack(int stackId, boolean createStaticStackIfNeeded, boolean createOnTop) {
ActivityContainer activityContainer = mActivityContainers.get(stackId);
if (activityContainer != null) {
return activityContainer.mStack;
}
if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
return null;
}
return createStackOnDisplay(stackId, Display.DEFAULT_DISPLAY, createOnTop);
}
ArrayList<ActivityStack> getStacks() {
ArrayList<ActivityStack> allStacks = new ArrayList<>();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
allStacks.addAll(mActivityDisplays.valueAt(displayNdx).mStacks);
}
return allStacks;
}
IBinder getHomeActivityToken() {
ActivityRecord homeActivity = getHomeActivity();
if (homeActivity != null) {
return homeActivity.appToken;
}
return null;
}
ActivityRecord getHomeActivity() {
return getHomeActivityForUser(mCurrentUser);
}
ActivityRecord getHomeActivityForUser(int userId) {
final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
if (task.isHomeTask()) {
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.isHomeActivity()
&& ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
return r;
}
}
}
}
return null;
}
/**
* Returns if a stack should be treated as if it's docked. Returns true if the stack is
* the docked stack itself, or if it's side-by-side to the docked stack.
*/
boolean isStackDockedInEffect(int stackId) {
return stackId == DOCKED_STACK_ID ||
(StackId.isResizeableByDockedStack(stackId) && getStack(DOCKED_STACK_ID) != null);
}
ActivityContainer createVirtualActivityContainer(ActivityRecord parentActivity,
IActivityContainerCallback callback) {
ActivityContainer activityContainer =
new VirtualActivityContainer(parentActivity, callback);
mActivityContainers.put(activityContainer.mStackId, activityContainer);
if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
"createActivityContainer: " + activityContainer);
parentActivity.mChildContainers.add(activityContainer);
return activityContainer;
}
void removeChildActivityContainers(ActivityRecord parentActivity) {
final ArrayList<ActivityContainer> childStacks = parentActivity.mChildContainers;
for (int containerNdx = childStacks.size() - 1; containerNdx >= 0; --containerNdx) {
ActivityContainer container = childStacks.remove(containerNdx);
if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "removeChildActivityContainers: removing "
+ container);
container.release();
}
}
void deleteActivityContainer(IActivityContainer container) {
ActivityContainer activityContainer = (ActivityContainer)container;
if (activityContainer != null) {
if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
"deleteActivityContainer: callers=" + Debug.getCallers(4));
final int stackId = activityContainer.mStackId;
mActivityContainers.remove(stackId);
mWindowManager.removeStack(stackId);
}
}
void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
if (stackId == DOCKED_STACK_ID) {
resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
preserveWindows);
return;
}
final ActivityStack stack = getStack(stackId);
if (stack == null) {
Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
return;
}
if (!allowResizeInDockedMode && getStack(DOCKED_STACK_ID) != null) {
// If the docked stack exist we don't allow resizes of stacks not caused by the docked
// stack size changing so things don't get out of sync.
return;
}
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
mWindowManager.deferSurfaceLayout();
try {
resizeStackUncheckedLocked(stack, bounds, tempTaskBounds, tempTaskInsetBounds);
if (!deferResume) {
stack.ensureVisibleActivitiesConfigurationLocked(
stack.topRunningActivityLocked(), preserveWindows);
}
} finally {
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
void deferUpdateBounds(int stackId) {
final ActivityStack stack = getStack(stackId);
if (stack != null) {
stack.deferUpdateBounds();
}
}
void continueUpdateBounds(int stackId) {
final ActivityStack stack = getStack(stackId);
if (stack != null) {
stack.continueUpdateBounds();
}
}
void notifyAppTransitionDone() {
continueUpdateBounds(HOME_STACK_ID);
for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
final int taskId = mResizingTasksDuringAnimation.valueAt(i);
if (anyTaskForIdLocked(taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID) != null) {
mWindowManager.setTaskDockedResizing(taskId, false);
}
}
mResizingTasksDuringAnimation.clear();
}
void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
Rect tempTaskInsetBounds) {
bounds = TaskRecord.validateBounds(bounds);
if (!stack.updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
return;
}
mTmpBounds.clear();
mTmpConfigs.clear();
mTmpInsetBounds.clear();
final ArrayList<TaskRecord> tasks = stack.getAllTasks();
final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
for (int i = tasks.size() - 1; i >= 0; i--) {
final TaskRecord task = tasks.get(i);
if (task.isResizeable()) {
if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
// For freeform stack we don't adjust the size of the tasks to match that
// of the stack, but we do try to make sure the tasks are still contained
// with the bounds of the stack.
tempRect2.set(task.mBounds);
fitWithinBounds(tempRect2, bounds);
task.updateOverrideConfiguration(tempRect2);
} else {
task.updateOverrideConfiguration(taskBounds, insetBounds);
}
}
mTmpConfigs.put(task.taskId, task.mOverrideConfig);
mTmpBounds.put(task.taskId, task.mBounds);
if (tempTaskInsetBounds != null) {
mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
}
}
// We might trigger a configuration change. Save the current task bounds for freezing.
mWindowManager.prepareFreezingTaskBounds(stack.mStackId);
stack.mFullscreen = mWindowManager.resizeStack(stack.mStackId, bounds, mTmpConfigs,
mTmpBounds, mTmpInsetBounds);
stack.setBounds(bounds);
}
void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) {
final ActivityStack stack = getStack(fromStackId);
if (stack == null) {
return;
}
mWindowManager.deferSurfaceLayout();
try {
if (fromStackId == DOCKED_STACK_ID) {
// We are moving all tasks from the docked stack to the fullscreen stack,
// which is dismissing the docked stack, so resize all other stacks to
// fullscreen here already so we don't end up with resize trashing.
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
if (StackId.isResizeableByDockedStack(i)) {
ActivityStack otherStack = getStack(i);
if (otherStack != null) {
resizeStackLocked(i, null, null, null, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, DEFER_RESUME);
}
}
}
// Also disable docked stack resizing since we have manually adjusted the
// size of other stacks above and we don't want to trigger a docked stack
// resize when we remove task from it below and it is detached from the
// display because it no longer contains any tasks.
mAllowDockedStackResize = false;
}
final ArrayList<TaskRecord> tasks = stack.getAllTasks();
final int size = tasks.size();
if (onTop) {
for (int i = 0; i < size; i++) {
moveTaskToStackLocked(tasks.get(i).taskId,
FULLSCREEN_WORKSPACE_STACK_ID, onTop, onTop /*forceFocus*/,
"moveTasksToFullscreenStack", ANIMATE, DEFER_RESUME);
}
ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
} else {
for (int i = size - 1; i >= 0; i--) {
positionTaskInStackLocked(tasks.get(i).taskId,
FULLSCREEN_WORKSPACE_STACK_ID, 0);
}
}
} finally {
mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
}
}
void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows) {
if (!mAllowDockedStackResize) {
// Docked stack resize currently disabled.
return;
}
final ActivityStack stack = getStack(DOCKED_STACK_ID);
if (stack == null) {
Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
return;
}
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
mWindowManager.deferSurfaceLayout();
try {
// Don't allow re-entry while resizing. E.g. due to docked stack detaching.
mAllowDockedStackResize = false;
ActivityRecord r = stack.topRunningActivityLocked();
resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds,
tempDockedTaskInsetBounds);
// TODO: Checking for isAttached might not be needed as if the user passes in null
// dockedBounds then they want the docked stack to be dismissed.
if (stack.mFullscreen || (dockedBounds == null && !stack.isAttached())) {
// The dock stack either was dismissed or went fullscreen, which is kinda the same.
// In this case we make all other static stacks fullscreen and move all
// docked stack tasks to the fullscreen stack.
moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, ON_TOP);
// stack shouldn't contain anymore activities, so nothing to resume.
r = null;
} else {
// Docked stacks occupy a dedicated region on screen so the size of all other
// static stacks need to be adjusted so they don't overlap with the docked stack.
// We get the bounds to use from window manager which has been adjusted for any
// screen controls and is also the same for all stacks.
mWindowManager.getStackDockedModeBounds(
HOME_STACK_ID, tempRect, true /* ignoreVisibility */);
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
resizeStackLocked(i, tempRect, tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
}
}
}
stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
} finally {
mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
mResizeDockedStackTimeout.notifyResizing(dockedBounds,
tempDockedTaskBounds != null
|| tempDockedTaskInsetBounds != null
|| tempOtherTaskBounds != null
|| tempOtherTaskInsetBounds != null);
}
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
final ActivityStack stack = getStack(PINNED_STACK_ID);
if (stack == null) {
Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
return;
}
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
mWindowManager.deferSurfaceLayout();
try {
ActivityRecord r = stack.topRunningActivityLocked();
resizeStackUncheckedLocked(stack, pinnedBounds, tempPinnedTaskBounds,
null);
stack.ensureVisibleActivitiesConfigurationLocked(r, false);
} finally {
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
boolean resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow,
boolean deferResume) {
if (!task.isResizeable()) {
Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
return true;
}
// If this is a forced resize, let it go through even if the bounds is not changing,
// as we might need a relayout due to surface size change (to/from fullscreen).
final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
if (Objects.equals(task.mBounds, bounds) && !forced) {
// Nothing to do here...
return true;
}
bounds = TaskRecord.validateBounds(bounds);
if (!mWindowManager.isValidTaskId(task.taskId)) {
// Task doesn't exist in window manager yet (e.g. was restored from recents).
// All we can do for now is update the bounds so it can be used when the task is
// added to window manager.
task.updateOverrideConfiguration(bounds);
if (task.stack != null && task.stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) {
// re-restore the task so it can have the proper stack association.
restoreRecentTaskLocked(task, FREEFORM_WORKSPACE_STACK_ID);
}
return true;
}
// Do not move the task to another stack here.
// This method assumes that the task is already placed in the right stack.
// we do not mess with that decision and we only do the resize!
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId);
final Configuration overrideConfig = task.updateOverrideConfiguration(bounds);
// This variable holds information whether the configuration didn't change in a significant
// way and the activity was kept the way it was. If it's false, it means the activity had
// to be relaunched due to configuration change.
boolean kept = true;
if (overrideConfig != null) {
final ActivityRecord r = task.topRunningActivityLocked();
if (r != null) {
final ActivityStack stack = task.stack;
kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
if (!deferResume) {
// All other activities must be made visible with their correct configuration.
ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
if (!kept) {
resumeFocusedStackTopActivityLocked();
}
}
}
}
mWindowManager.resizeTask(task.taskId, task.mBounds, task.mOverrideConfig, kept, forced);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
return kept;
}
ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay == null) {
return null;
}
ActivityContainer activityContainer = new ActivityContainer(stackId);
mActivityContainers.put(stackId, activityContainer);
activityContainer.attachToDisplayLocked(activityDisplay, onTop);
return activityContainer.mStack;
}
int getNextStackId() {
while (true) {
if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID
&& getStack(mNextFreeStackId) == null) {
break;
}
mNextFreeStackId++;
}
return mNextFreeStackId;
}
/**
* Restores a recent task to a stack
* @param task The recent task to be restored.
* @param stackId The stack to restore the task to (default launch stack will be used
* if stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}).
* @return true if the task has been restored successfully.
*/
private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
if (stackId == INVALID_STACK_ID) {
stackId = task.getLaunchStackId();
} else if (stackId == DOCKED_STACK_ID && !task.canGoInDockedStack()) {
// Preferred stack is the docked stack, but the task can't go in the docked stack.
// Put it in the fullscreen stack.
stackId = FULLSCREEN_WORKSPACE_STACK_ID;
}
if (task.stack != null) {
// Task has already been restored once. See if we need to do anything more
if (task.stack.mStackId == stackId) {
// Nothing else to do since it is already restored in the right stack.
return true;
}
// Remove current stack association, so we can re-associate the task with the
// right stack below.
task.stack.removeTask(task, "restoreRecentTaskLocked", REMOVE_TASK_MODE_MOVING);
}
final ActivityStack stack =
getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
if (stack == null) {
// What does this mean??? Not sure how we would get here...
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
"Unable to find/create stack to restore recent task=" + task);
return false;
}
stack.addTask(task, false, "restoreRecentTask");
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
"Added restored task=" + task + " to stack=" + stack);
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
stack.addConfigOverride(activities.get(activityNdx), task);
}
return true;
}
/**
* Moves the specified task record to the input stack id.
* WARNING: This method performs an unchecked/raw move of the task and
* can leave the system in an unstable state if used incorrectly.
* Use {@link #moveTaskToStackLocked} to perform safe task movement to a stack.
* @param task Task to move.
* @param stackId Id of stack to move task to.
* @param toTop True if the task should be placed at the top of the stack.
* @param forceFocus if focus should be moved to the new stack
* @param reason Reason the task is been moved.
* @return The stack the task was moved to.
*/
ActivityStack moveTaskToStackUncheckedLocked(
TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) {
if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
throw new IllegalStateException("moveTaskToStackUncheckedLocked: Device doesn't "
+ "support multi-window task=" + task + " to stackId=" + stackId);
}
final ActivityRecord r = task.topRunningActivityLocked();
final ActivityStack prevStack = task.stack;
final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
final boolean wasResumed = prevStack.mResumedActivity == r;
// In some cases the focused stack isn't the front stack. E.g. pinned stack.
// Whenever we are moving the top activity from the front stack we want to make sure to move
// the stack to the front.
final boolean wasFront = isFrontStack(prevStack)
&& (prevStack.topRunningActivityLocked() == r);
if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
// We don't allow moving a unresizeable task to the docked stack since the docked
// stack is used for split-screen mode and will cause things like the docked divider to
// show up. We instead leave the task in its current stack or move it to the fullscreen
// stack if it isn't currently in a stack.
stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
Slog.w(TAG, "Can not move unresizeable task=" + task
+ " to docked stack. Moving to stackId=" + stackId + " instead.");
}
// Temporarily disable resizeablility of task we are moving. We don't want it to be resized
// if a docked stack is created below which will lead to the stack we are moving from and
// its resizeable tasks being resized.
task.mTemporarilyUnresizable = true;
final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
task.mTemporarilyUnresizable = false;
mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop);
stack.addTask(task, toTop, reason);
// If the task had focus before (or we're requested to move focus),
// move focus to the new stack by moving the stack to the front.
stack.moveToFrontAndResumeStateIfNeeded(
r, forceFocus || wasFocused || wasFront, wasResumed, reason);
return stack;
}
boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
String reason, boolean animate) {
return moveTaskToStackLocked(taskId, stackId, toTop, forceFocus, reason, animate,
false /* deferResume */);
}
boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
String reason, boolean animate, boolean deferResume) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
return false;
}
if (task.stack != null && task.stack.mStackId == stackId) {
// You are already in the right stack silly...
Slog.i(TAG, "moveTaskToStack: taskId=" + taskId + " already in stackId=" + stackId);
return true;
}
if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
throw new IllegalArgumentException("moveTaskToStack:"
+ "Attempt to move task " + taskId + " to unsupported freeform stack");
}
final ActivityRecord topActivity = task.getTopActivity();
final int sourceStackId = task.stack != null ? task.stack.mStackId : INVALID_STACK_ID;
final boolean mightReplaceWindow =
StackId.replaceWindowsOnTaskMove(sourceStackId, stackId) && topActivity != null;
if (mightReplaceWindow) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
// and then add a new one. This call will tell window manager about this, so it can
// preserve the old window until the new one is drawn. This prevents having a gap
// between the removal and addition, in which no window is visible. We also want the
// entrance of the new window to be properly animated.
// Note here we always set the replacing window first, as the flags might be needed
// during the relaunch. If we end up not doing any relaunch, we clear the flags later.
mWindowManager.setReplacingWindow(topActivity.appToken, animate);
}
mWindowManager.deferSurfaceLayout();
final int preferredLaunchStackId = stackId;
boolean kept = true;
try {
final ActivityStack stack = moveTaskToStackUncheckedLocked(
task, stackId, toTop, forceFocus, reason + " moveTaskToStack");
stackId = stack.mStackId;
if (!animate) {
stack.mNoAnimActivities.add(topActivity);
}
// We might trigger a configuration change. Save the current task bounds for freezing.
mWindowManager.prepareFreezingTaskBounds(stack.mStackId);
// Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
!mightReplaceWindow, deferResume);
} else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
Rect bounds = task.getLaunchBounds();
if (bounds == null) {
stack.layoutTaskInStack(task, null);
bounds = task.mBounds;
}
kept = resizeTaskLocked(task, bounds, RESIZE_MODE_FORCED, !mightReplaceWindow,
deferResume);
} else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
!mightReplaceWindow, deferResume);
}
} finally {
mWindowManager.continueSurfaceLayout();
}
if (mightReplaceWindow) {
// If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
// window), we need to clear the replace window settings. Otherwise, we schedule a
// timeout to remove the old window if the replacing window is not coming in time.
mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken, !kept);
}
if (!deferResume) {
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
resumeFocusedStackTopActivityLocked();
}
handleNonResizableTaskIfNeeded(task, preferredLaunchStackId, stackId);
return (preferredLaunchStackId == stackId);
}
boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect bounds) {
final ActivityStack stack = getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
if (stack == null) {
throw new IllegalArgumentException(
"moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
}
final ActivityRecord r = stack.topRunningActivityLocked();
if (r == null) {
Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
+ " in stack=" + stack);
return false;
}
if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
Slog.w(TAG,
"moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
+ " r=" + r);
return false;
}
moveActivityToPinnedStackLocked(r, "moveTopActivityToPinnedStack", bounds);
return true;
}
void moveActivityToPinnedStackLocked(ActivityRecord r, String reason, Rect bounds) {
mWindowManager.deferSurfaceLayout();
try {
final TaskRecord task = r.task;
if (r == task.stack.getVisibleBehindActivity()) {
// An activity can't be pinned and visible behind at the same time. Go ahead and
// release it from been visible behind before pinning.
requestVisibleBehindLocked(r, false);
}
// Need to make sure the pinned stack exist so we can resize it below...
final ActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
// Resize the pinned stack to match the current size of the task the activity we are
// going to be moving is currently contained in. We do this to have the right starting
// animation bounds for the pinned stack to the desired bounds the caller wants.
resizeStackLocked(PINNED_STACK_ID, task.mBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
if (task.mActivities.size() == 1) {
// There is only one activity in the task. So, we can just move the task over to
// the stack without re-parenting the activity in a different task.
if (task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
// Move the home stack forward if the task we just moved to the pinned stack
// was launched from home so home should be visible behind it.
moveHomeStackToFront(reason);
}
moveTaskToStackLocked(
task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS, reason, !ANIMATE);
} else {
stack.moveActivityToStack(r);
}
} finally {
mWindowManager.continueSurfaceLayout();
}
// The task might have already been running and its visibility needs to be synchronized
// with the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
mWindowManager.animateResizePinnedStack(bounds, -1);
mService.notifyActivityPinnedLocked();
}
void positionTaskInStackLocked(int taskId, int stackId, int position) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "positionTaskInStackLocked: no task for id=" + taskId);
return;
}
final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
task.updateOverrideConfigurationForStack(stack);
mWindowManager.positionTaskInStack(
taskId, stackId, position, task.mBounds, task.mOverrideConfig);
stack.positionTask(task, position);
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
}
ActivityRecord findTaskLocked(ActivityRecord r) {
mTmpFindTaskResult.r = null;
mTmpFindTaskResult.matchedByRootAffinity = false;
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
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) {
final ActivityStack stack = stacks.get(stackNdx);
if (!r.isApplicationActivity() && !stack.isHomeStack()) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping stack: (home activity) " + stack);
continue;
}
if (!stack.mActivityContainer.isEligibleForNewTasks()) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS,
"Skipping stack: (new task not allowed) " + stack);
continue;
}
stack.findTaskLocked(r, mTmpFindTaskResult);
// It is possible to have task in multiple stacks with the same root affinity.
// If the match we found was based on root affinity we keep on looking to see if
// there is a better match in another stack. We eventually return the match based
// on root affinity if we don't find a better match.
if (mTmpFindTaskResult.r != null && !mTmpFindTaskResult.matchedByRootAffinity) {
return mTmpFindTaskResult.r;
}
}
}
if (DEBUG_TASKS && mTmpFindTaskResult.r == null) Slog.d(TAG_TASKS, "No task found");
return mTmpFindTaskResult.r;
}
ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
boolean compareIntentFilters) {
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) {
final ActivityRecord ar = stacks.get(stackNdx)
.findActivityLocked(intent, info, compareIntentFilters);
if (ar != null) {
return ar;
}
}
}
return null;
}
void goingToSleepLocked() {
scheduleSleepTimeout();
if (!mGoingToSleep.isHeld()) {
mGoingToSleep.acquire();
if (mLaunchingActivity.isHeld()) {
if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivity.release();
mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
}
}
checkReadyForSleepLocked();
}
boolean shutdownLocked(int timeout) {
goingToSleepLocked();
boolean timedout = false;
final long endTime = System.currentTimeMillis() + timeout;
while (true) {
boolean cantShutdown = false;
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) {
cantShutdown |= stacks.get(stackNdx).checkReadyForSleepLocked();
}
}
if (cantShutdown) {
long timeRemaining = endTime - System.currentTimeMillis();
if (timeRemaining > 0) {
try {
mService.wait(timeRemaining);
} catch (InterruptedException e) {
}
} else {
Slog.w(TAG, "Activity manager shutdown timed out");
timedout = true;
break;
}
} else {
break;
}
}
// Force checkReadyForSleep to complete.
mSleepTimeout = true;
checkReadyForSleepLocked();
return timedout;
}
void comeOutOfSleepIfNeededLocked() {
removeSleepTimeouts();
if (mGoingToSleep.isHeld()) {
mGoingToSleep.release();
}
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) {
final ActivityStack stack = stacks.get(stackNdx);
stack.awakeFromSleepingLocked();
if (isFocusedStack(stack)) {
resumeFocusedStackTopActivityLocked();
}
}
}
mGoingToSleepActivities.clear();
}
void activitySleptLocked(ActivityRecord r) {
mGoingToSleepActivities.remove(r);
checkReadyForSleepLocked();
}
void checkReadyForSleepLocked() {
if (!mService.isSleepingOrShuttingDownLocked()) {
// Do not care.
return;
}
if (!mSleepTimeout) {
boolean dontSleep = false;
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) {
dontSleep |= stacks.get(stackNdx).checkReadyForSleepLocked();
}
}
if (mStoppingActivities.size() > 0) {
// Still need to tell some activities to stop; can't sleep yet.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
+ mStoppingActivities.size() + " activities");
scheduleIdleLocked();
dontSleep = true;
}
if (mGoingToSleepActivities.size() > 0) {
// Still need to tell some activities to sleep; can't sleep yet.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to sleep "
+ mGoingToSleepActivities.size() + " activities");
dontSleep = true;
}
if (dontSleep) {
return;
}
}
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) {
stacks.get(stackNdx).goToSleep();
}
}
removeSleepTimeouts();
if (mGoingToSleep.isHeld()) {
mGoingToSleep.release();
}
if (mService.mShuttingDown) {
mService.notifyAll();
}
}
boolean reportResumedActivityLocked(ActivityRecord r) {
final ActivityStack stack = r.task.stack;
if (isFocusedStack(stack)) {
mService.updateUsageStats(r, true);
}
if (allResumedActivitiesComplete()) {
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mWindowManager.executeAppTransition();
return true;
}
return false;
}
void handleAppCrashLocked(ProcessRecord app) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
int stackNdx = stacks.size() - 1;
while (stackNdx >= 0) {
stacks.get(stackNdx).handleAppCrashLocked(app);
stackNdx--;
}
}
}
boolean requestVisibleBehindLocked(ActivityRecord r, boolean visible) {
final ActivityStack stack = r.task.stack;
if (stack == null) {
if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND,
"requestVisibleBehind: r=" + r + " visible=" + visible + " stack is null");
return false;
}
if (visible && !StackId.activitiesCanRequestVisibleBehind(stack.mStackId)) {
if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND, "requestVisibleBehind: r=" + r
+ " visible=" + visible + " stackId=" + stack.mStackId
+ " can't contain visible behind activities");
return false;
}
final boolean isVisible = stack.hasVisibleBehindActivity();
if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND,
"requestVisibleBehind r=" + r + " visible=" + visible + " isVisible=" + isVisible);
final ActivityRecord top = topRunningActivityLocked();
if (top == null || top == r || (visible == isVisible)) {
if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND, "requestVisibleBehind: quick return");
stack.setVisibleBehindActivity(visible ? r : null);
return true;
}
// A non-top activity is reporting a visibility change.
if (visible && top.fullscreen) {
// Let the caller know that it can't be seen.
if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND,
"requestVisibleBehind: returning top.fullscreen=" + top.fullscreen
+ " top.state=" + top.state + " top.app=" + top.app + " top.app.thread="
+ top.app.thread);
return false;
} else if (!visible && stack.getVisibleBehindActivity() != r) {
// Only the activity set as currently visible behind should actively reset its
// visible behind state.
if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND,
"requestVisibleBehind: returning visible=" + visible
+ " stack.getVisibleBehindActivity()=" + stack.getVisibleBehindActivity()
+ " r=" + r);
return false;
}
stack.setVisibleBehindActivity(visible ? r : null);
if (!visible) {
// If there is a translucent home activity, we need to force it stop being translucent,
// because we can't depend on the application to necessarily perform that operation.
// Check out b/14469711 for details.
final ActivityRecord next = stack.findNextTranslucentActivity(r);
if (next != null && next.isHomeActivity()) {
mService.convertFromTranslucent(next.appToken);
}
}
if (top.app != null && top.app.thread != null) {
// Notify the top app of the change.
try {
top.app.thread.scheduleBackgroundVisibleBehindChanged(top.appToken, visible);
} catch (RemoteException e) {
}
}
return true;
}
// Called when WindowManager has finished animating the launchingBehind activity to the back.
void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
final TaskRecord task = r.task;
final ActivityStack stack = task.stack;
r.mLaunchTaskBehind = false;
task.setLastThumbnailLocked(stack.screenshotActivitiesLocked(r));
mRecentTasks.addLocked(task);
mService.notifyTaskStackChangedLocked();
mWindowManager.setAppVisibility(r.appToken, false);
// When launching tasks behind, update the last active time of the top task after the new
// task has been shown briefly
final ActivityRecord top = stack.topActivity();
if (top != null) {
top.task.touchActiveTime();
}
}
void scheduleLaunchTaskBehindComplete(IBinder token) {
mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
}
void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
boolean preserveWindows) {
// First the front stacks. In case any are not fullscreen and are in front of home.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
final int topStackNdx = stacks.size() - 1;
for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows);
}
}
}
void invalidateTaskLayers() {
mTaskLayersChanged = true;
}
void rankTaskLayersIfNeeded() {
if (!mTaskLayersChanged) {
return;
}
mTaskLayersChanged = false;
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
int baseLayer = 0;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
baseLayer += stacks.get(stackNdx).rankTaskLayers(baseLayer);
}
}
}
void clearOtherAppTimeTrackers(AppTimeTracker except) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
final int topStackNdx = stacks.size() - 1;
for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
stack.clearOtherAppTimeTrackers(except);
}
}
}
void scheduleDestroyAllActivities(ProcessRecord app, String reason) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
stack.scheduleDestroyActivities(app, reason);
}
}
}
void releaseSomeActivitiesLocked(ProcessRecord app, String reason) {
// Examine all activities currently running in the process.
TaskRecord firstTask = null;
// Tasks is non-null only if two or more tasks are found.
ArraySet<TaskRecord> tasks = null;
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
for (int i = 0; i < app.activities.size(); i++) {
ActivityRecord r = app.activities.get(i);
// First, if we find an activity that is in the process of being destroyed,
// then we just aren't going to do anything for now; we want things to settle
// down before we try to prune more activities.
if (r.finishing || r.state == DESTROYING || r.state == DESTROYED) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
return;
}
// Don't consider any activies that are currently not in a state where they
// can be destroyed.
if (r.visible || !r.stopped || !r.haveState || r.state == RESUMED || r.state == PAUSING
|| r.state == PAUSED || r.state == STOPPING) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
continue;
}
if (r.task != null) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + r.task
+ " from " + r);
if (firstTask == null) {
firstTask = r.task;
} else if (firstTask != r.task) {
if (tasks == null) {
tasks = new ArraySet<>();
tasks.add(firstTask);
}
tasks.add(r.task);
}
}
}
if (tasks == null) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
return;
}
// If we have activities in multiple tasks that are in a position to be destroyed,
// let's iterate through the tasks and release the oldest one.
final int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
// Step through all stacks starting from behind, to hit the oldest things first.
for (int stackNdx = 0; stackNdx < stacks.size(); stackNdx++) {
final ActivityStack stack = stacks.get(stackNdx);
// Try to release activities in this stack; if we manage to, we are done.
if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
return;
}
}
}
}
boolean switchUserLocked(int userId, UserState uss) {
final int focusStackId = mFocusedStack.getStackId();
// We dismiss the docked stack whenever we switch users.
moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, focusStackId == DOCKED_STACK_ID);
mUserStackInFront.put(mCurrentUser, focusStackId);
final int restoreStackId = mUserStackInFront.get(userId, HOME_STACK_ID);
mCurrentUser = userId;
mStartingUsers.add(uss);
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) {
final ActivityStack stack = stacks.get(stackNdx);
stack.switchUserLocked(userId);
TaskRecord task = stack.topTask();
if (task != null) {
mWindowManager.moveTaskToTop(task.taskId);
}
}
}
ActivityStack stack = getStack(restoreStackId);
if (stack == null) {
stack = mHomeStack;
}
final boolean homeInFront = stack.isHomeStack();
if (stack.isOnHomeDisplay()) {
stack.moveToFront("switchUserOnHomeDisplay");
} else {
// Stack was moved to another display while user was swapped out.
resumeHomeStackTask(HOME_ACTIVITY_TYPE, null, "switchUserOnOtherDisplay");
}
return homeInFront;
}
/** Checks whether the userid is a profile of the current user. */
boolean isCurrentProfileLocked(int userId) {
if (userId == mCurrentUser) return true;
return mService.mUserController.isCurrentProfileLocked(userId);
}
/** Checks whether the activity should be shown for current user. */
boolean okToShowLocked(ActivityRecord r) {
return r != null && ((r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
|| (isCurrentProfileLocked(r.userId)
&& !mService.mUserController.isUserStoppingOrShuttingDownLocked(r.userId)));
}
final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
ArrayList<ActivityRecord> stops = null;
final boolean nowVisible = allResumedActivitiesVisible();
for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord s = mStoppingActivities.get(activityNdx);
final boolean waitingVisible = mWaitingVisibleActivities.contains(s);
if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+ " waitingVisible=" + waitingVisible + " finishing=" + s.finishing);
if (waitingVisible && nowVisible) {
mWaitingVisibleActivities.remove(s);
if (s.finishing) {
// If this activity is finishing, it is sitting on top of
// everyone else but we now know it is no longer needed...
// so get rid of it. Otherwise, we need to go through the
// normal flow and hide it once we determine that it is
// hidden by the activities in front of it.
if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
mWindowManager.setAppVisibility(s.appToken, false);
}
}
if ((!waitingVisible || mService.isSleepingOrShuttingDownLocked()) && remove) {
if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
if (stops == null) {
stops = new ArrayList<>();
}
stops.add(s);
mStoppingActivities.remove(activityNdx);
}
}
return stops;
}
void validateTopActivitiesLocked() {
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) {
final ActivityStack stack = stacks.get(stackNdx);
final ActivityRecord r = stack.topRunningActivityLocked();
final ActivityState state = r == null ? DESTROYED : r.state;
if (isFocusedStack(stack)) {
if (r == null) Slog.e(TAG,
"validateTop...: null top activity, stack=" + stack);
else {
final ActivityRecord pausing = stack.mPausingActivity;
if (pausing != null && pausing == r) Slog.e(TAG,
"validateTop...: top stack has pausing activity r=" + r
+ " state=" + state);
if (state != INITIALIZING && state != RESUMED) Slog.e(TAG,
"validateTop...: activity in front not resumed r=" + r
+ " state=" + state);
}
} else {
final ActivityRecord resumed = stack.mResumedActivity;
if (resumed != null && resumed == r) Slog.e(TAG,
"validateTop...: back stack has resumed activity r=" + r
+ " state=" + state);
if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG,
"validateTop...: activity in back resumed r=" + r + " state=" + state);
}
}
}
}
private String lockTaskModeToString() {
switch (mLockTaskModeState) {
case LOCK_TASK_MODE_LOCKED:
return "LOCKED";
case LOCK_TASK_MODE_PINNED:
return "PINNED";
case LOCK_TASK_MODE_NONE:
return "NONE";
default: return "unknown=" + mLockTaskModeState;
}
}
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
pw.print(prefix); pw.println("mSleepTimeout=" + mSleepTimeout);
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
pw.print(prefix); pw.println("mActivityContainers=" + mActivityContainers);
pw.print(prefix); pw.print("mLockTaskModeState=" + lockTaskModeToString());
final SparseArray<String[]> packages = mService.mLockTaskPackages;
if (packages.size() > 0) {
pw.println(" mLockTaskPackages (userId:packages)=");
for (int i = 0; i < packages.size(); ++i) {
pw.print(prefix); pw.print(prefix); pw.print(packages.keyAt(i));
pw.print(":"); pw.println(Arrays.toString(packages.valueAt(i)));
}
}
pw.println(" mLockTaskModeTasks" + mLockTaskModeTasks);
}
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
return mFocusedStack.getDumpActivitiesLocked(name);
}
static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage,
boolean needSep, String prefix) {
if (activity != null) {
if (dumpPackage == null || dumpPackage.equals(activity.packageName)) {
if (needSep) {
pw.println();
}
pw.print(prefix);
pw.println(activity);
return true;
}
}
return false;
}
boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
boolean dumpClient, String dumpPackage) {
boolean printed = false;
boolean needSep = false;
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
pw.println(" (activities from top to bottom):");
ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
StringBuilder stackHeader = new StringBuilder(128);
stackHeader.append(" Stack #");
stackHeader.append(stack.mStackId);
stackHeader.append(":");
stackHeader.append("\n");
stackHeader.append(" mFullscreen=" + stack.mFullscreen);
stackHeader.append("\n");
stackHeader.append(" mBounds=" + stack.mBounds);
printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
needSep, stackHeader.toString());
printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false,
!dumpAll, false, dumpPackage, true,
" Running activities (most recent first):", null);
needSep = printed;
boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
" mPausingActivity: ");
if (pr) {
printed = true;
needSep = false;
}
pr = printThisActivity(pw, stack.mResumedActivity, dumpPackage, needSep,
" mResumedActivity: ");
if (pr) {
printed = true;
needSep = false;
}
if (dumpAll) {
pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
" mLastPausedActivity: ");
if (pr) {
printed = true;
needSep = true;
}
printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
needSep, " mLastNoHistoryActivity: ");
}
needSep = printed;
}
}
printed |= dumpHistoryList(fd, pw, mFinishingActivities, " ", "Fin", false, !dumpAll,
false, dumpPackage, true, " Activities waiting to finish:", null);
printed |= dumpHistoryList(fd, pw, mStoppingActivities, " ", "Stop", false, !dumpAll,
false, dumpPackage, true, " Activities waiting to stop:", null);
printed |= dumpHistoryList(fd, pw, mWaitingVisibleActivities, " ", "Wait", false, !dumpAll,
false, dumpPackage, true, " Activities waiting for another to become visible:",
null);
printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll,
false, dumpPackage, true, " Activities waiting to sleep:", null);
printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll,
false, dumpPackage, true, " Activities waiting to sleep:", null);
return printed;
}
static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
String prefix, String label, boolean complete, boolean brief, boolean client,
String dumpPackage, boolean needNL, String header1, String header2) {
TaskRecord lastTask = null;
String innerPrefix = null;
String[] args = null;
boolean printed = false;
for (int i=list.size()-1; i>=0; i--) {
final ActivityRecord r = list.get(i);
if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
continue;
}
if (innerPrefix == null) {
innerPrefix = prefix + " ";
args = new String[0];
}
printed = true;
final boolean full = !brief && (complete || !r.isInHistory());
if (needNL) {
pw.println("");
needNL = false;
}
if (header1 != null) {
pw.println(header1);
header1 = null;
}
if (header2 != null) {
pw.println(header2);
header2 = null;
}
if (lastTask != r.task) {
lastTask = r.task;
pw.print(prefix);
pw.print(full ? "* " : " ");
pw.println(lastTask);
if (full) {
lastTask.dump(pw, prefix + " ");
} else if (complete) {
// Complete + brief == give a summary. Isn't that obvious?!?
if (lastTask.intent != null) {
pw.print(prefix); pw.print(" ");
pw.println(lastTask.intent.toInsecureStringWithClip());
}
}
}
pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
pw.print(" #"); pw.print(i); pw.print(": ");
pw.println(r);
if (full) {
r.dump(pw, innerPrefix);
} else if (complete) {
// Complete + brief == give a summary. Isn't that obvious?!?
pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
if (r.app != null) {
pw.print(innerPrefix); pw.println(r.app);
}
}
if (client && r.app != null && r.app.thread != null) {
// flush anything that is already in the PrintWriter since the thread is going
// to write to the file descriptor directly
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
r.appToken, innerPrefix, args);
// Short timeout, since blocking here can
// deadlock with the application.
tp.go(fd, 2000);
} finally {
tp.kill();
}
} catch (IOException e) {
pw.println(innerPrefix + "Failure while dumping the activity: " + e);
} catch (RemoteException e) {
pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
}
needNL = true;
}
}
return printed;
}
void scheduleIdleTimeoutLocked(ActivityRecord next) {
if (DEBUG_IDLE) Slog.d(TAG_IDLE,
"scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
}
final void scheduleIdleLocked() {
mHandler.sendEmptyMessage(IDLE_NOW_MSG);
}
void removeTimeoutsForActivityLocked(ActivityRecord r) {
if (DEBUG_IDLE) Slog.d(TAG_IDLE, "removeTimeoutsForActivity: Callers="
+ Debug.getCallers(4));
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
}
final void scheduleResumeTopActivities() {
if (!mHandler.hasMessages(RESUME_TOP_ACTIVITY_MSG)) {
mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
}
}
void removeSleepTimeouts() {
mSleepTimeout = false;
mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
}
final void scheduleSleepTimeout() {
removeSleepTimeouts();
mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
}
@Override
public void onDisplayAdded(int displayId) {
if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_ADDED, displayId, 0));
}
@Override
public void onDisplayRemoved(int displayId) {
if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_REMOVED, displayId, 0));
}
@Override
public void onDisplayChanged(int displayId) {
if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
}
private void handleDisplayAdded(int displayId) {
boolean newDisplay;
synchronized (mService) {
newDisplay = mActivityDisplays.get(displayId) == null;
if (newDisplay) {
ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
if (activityDisplay.mDisplay == null) {
Slog.w(TAG, "Display " + displayId + " gone before initialization complete");
return;
}
mActivityDisplays.put(displayId, activityDisplay);
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
}
if (newDisplay) {
mWindowManager.onDisplayAdded(displayId);
}
}
private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) {
mDefaultMinSizeOfResizeableTask =
mService.mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.default_minimal_size_resizable_task);
}
private void handleDisplayRemoved(int displayId) {
synchronized (mService) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay != null) {
ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
stacks.get(stackNdx).mActivityContainer.detachLocked();
}
mActivityDisplays.remove(displayId);
}
}
mWindowManager.onDisplayRemoved(displayId);
}
private void handleDisplayChanged(int displayId) {
synchronized (mService) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay != null) {
// TODO: Update the bounds.
}
}
mWindowManager.onDisplayChanged(displayId);
}
private StackInfo getStackInfoLocked(ActivityStack stack) {
final ActivityDisplay display = mActivityDisplays.get(Display.DEFAULT_DISPLAY);
StackInfo info = new StackInfo();
mWindowManager.getStackBounds(stack.mStackId, info.bounds);
info.displayId = Display.DEFAULT_DISPLAY;
info.stackId = stack.mStackId;
info.userId = stack.mCurrentUser;
info.visible = stack.getStackVisibilityLocked(null) == STACK_VISIBLE;
info.position = display != null
? display.mStacks.indexOf(stack)
: 0;
ArrayList<TaskRecord> tasks = stack.getAllTasks();
final int numTasks = tasks.size();
int[] taskIds = new int[numTasks];
String[] taskNames = new String[numTasks];
Rect[] taskBounds = new Rect[numTasks];
int[] taskUserIds = new int[numTasks];
for (int i = 0; i < numTasks; ++i) {
final TaskRecord task = tasks.get(i);
taskIds[i] = task.taskId;
taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
: task.realActivity != null ? task.realActivity.flattenToString()
: task.getTopActivity() != null ? task.getTopActivity().packageName
: "unknown";
taskBounds[i] = new Rect();
mWindowManager.getTaskBounds(task.taskId, taskBounds[i]);
taskUserIds[i] = task.userId;
}
info.taskIds = taskIds;
info.taskNames = taskNames;
info.taskBounds = taskBounds;
info.taskUserIds = taskUserIds;
final ActivityRecord top = stack.topRunningActivityLocked();
info.topActivity = top != null ? top.intent.getComponent() : null;
return info;
}
StackInfo getStackInfoLocked(int stackId) {
ActivityStack stack = getStack(stackId);
if (stack != null) {
return getStackInfoLocked(stack);
}
return null;
}
ArrayList<StackInfo> getAllStackInfosLocked() {
ArrayList<StackInfo> list = new ArrayList<>();
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int ndx = stacks.size() - 1; ndx >= 0; --ndx) {
list.add(getStackInfoLocked(stacks.get(ndx)));
}
}
return list;
}
TaskRecord getLockedTaskLocked() {
final int top = mLockTaskModeTasks.size() - 1;
if (top >= 0) {
return mLockTaskModeTasks.get(top);
}
return null;
}
boolean isLockedTask(TaskRecord task) {
return mLockTaskModeTasks.contains(task);
}
boolean isLastLockedTask(TaskRecord task) {
return mLockTaskModeTasks.size() == 1 && mLockTaskModeTasks.contains(task);
}
void removeLockedTaskLocked(final TaskRecord task) {
if (!mLockTaskModeTasks.remove(task)) {
return;
}
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "removeLockedTaskLocked: removed " + task);
if (mLockTaskModeTasks.isEmpty()) {
// Last one.
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
" last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
final Message lockTaskMsg = Message.obtain();
lockTaskMsg.arg1 = task.userId;
lockTaskMsg.what = LOCK_TASK_END_MSG;
mHandler.sendMessage(lockTaskMsg);
}
}
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId, int actualStackId) {
handleNonResizableTaskIfNeeded(task, preferredStackId, actualStackId,
false /* forceNonResizable */);
}
void handleNonResizableTaskIfNeeded(
TaskRecord task, int preferredStackId, int actualStackId, boolean forceNonResizable) {
if ((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
|| task.isHomeTask()) {
return;
}
final ActivityRecord topActivity = task.getTopActivity();
if (!task.canGoInDockedStack() || forceNonResizable) {
// Display a warning toast that we tried to put a non-dockable task in the docked stack.
mService.mHandler.sendEmptyMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
} else if (topActivity != null && topActivity.isNonResizableOrForced()
&& !topActivity.noDisplay) {
String packageName = topActivity.appInfo.packageName;
mService.mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, task.taskId, 0,
packageName).sendToTarget();
}
}
void showLockTaskToast() {
if (mLockTaskNotify != null) {
mLockTaskNotify.showToast(mLockTaskModeState);
}
}
void showLockTaskEscapeMessageLocked(TaskRecord task) {
if (mLockTaskModeTasks.contains(task)) {
mHandler.sendEmptyMessage(SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG);
}
}
void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason,
boolean andResume) {
if (task == null) {
// Take out of lock task mode if necessary
final TaskRecord lockedTask = getLockedTaskLocked();
if (lockedTask != null) {
removeLockedTaskLocked(lockedTask);
if (!mLockTaskModeTasks.isEmpty()) {
// There are locked tasks remaining, can only finish this task, not unlock it.
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: Tasks remaining, can't unlock");
lockedTask.performClearTaskLocked();
resumeFocusedStackTopActivityLocked();
return;
}
}
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: No tasks to unlock. Callers=" + Debug.getCallers(4));
return;
}
// Should have already been checked, but do it again.
if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: Can't lock due to auth");
return;
}
if (isLockTaskModeViolation(task)) {
Slog.e(TAG_LOCKTASK, "setLockTaskMode: Attempt to start an unauthorized lock task.");
return;
}
if (mLockTaskModeTasks.isEmpty()) {
// First locktask.
final Message lockTaskMsg = Message.obtain();
lockTaskMsg.obj = task.intent.getComponent().getPackageName();
lockTaskMsg.arg1 = task.userId;
lockTaskMsg.what = LOCK_TASK_START_MSG;
lockTaskMsg.arg2 = lockTaskModeState;
mHandler.sendMessage(lockTaskMsg);
}
// Add it or move it to the top.
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskModeLocked: Locking to " + task +
" Callers=" + Debug.getCallers(4));
mLockTaskModeTasks.remove(task);
mLockTaskModeTasks.add(task);
if (task.mLockTaskUid == -1) {
task.mLockTaskUid = task.effectiveUid;
}
if (andResume) {
findTaskToMoveToFrontLocked(task, 0, null, reason,
lockTaskModeState != LOCK_TASK_MODE_NONE);
resumeFocusedStackTopActivityLocked();
} else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId,
true /* forceNonResizable */);
}
}
boolean isLockTaskModeViolation(TaskRecord task) {
return isLockTaskModeViolation(task, false);
}
boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
if (getLockedTaskLocked() == task && !isNewClearTask) {
return false;
}
final int lockTaskAuth = task.mLockTaskAuth;
switch (lockTaskAuth) {
case LOCK_TASK_AUTH_DONT_LOCK:
return !mLockTaskModeTasks.isEmpty();
case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
case LOCK_TASK_AUTH_LAUNCHABLE:
case LOCK_TASK_AUTH_WHITELISTED:
return false;
case LOCK_TASK_AUTH_PINNABLE:
// Pinnable tasks can't be launched on top of locktask tasks.
return !mLockTaskModeTasks.isEmpty();
default:
Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
return true;
}
}
void onLockTaskPackagesUpdatedLocked() {
boolean didSomething = false;
for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
final boolean wasWhitelisted =
(lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) ||
(lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED);
lockedTask.setLockTaskAuth();
final boolean isWhitelisted =
(lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) ||
(lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED);
if (wasWhitelisted && !isWhitelisted) {
// Lost whitelisting authorization. End it now.
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
lockedTask + " mLockTaskAuth=" + lockedTask.lockTaskAuthToString());
removeLockedTaskLocked(lockedTask);
lockedTask.performClearTaskLocked();
didSomething = true;
}
}
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
stack.onLockTaskPackagesUpdatedLocked();
}
}
final ActivityRecord r = topRunningActivityLocked();
final TaskRecord task = r != null ? r.task : null;
if (mLockTaskModeTasks.isEmpty() && task != null
&& task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
// This task must have just been authorized.
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
"onLockTaskPackagesUpdated: starting new locktask task=" + task);
setLockTaskModeLocked(task, ActivityManager.LOCK_TASK_MODE_LOCKED, "package updated",
false);
didSomething = true;
}
if (didSomething) {
resumeFocusedStackTopActivityLocked();
}
}
int getLockTaskModeState() {
return mLockTaskModeState;
}
void activityRelaunchedLocked(IBinder token) {
mWindowManager.notifyAppRelaunchingFinished(token);
}
void activityRelaunchingLocked(ActivityRecord r) {
mWindowManager.notifyAppRelaunching(r.appToken);
}
void logStackState() {
mActivityMetricsLogger.logWindowState();
}
void scheduleReportMultiWindowModeChanged(TaskRecord task) {
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
if (r.app != null && r.app.thread != null) {
mMultiWindowModeChangedActivities.add(r);
}
}
if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
}
}
void scheduleReportPictureInPictureModeChangedIfNeeded(TaskRecord task, ActivityStack prevStack) {
final ActivityStack stack = task.stack;
if (prevStack == null || prevStack == stack
|| (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
return;
}
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
if (r.app != null && r.app.thread != null) {
mPipModeChangedActivities.add(r);
}
}
if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
}
}
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) {
super(looper);
}
void activityIdleInternal(ActivityRecord r) {
synchronized (mService) {
activityIdleInternalLocked(r != null ? r.appToken : null, true, null);
}
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
synchronized (mService) {
for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
r.scheduleMultiWindowModeChanged();
}
}
} break;
case REPORT_PIP_MODE_CHANGED_MSG: {
synchronized (mService) {
for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mPipModeChangedActivities.remove(i);
r.schedulePictureInPictureModeChanged();
}
}
} break;
case IDLE_TIMEOUT_MSG: {
if (DEBUG_IDLE) Slog.d(TAG_IDLE,
"handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
if (mService.mDidDexOpt) {
mService.mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
nmsg.obj = msg.obj;
mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
return;
}
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
activityIdleInternal((ActivityRecord)msg.obj);
} break;
case IDLE_NOW_MSG: {
if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
activityIdleInternal((ActivityRecord)msg.obj);
} break;
case RESUME_TOP_ACTIVITY_MSG: {
synchronized (mService) {
resumeFocusedStackTopActivityLocked();
}
} break;
case SLEEP_TIMEOUT_MSG: {
synchronized (mService) {
if (mService.isSleepingOrShuttingDownLocked()) {
Slog.w(TAG, "Sleep timeout! Sleeping now.");
mSleepTimeout = true;
checkReadyForSleepLocked();
}
}
} break;
case LAUNCH_TIMEOUT_MSG: {
if (mService.mDidDexOpt) {
mService.mDidDexOpt = false;
mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT);
return;
}
synchronized (mService) {
if (mLaunchingActivity.isHeld()) {
Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
if (VALIDATE_WAKE_LOCK_CALLER
&& Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivity.release();
}
}
} break;
case HANDLE_DISPLAY_ADDED: {
handleDisplayAdded(msg.arg1);
} break;
case HANDLE_DISPLAY_CHANGED: {
handleDisplayChanged(msg.arg1);
} break;
case HANDLE_DISPLAY_REMOVED: {
handleDisplayRemoved(msg.arg1);
} break;
case CONTAINER_CALLBACK_VISIBILITY: {
final ActivityContainer container = (ActivityContainer) msg.obj;
final IActivityContainerCallback callback = container.mCallback;
if (callback != null) {
try {
callback.setVisible(container.asBinder(), msg.arg1 == 1);
} catch (RemoteException e) {
}
}
} break;
case LOCK_TASK_START_MSG: {
// When lock task starts, we disable the status bars.
try {
if (mLockTaskNotify == null) {
mLockTaskNotify = new LockTaskNotify(mService.mContext);
}
mLockTaskNotify.show(true);
mLockTaskModeState = msg.arg2;
if (getStatusBarService() != null) {
int flags = 0;
if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
flags = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_BACK);
} else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
flags = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_BACK)
& (~StatusBarManager.DISABLE_HOME)
& (~StatusBarManager.DISABLE_RECENT);
}
getStatusBarService().disable(flags, mToken,
mService.mContext.getPackageName());
}
mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
if (getDevicePolicyManager() != null) {
getDevicePolicyManager().notifyLockTaskModeChanged(true,
(String)msg.obj, msg.arg1);
}
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
} break;
case LOCK_TASK_END_MSG: {
// When lock task ends, we enable the status bars.
try {
if (getStatusBarService() != null) {
getStatusBarService().disable(StatusBarManager.DISABLE_NONE, mToken,
mService.mContext.getPackageName());
}
mWindowManager.reenableKeyguard(mToken);
if (getDevicePolicyManager() != null) {
getDevicePolicyManager().notifyLockTaskModeChanged(false, null,
msg.arg1);
}
if (mLockTaskNotify == null) {
mLockTaskNotify = new LockTaskNotify(mService.mContext);
}
mLockTaskNotify.show(false);
try {
boolean shouldLockKeyguard = Settings.Secure.getInt(
mService.mContext.getContentResolver(),
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED) != 0;
if (mLockTaskModeState == LOCK_TASK_MODE_PINNED && shouldLockKeyguard) {
mWindowManager.lockNow(null);
mWindowManager.dismissKeyguard();
new LockPatternUtils(mService.mContext)
.requireCredentialEntry(UserHandle.USER_ALL);
}
} catch (SettingNotFoundException e) {
// No setting, don't lock.
}
} catch (RemoteException ex) {
throw new RuntimeException(ex);
} finally {
mLockTaskModeState = LOCK_TASK_MODE_NONE;
}
} break;
case SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG: {
if (mLockTaskNotify == null) {
mLockTaskNotify = new LockTaskNotify(mService.mContext);
}
mLockTaskNotify.showToast(LOCK_TASK_MODE_PINNED);
} break;
case CONTAINER_CALLBACK_TASK_LIST_EMPTY: {
final ActivityContainer container = (ActivityContainer) msg.obj;
final IActivityContainerCallback callback = container.mCallback;
if (callback != null) {
try {
callback.onAllActivitiesComplete(container.asBinder());
} catch (RemoteException e) {
}
}
} break;
case LAUNCH_TASK_BEHIND_COMPLETE: {
synchronized (mService) {
ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
if (r != null) {
handleLaunchTaskBehindCompleteLocked(r);
}
}
} break;
}
}
}
class ActivityContainer extends android.app.IActivityContainer.Stub {
final static int FORCE_NEW_TASK_FLAGS = FLAG_ACTIVITY_NEW_TASK |
FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION;
final int mStackId;
IActivityContainerCallback mCallback = null;
final ActivityStack mStack;
ActivityRecord mParentActivity = null;
String mIdString;
boolean mVisible = true;
/** Display this ActivityStack is currently on. Null if not attached to a Display. */
ActivityDisplay mActivityDisplay;
final static int CONTAINER_STATE_HAS_SURFACE = 0;
final static int CONTAINER_STATE_NO_SURFACE = 1;
final static int CONTAINER_STATE_FINISHING = 2;
int mContainerState = CONTAINER_STATE_HAS_SURFACE;
ActivityContainer(int stackId) {
synchronized (mService) {
mStackId = stackId;
mStack = new ActivityStack(this, mRecentTasks);
mIdString = "ActivtyContainer{" + mStackId + "}";
if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this);
}
}
void attachToDisplayLocked(ActivityDisplay activityDisplay, boolean onTop) {
if (DEBUG_STACK) Slog.d(TAG_STACK, "attachToDisplayLocked: " + this
+ " to display=" + activityDisplay + " onTop=" + onTop);
mActivityDisplay = activityDisplay;
mStack.attachDisplay(activityDisplay, onTop);
activityDisplay.attachActivities(mStack, onTop);
}
@Override
public void attachToDisplay(int displayId) {
synchronized (mService) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay == null) {
return;
}
attachToDisplayLocked(activityDisplay, true);
}
}
@Override
public int getDisplayId() {
synchronized (mService) {
if (mActivityDisplay != null) {
return mActivityDisplay.mDisplayId;
}
}
return -1;
}
@Override
public int getStackId() {
synchronized (mService) {
return mStackId;
}
}
@Override
public boolean injectEvent(InputEvent event) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mService) {
if (mActivityDisplay != null) {
return mInputManagerInternal.injectInputEvent(event,
mActivityDisplay.mDisplayId,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
}
return false;
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public void release() {
synchronized (mService) {
if (mContainerState == CONTAINER_STATE_FINISHING) {
return;
}
mContainerState = CONTAINER_STATE_FINISHING;
long origId = Binder.clearCallingIdentity();
try {
mStack.finishAllActivitiesLocked(false);
mService.mActivityStarter.removePendingActivityLaunchesLocked(mStack);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
protected void detachLocked() {
if (DEBUG_STACK) Slog.d(TAG_STACK, "detachLocked: " + this + " from display="
+ mActivityDisplay + " Callers=" + Debug.getCallers(2));
if (mActivityDisplay != null) {
mActivityDisplay.detachActivitiesLocked(mStack);
mActivityDisplay = null;
mStack.detachDisplay();
}
}
@Override
public final int startActivity(Intent intent) {
return mService.startActivity(intent, this);
}
@Override
public final int startActivityIntentSender(IIntentSender intentSender)
throws TransactionTooLargeException {
mService.enforceNotIsolatedCaller("ActivityContainer.startActivityIntentSender");
if (!(intentSender instanceof PendingIntentRecord)) {
throw new IllegalArgumentException("Bad PendingIntent object");
}
final int userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), mCurrentUser, false,
ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null);
final PendingIntentRecord pendingIntent = (PendingIntentRecord) intentSender;
checkEmbeddedAllowedInner(userId, pendingIntent.key.requestIntent,
pendingIntent.key.requestResolvedType);
return pendingIntent.sendInner(0, null, null, null, null, null, null, 0,
FORCE_NEW_TASK_FLAGS, FORCE_NEW_TASK_FLAGS, null, this);
}
void checkEmbeddedAllowedInner(int userId, Intent intent, String resolvedType) {
ActivityInfo aInfo = resolveActivity(intent, resolvedType, 0, null, userId);
if (aInfo != null && (aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
throw new SecurityException(
"Attempt to embed activity that has not set allowEmbedded=\"true\"");
}
}
@Override
public IBinder asBinder() {
return this;
}
@Override
public void setSurface(Surface surface, int width, int height, int density) {
mService.enforceNotIsolatedCaller("ActivityContainer.attachToSurface");
}
ActivityStackSupervisor getOuter() {
return ActivityStackSupervisor.this;
}
boolean isAttachedLocked() {
return mActivityDisplay != null;
}
// TODO: Make sure every change to ActivityRecord.visible results in a call to this.
void setVisible(boolean visible) {
if (mVisible != visible) {
mVisible = visible;
if (mCallback != null) {
mHandler.obtainMessage(CONTAINER_CALLBACK_VISIBILITY, visible ? 1 : 0,
0 /* unused */, this).sendToTarget();
}
}
}
void setDrawn() {
}
// You can always start a new task on a regular ActivityStack.
boolean isEligibleForNewTasks() {
return true;
}
void onTaskListEmptyLocked() {
detachLocked();
deleteActivityContainer(this);
mHandler.obtainMessage(CONTAINER_CALLBACK_TASK_LIST_EMPTY, this).sendToTarget();
}
@Override
public String toString() {
return mIdString + (mActivityDisplay == null ? "N" : "A");
}
}
private class VirtualActivityContainer extends ActivityContainer {
Surface mSurface;
boolean mDrawn = false;
VirtualActivityContainer(ActivityRecord parent, IActivityContainerCallback callback) {
super(getNextStackId());
mParentActivity = parent;
mCallback = callback;
mContainerState = CONTAINER_STATE_NO_SURFACE;
mIdString = "VirtualActivityContainer{" + mStackId + ", parent=" + mParentActivity + "}";
}
@Override
public void setSurface(Surface surface, int width, int height, int density) {
super.setSurface(surface, width, height, density);
synchronized (mService) {
final long origId = Binder.clearCallingIdentity();
try {
setSurfaceLocked(surface, width, height, density);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
private void setSurfaceLocked(Surface surface, int width, int height, int density) {
if (mContainerState == CONTAINER_STATE_FINISHING) {
return;
}
VirtualActivityDisplay virtualActivityDisplay =
(VirtualActivityDisplay) mActivityDisplay;
if (virtualActivityDisplay == null) {
virtualActivityDisplay =
new VirtualActivityDisplay(width, height, density);
mActivityDisplay = virtualActivityDisplay;
mActivityDisplays.put(virtualActivityDisplay.mDisplayId, virtualActivityDisplay);
attachToDisplayLocked(virtualActivityDisplay, true);
}
if (mSurface != null) {
mSurface.release();
}
mSurface = surface;
if (surface != null) {
resumeFocusedStackTopActivityLocked();
} else {
mContainerState = CONTAINER_STATE_NO_SURFACE;
((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
mStack.startPausingLocked(false, true, false, false);
}
}
setSurfaceIfReadyLocked();
if (DEBUG_STACK) Slog.d(TAG_STACK,
"setSurface: " + this + " to display=" + virtualActivityDisplay);
}
@Override
boolean isAttachedLocked() {
return mSurface != null && super.isAttachedLocked();
}
@Override
void setDrawn() {
synchronized (mService) {
mDrawn = true;
setSurfaceIfReadyLocked();
}
}
// Never start a new task on an ActivityView if it isn't explicitly specified.
@Override
boolean isEligibleForNewTasks() {
return false;
}
private void setSurfaceIfReadyLocked() {
if (DEBUG_STACK) Slog.v(TAG_STACK, "setSurfaceIfReadyLocked: mDrawn=" + mDrawn +
" mContainerState=" + mContainerState + " mSurface=" + mSurface);
if (mDrawn && mSurface != null && mContainerState == CONTAINER_STATE_NO_SURFACE) {
((VirtualActivityDisplay) mActivityDisplay).setSurface(mSurface);
mContainerState = CONTAINER_STATE_HAS_SURFACE;
}
}
}
/** Exactly one of these classes per Display in the system. Capable of holding zero or more
* attached {@link ActivityStack}s */
class ActivityDisplay {
/** Actual Display this object tracks. */
int mDisplayId;
Display mDisplay;
DisplayInfo mDisplayInfo = new DisplayInfo();
/** All of the stacks on this display. Order matters, topmost stack is in front of all other
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */
final ArrayList<ActivityStack> mStacks = new ArrayList<>();
ActivityRecord mVisibleBehindActivity;
ActivityDisplay() {
}
// After instantiation, check that mDisplay is not null before using this. The alternative
// is for this to throw an exception if mDisplayManager.getDisplay() returns null.
ActivityDisplay(int displayId) {
final Display display = mDisplayManager.getDisplay(displayId);
if (display == null) {
return;
}
init(display);
}
void init(Display display) {
mDisplay = display;
mDisplayId = display.getDisplayId();
mDisplay.getDisplayInfo(mDisplayInfo);
}
void attachActivities(ActivityStack stack, boolean onTop) {
if (DEBUG_STACK) Slog.v(TAG_STACK,
"attachActivities: attaching " + stack + " to displayId=" + mDisplayId
+ " onTop=" + onTop);
if (onTop) {
mStacks.add(stack);
} else {
mStacks.add(0, stack);
}
}
void detachActivitiesLocked(ActivityStack stack) {
if (DEBUG_STACK) Slog.v(TAG_STACK, "detachActivitiesLocked: detaching " + stack
+ " from displayId=" + mDisplayId);
mStacks.remove(stack);
}
void setVisibleBehindActivity(ActivityRecord r) {
mVisibleBehindActivity = r;
}
boolean hasVisibleBehindActivity() {
return mVisibleBehindActivity != null;
}
@Override
public String toString() {
return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
}
}
class VirtualActivityDisplay extends ActivityDisplay {
VirtualDisplay mVirtualDisplay;
VirtualActivityDisplay(int width, int height, int density) {
DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
mVirtualDisplay = dm.createVirtualDisplay(mService.mContext, null,
VIRTUAL_DISPLAY_BASE_NAME, width, height, density, null,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY, null, null);
init(mVirtualDisplay.getDisplay());
mWindowManager.handleDisplayAdded(mDisplayId);
}
void setSurface(Surface surface) {
if (mVirtualDisplay != null) {
mVirtualDisplay.setSurface(surface);
}
}
@Override
void detachActivitiesLocked(ActivityStack stack) {
super.detachActivitiesLocked(stack);
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
mVirtualDisplay = null;
}
}
@Override
public String toString() {
return "VirtualActivityDisplay={" + mDisplayId + "}";
}
}
/**
* Adjust bounds to stay within stack bounds.
*
* Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
* that keep them unchanged, but be contained within the stack bounds.
*
* @param bounds Bounds to be adjusted.
* @param stackBounds Bounds within which the other bounds should remain.
*/
private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
if (stackBounds == null || stackBounds.contains(bounds)) {
return;
}
if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
final int maxRight = stackBounds.right
- (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
int horizontalDiff = stackBounds.left - bounds.left;
if ((horizontalDiff < 0 && bounds.left >= maxRight)
|| (bounds.left + horizontalDiff >= maxRight)) {
horizontalDiff = maxRight - bounds.left;
}
bounds.left += horizontalDiff;
bounds.right += horizontalDiff;
}
if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
final int maxBottom = stackBounds.bottom
- (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
int verticalDiff = stackBounds.top - bounds.top;
if ((verticalDiff < 0 && bounds.top >= maxBottom)
|| (bounds.top + verticalDiff >= maxBottom)) {
verticalDiff = maxBottom - bounds.top;
}
bounds.top += verticalDiff;
bounds.bottom += verticalDiff;
}
}
ActivityStack findStackBehind(ActivityStack stack) {
// TODO(multi-display): We are only looking for stacks on the default display.
final ActivityDisplay display = mActivityDisplays.get(Display.DEFAULT_DISPLAY);
if (display == null) {
return null;
}
final ArrayList<ActivityStack> stacks = display.mStacks;
for (int i = stacks.size() - 1; i >= 0; i--) {
if (stacks.get(i) == stack && i > 0) {
return stacks.get(i - 1);
}
}
throw new IllegalStateException("Failed to find a stack behind stack=" + stack
+ " in=" + stacks);
}
/**
* Puts a task into resizing mode during the next app transition.
*
* @param taskId the id of the task to put into resizing mode
*/
private void setResizingDuringAnimation(int taskId) {
mResizingTasksDuringAnimation.add(taskId);
mWindowManager.setTaskDockedResizing(taskId, true);
}
final int startActivityFromRecentsInner(int taskId, Bundle bOptions) {
final TaskRecord task;
final int callingUid;
final String callingPackage;
final Intent intent;
final int userId;
final ActivityOptions activityOptions = (bOptions != null)
? new ActivityOptions(bOptions) : null;
final int launchStackId = (activityOptions != null)
? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
if (launchStackId == HOME_STACK_ID) {
throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
+ taskId + " can't be launch in the home stack.");
}
if (launchStackId == DOCKED_STACK_ID) {
mWindowManager.setDockedStackCreateState(
activityOptions.getDockCreateMode(), null /* initialBounds */);
// Defer updating the stack in which recents is until the app transition is done, to
// not run into issues where we still need to draw the task in recents but the
// docked stack is already created.
deferUpdateBounds(HOME_STACK_ID);
mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
}
task = anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
if (task == null) {
continueUpdateBounds(HOME_STACK_ID);
mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
"startActivityFromRecentsInner: Task " + taskId + " not found.");
}
// Since we don't have an actual source record here, we assume that the currently focused
// activity was the source.
final ActivityStack focusedStack = getFocusedStack();
final ActivityRecord sourceRecord =
focusedStack != null ? focusedStack.topActivity() : null;
if (launchStackId != INVALID_STACK_ID) {
if (task.stack.mStackId != launchStackId) {
moveTaskToStackLocked(
taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
ANIMATE);
}
}
// If the user must confirm credentials (e.g. when first launching a work app and the
// Work Challenge is present) let startActivityInPackage handle the intercepting.
if (!mService.mUserController.shouldConfirmCredentials(task.userId)
&& task.getRootActivity() != null) {
mActivityMetricsLogger.notifyActivityLaunching();
mService.moveTaskToFrontLocked(task.taskId, 0, bOptions);
mActivityMetricsLogger.notifyActivityLaunched(ActivityManager.START_TASK_TO_FRONT,
task.getTopActivity());
// If we are launching the task in the docked stack, put it into resizing mode so
// the window renders full-screen with the background filling the void. Also only
// call this at the end to make sure that tasks exists on the window manager side.
if (launchStackId == DOCKED_STACK_ID) {
setResizingDuringAnimation(taskId);
}
mService.mActivityStarter.postStartActivityUncheckedProcessing(task.getTopActivity(),
ActivityManager.START_TASK_TO_FRONT,
sourceRecord != null ? sourceRecord.task.stack.mStackId : INVALID_STACK_ID,
sourceRecord, task.stack);
return ActivityManager.START_TASK_TO_FRONT;
}
callingUid = task.mCallingUid;
callingPackage = task.mCallingPackage;
intent = task.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
userId = task.userId;
int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
null, null, 0, 0, bOptions, userId, null, task);
if (launchStackId == DOCKED_STACK_ID) {
setResizingDuringAnimation(task.taskId);
}
return result;
}
/**
* @return a list of activities which are the top ones in each visible stack. The first
* entry will be the focused activity.
*/
public List<IBinder> getTopVisibleActivities() {
final ActivityDisplay display = mActivityDisplays.get(Display.DEFAULT_DISPLAY);
if (display == null) {
return Collections.EMPTY_LIST;
}
ArrayList<IBinder> topActivityTokens = new ArrayList<>();
final ArrayList<ActivityStack> stacks = display.mStacks;
for (int i = stacks.size() - 1; i >= 0; i--) {
ActivityStack stack = stacks.get(i);
if (stack.getStackVisibilityLocked(null) == ActivityStack.STACK_VISIBLE) {
ActivityRecord top = stack.topActivity();
if (top != null) {
if (stack == mFocusedStack) {
topActivityTokens.add(0, top.appToken);
} else {
topActivityTokens.add(top.appToken);
}
}
}
}
return topActivityTokens;
}
}