blob: fe68cd6110f290e51d265d1a629ae25797395aae [file] [log] [blame]
/*
* Copyright (C) 2018 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.wm;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.IApplicationThread;
import android.app.ProfilerInfo;
import android.app.servertransaction.ConfigurationChangeItem;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.IRemoteAnimationRunner;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.Watchdog;
import com.android.server.wm.ActivityTaskManagerService.HotPath;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
* The Activity Manager (AM) package manages the lifecycle of processes in the system through
* ProcessRecord. However, it is important for the Window Manager (WM) package to be aware
* of the processes and their state since it affects how WM manages windows and activities. This
* class that allows the ProcessRecord object in the AM package to communicate important
* changes to its state to the WM package in a structured way. WM package also uses
* {@link WindowProcessListener} to request changes to the process state on the AM side.
* Note that public calls into this class are assumed to be originating from outside the
* window manager so the window manager lock is held and appropriate permissions are checked before
* calls are allowed to proceed.
*/
public class WindowProcessController extends ConfigurationContainer<ConfigurationContainer>
implements ConfigurationContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_ATM;
private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
// all about the first app in the process
final ApplicationInfo mInfo;
final String mName;
final int mUid;
// The process of this application; 0 if none
private volatile int mPid;
// user of process.
final int mUserId;
// The owner of this window process controller object. Mainly for identification when we
// communicate back to the activity manager side.
public final Object mOwner;
// List of packages running in the process
final ArraySet<String> mPkgList = new ArraySet<>();
private final WindowProcessListener mListener;
private final ActivityTaskManagerService mAtm;
// The actual proc... may be null only if 'persistent' is true (in which case we are in the
// process of launching the app)
private IApplicationThread mThread;
// Currently desired scheduling class
private volatile int mCurSchedGroup;
// Currently computed process state
private volatile int mCurProcState = PROCESS_STATE_NONEXISTENT;
// Last reported process state;
private volatile int mRepProcState = PROCESS_STATE_NONEXISTENT;
// are we in the process of crashing?
private volatile boolean mCrashing;
// does the app have a not responding dialog?
private volatile boolean mNotResponding;
// always keep this application running?
private volatile boolean mPersistent;
// The ABI this process was launched with
private volatile String mRequiredAbi;
// Running any services that are foreground?
private volatile boolean mHasForegroundServices;
// Running any activities that are foreground?
private volatile boolean mHasForegroundActivities;
// Are there any client services with activities?
private volatile boolean mHasClientActivities;
// Is this process currently showing a non-activity UI that the user is interacting with?
// E.g. The status bar when it is expanded, but not when it is minimized. When true the process
// will be set to use the ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost performance.
private volatile boolean mHasTopUi;
// Is the process currently showing a non-activity UI that overlays on-top of activity UIs on
// screen. E.g. display a window of type
// android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY When true the process will
// oom adj score will be set to ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance
// of the process getting killed.
private volatile boolean mHasOverlayUi;
// Want to clean up resources from showing UI?
private volatile boolean mPendingUiClean;
// The time we sent the last interaction event
private volatile long mInteractionEventTime;
// When we became foreground for interaction purposes
private volatile long mFgInteractionTime;
// When (uptime) the process last became unimportant
private volatile long mWhenUnimportant;
// was app launched for debugging?
private volatile boolean mDebugging;
// Active instrumentation running in process?
private volatile boolean mInstrumenting;
// Active instrumentation with background activity starts privilege running in process?
private volatile boolean mInstrumentingWithBackgroundActivityStartPrivileges;
// This process it perceptible by the user.
private volatile boolean mPerceptible;
// Set to true when process was launched with a wrapper attached
private volatile boolean mUsingWrapper;
// Set to true if this process is currently temporarily whitelisted to start activities even if
// it's not in the foreground
private volatile boolean mAllowBackgroundActivityStarts;
// Set of UIDs of clients currently bound to this process
private volatile ArraySet<Integer> mBoundClientUids = new ArraySet<Integer>();
// Thread currently set for VR scheduling
int mVrThreadTid;
// Whether this process has ever started a service with the BIND_INPUT_METHOD permission.
private volatile boolean mHasImeService;
// all activities running in the process
private final ArrayList<ActivityRecord> mActivities = new ArrayList<>();
// any tasks this process had run root activities in
private final ArrayList<Task> mRecentTasks = new ArrayList<>();
// The most recent top-most activity that was resumed in the process for pre-Q app.
private ActivityRecord mPreQTopResumedActivity = null;
// The last time an activity was launched in the process
private long mLastActivityLaunchTime;
// The last time an activity was finished in the process while the process participated
// in a visible task
private long mLastActivityFinishTime;
// Last configuration that was reported to the process.
private final Configuration mLastReportedConfiguration = new Configuration();
// Configuration that is waiting to be dispatched to the process.
private Configuration mPendingConfiguration;
private final Configuration mNewOverrideConfig = new Configuration();
// Registered display id as a listener to override config change
private int mDisplayId;
private ActivityRecord mConfigActivityRecord;
// Whether the activity config override is allowed for this process.
private volatile boolean mIsActivityConfigOverrideAllowed = true;
/**
* Activities that hosts some UI drawn by the current process. The activities live
* in another process. This is used to check if the process is currently showing anything
* visible to the user.
*/
@Nullable
private final ArrayList<ActivityRecord> mHostActivities = new ArrayList<>();
/** Whether our process is currently running a {@link RecentsAnimation} */
private boolean mRunningRecentsAnimation;
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
private boolean mRunningRemoteAnimation;
public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
String name, int uid, int userId, Object owner, WindowProcessListener listener) {
mInfo = info;
mName = name;
mUid = uid;
mUserId = userId;
mOwner = owner;
mListener = listener;
mAtm = atm;
mDisplayId = INVALID_DISPLAY;
boolean isSysUiPackage = info.packageName.equals(
mAtm.getSysUiServiceComponentLocked().getPackageName());
if (isSysUiPackage || mUid == Process.SYSTEM_UID) {
// This is a system owned process and should not use an activity config.
// TODO(b/151161907): Remove after support for display-independent (raw) SysUi configs.
mIsActivityConfigOverrideAllowed = false;
}
onConfigurationChanged(atm.getGlobalConfiguration());
}
public void setPid(int pid) {
mPid = pid;
}
public int getPid() {
return mPid;
}
@HotPath(caller = HotPath.PROCESS_CHANGE)
public void setThread(IApplicationThread thread) {
synchronized (mAtm.mGlobalLockWithoutBoost) {
mThread = thread;
// In general this is called from attaching application, so the last configuration
// has been sent to client by {@link android.app.IApplicationThread#bindApplication}.
// If this process is system server, it is fine because system is booting and a new
// configuration will update when display is ready.
if (thread != null) {
setLastReportedConfiguration(getConfiguration());
}
}
}
IApplicationThread getThread() {
return mThread;
}
boolean hasThread() {
return mThread != null;
}
public void setCurrentSchedulingGroup(int curSchedGroup) {
mCurSchedGroup = curSchedGroup;
}
int getCurrentSchedulingGroup() {
return mCurSchedGroup;
}
public void setCurrentProcState(int curProcState) {
mCurProcState = curProcState;
}
int getCurrentProcState() {
return mCurProcState;
}
public void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
}
int getReportedProcState() {
return mRepProcState;
}
public void setCrashing(boolean crashing) {
mCrashing = crashing;
}
boolean isCrashing() {
return mCrashing;
}
public void setNotResponding(boolean notResponding) {
mNotResponding = notResponding;
}
boolean isNotResponding() {
return mNotResponding;
}
public void setPersistent(boolean persistent) {
mPersistent = persistent;
}
boolean isPersistent() {
return mPersistent;
}
public void setHasForegroundServices(boolean hasForegroundServices) {
mHasForegroundServices = hasForegroundServices;
}
boolean hasForegroundServices() {
return mHasForegroundServices;
}
public void setHasForegroundActivities(boolean hasForegroundActivities) {
mHasForegroundActivities = hasForegroundActivities;
}
boolean hasForegroundActivities() {
return mHasForegroundActivities;
}
public void setHasClientActivities(boolean hasClientActivities) {
mHasClientActivities = hasClientActivities;
}
boolean hasClientActivities() {
return mHasClientActivities;
}
public void setHasTopUi(boolean hasTopUi) {
mHasTopUi = hasTopUi;
}
boolean hasTopUi() {
return mHasTopUi;
}
public void setHasOverlayUi(boolean hasOverlayUi) {
mHasOverlayUi = hasOverlayUi;
}
boolean hasOverlayUi() {
return mHasOverlayUi;
}
public void setPendingUiClean(boolean hasPendingUiClean) {
mPendingUiClean = hasPendingUiClean;
}
boolean hasPendingUiClean() {
return mPendingUiClean;
}
/** @return {@code true} if the process registered to a display as a config listener. */
boolean registeredForDisplayConfigChanges() {
return mDisplayId != INVALID_DISPLAY;
}
/** @return {@code true} if the process registered to an activity as a config listener. */
@VisibleForTesting
boolean registeredForActivityConfigChanges() {
return mConfigActivityRecord != null;
}
void postPendingUiCleanMsg(boolean pendingUiClean) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
final Message m = PooledLambda.obtainMessage(
WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
mAtm.mH.sendMessage(m);
}
public void setInteractionEventTime(long interactionEventTime) {
mInteractionEventTime = interactionEventTime;
}
long getInteractionEventTime() {
return mInteractionEventTime;
}
public void setFgInteractionTime(long fgInteractionTime) {
mFgInteractionTime = fgInteractionTime;
}
long getFgInteractionTime() {
return mFgInteractionTime;
}
public void setWhenUnimportant(long whenUnimportant) {
mWhenUnimportant = whenUnimportant;
}
long getWhenUnimportant() {
return mWhenUnimportant;
}
public void setRequiredAbi(String requiredAbi) {
mRequiredAbi = requiredAbi;
}
String getRequiredAbi() {
return mRequiredAbi;
}
/** Returns ID of display overriding the configuration for this process, or
* INVALID_DISPLAY if no display is overriding. */
@VisibleForTesting
int getDisplayId() {
return mDisplayId;
}
public void setDebugging(boolean debugging) {
mDebugging = debugging;
}
boolean isDebugging() {
return mDebugging;
}
public void setUsingWrapper(boolean usingWrapper) {
mUsingWrapper = usingWrapper;
}
boolean isUsingWrapper() {
return mUsingWrapper;
}
void setLastActivityLaunchTime(long launchTime) {
if (launchTime <= mLastActivityLaunchTime) {
if (launchTime < mLastActivityLaunchTime) {
Slog.w(TAG,
"Tried to set launchTime (" + launchTime + ") < mLastActivityLaunchTime ("
+ mLastActivityLaunchTime + ")");
}
return;
}
mLastActivityLaunchTime = launchTime;
}
void setLastActivityFinishTimeIfNeeded(long finishTime) {
if (finishTime <= mLastActivityFinishTime || !hasActivityInVisibleTask()) {
return;
}
mLastActivityFinishTime = finishTime;
}
public void setAllowBackgroundActivityStarts(boolean allowBackgroundActivityStarts) {
mAllowBackgroundActivityStarts = allowBackgroundActivityStarts;
}
boolean areBackgroundActivityStartsAllowed() {
// allow if the whitelisting flag was explicitly set
if (mAllowBackgroundActivityStarts) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid
+ ")] Activity start allowed: mAllowBackgroundActivityStarts = true");
}
return true;
}
// allow if any activity in the caller has either started or finished very recently, and
// it must be started or finished after last stop app switches time.
final long now = SystemClock.uptimeMillis();
if (now - mLastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
|| now - mLastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
// if activity is started and finished before stop app switch time, we should not
// let app to be able to start background activity even it's in grace period.
if (mLastActivityLaunchTime > mAtm.getLastStopAppSwitchesTime()
|| mLastActivityFinishTime > mAtm.getLastStopAppSwitchesTime()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid
+ ")] Activity start allowed: within "
+ ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
}
return true;
}
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid + ")] Activity start within "
+ ACTIVITY_BG_START_GRACE_PERIOD_MS
+ "ms grace period but also within stop app switch window");
}
}
// allow if the proc is instrumenting with background activity starts privs
if (mInstrumentingWithBackgroundActivityStartPrivileges) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid
+ ")] Activity start allowed: process instrumenting with background "
+ "activity starts privileges");
}
return true;
}
// allow if the caller has an activity in any foreground task
if (hasActivityInVisibleTask()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid
+ ")] Activity start allowed: process has activity in foreground task");
}
return true;
}
// allow if the caller is bound by a UID that's currently foreground
if (isBoundByForegroundUid()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid
+ ")] Activity start allowed: process bound by foreground uid");
}
return true;
}
return false;
}
private boolean isBoundByForegroundUid() {
for (int i = mBoundClientUids.size() - 1; i >= 0; --i) {
if (mAtm.isUidForeground(mBoundClientUids.valueAt(i))) {
return true;
}
}
return false;
}
public void setBoundClientUids(ArraySet<Integer> boundClientUids) {
mBoundClientUids = boundClientUids;
}
public void setInstrumenting(boolean instrumenting,
boolean hasBackgroundActivityStartPrivileges) {
mInstrumenting = instrumenting;
mInstrumentingWithBackgroundActivityStartPrivileges = hasBackgroundActivityStartPrivileges;
}
boolean isInstrumenting() {
return mInstrumenting;
}
public void setPerceptible(boolean perceptible) {
mPerceptible = perceptible;
}
boolean isPerceptible() {
return mPerceptible;
}
@Override
protected int getChildCount() {
return 0;
}
@Override
protected ConfigurationContainer getChildAt(int index) {
return null;
}
@Override
protected ConfigurationContainer getParent() {
// Returning RootWindowContainer as the parent, so that this process controller always
// has full configuration and overrides (e.g. from display) are always added on top of
// global config.
return mAtm.mRootWindowContainer;
}
@HotPath(caller = HotPath.PROCESS_CHANGE)
public void addPackage(String packageName) {
synchronized (mAtm.mGlobalLockWithoutBoost) {
mPkgList.add(packageName);
}
}
@HotPath(caller = HotPath.PROCESS_CHANGE)
public void clearPackageList() {
synchronized (mAtm.mGlobalLockWithoutBoost) {
mPkgList.clear();
}
}
void addActivityIfNeeded(ActivityRecord r) {
// even if we already track this activity, note down that it has been launched
setLastActivityLaunchTime(r.lastLaunchTime);
if (mActivities.contains(r)) {
return;
}
mActivities.add(r);
updateActivityConfigurationListener();
}
void removeActivity(ActivityRecord r) {
mActivities.remove(r);
updateActivityConfigurationListener();
}
void makeFinishingForProcessRemoved() {
for (int i = mActivities.size() - 1; i >= 0; --i) {
mActivities.get(i).makeFinishingLocked();
}
}
void clearActivities() {
mActivities.clear();
updateActivityConfigurationListener();
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean hasActivities() {
synchronized (mAtm.mGlobalLockWithoutBoost) {
return !mActivities.isEmpty();
}
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean hasVisibleActivities() {
synchronized (mAtm.mGlobalLockWithoutBoost) {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
if (r.mVisibleRequested) {
return true;
}
}
}
return false;
}
@HotPath(caller = HotPath.LRU_UPDATE)
public boolean hasActivitiesOrRecentTasks() {
synchronized (mAtm.mGlobalLockWithoutBoost) {
return !mActivities.isEmpty() || !mRecentTasks.isEmpty();
}
}
private boolean hasActivityInVisibleTask() {
for (int i = mActivities.size() - 1; i >= 0; --i) {
Task task = mActivities.get(i).getTask();
if (task == null) {
continue;
}
ActivityRecord topActivity = task.getTopNonFinishingActivity();
if (topActivity != null && topActivity.mVisibleRequested) {
return true;
}
}
return false;
}
/**
* Update the top resuming activity in process for pre-Q apps, only the top-most visible
* activities are allowed to be resumed per process.
* @return {@code true} if the activity is allowed to be resumed by compatibility
* restrictions, which the activity was the topmost visible activity in process or the app is
* targeting after Q. Note that non-focusable activity, in picture-in-picture mode for instance,
* does not count as a topmost activity.
*/
boolean updateTopResumingActivityInProcessIfNeeded(@NonNull ActivityRecord activity) {
if (mInfo.targetSdkVersion >= Q || mPreQTopResumedActivity == activity) {
return true;
}
final DisplayContent display = activity.getDisplay();
if (display == null) {
// No need to update if the activity hasn't attach to any display.
return false;
}
boolean canUpdate = false;
final DisplayContent topDisplay =
mPreQTopResumedActivity != null ? mPreQTopResumedActivity.getDisplay() : null;
// Update the topmost activity if current top activity is
// - not on any display OR
// - no longer visible OR
// - not focusable (in PiP mode for instance)
if (topDisplay == null
|| !mPreQTopResumedActivity.mVisibleRequested
|| !mPreQTopResumedActivity.isFocusable()) {
canUpdate = true;
}
// Update the topmost activity if the current top activity wasn't on top of the other one.
if (!canUpdate && topDisplay.mDisplayContent.compareTo(display.mDisplayContent) < 0) {
canUpdate = true;
}
// Compare the z-order of ActivityStacks if both activities landed on same display.
if (display == topDisplay
&& mPreQTopResumedActivity.getRootTask().compareTo(
activity.getRootTask()) <= 0) {
canUpdate = true;
}
if (canUpdate) {
// Make sure the previous top activity in the process no longer be resumed.
if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
final ActivityStack stack = mPreQTopResumedActivity.getRootTask();
if (stack != null) {
stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
activity);
}
}
mPreQTopResumedActivity = activity;
}
return canUpdate;
}
public void stopFreezingActivities() {
synchronized (mAtm.mGlobalLock) {
int i = mActivities.size();
while (i > 0) {
i--;
mActivities.get(i).stopFreezingScreenLocked(true);
}
}
}
void finishActivities() {
ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
for (int i = 0; i < activities.size(); i++) {
final ActivityRecord r = activities.get(i);
if (!r.finishing && r.isInStackLocked()) {
r.finishIfPossible("finish-heavy", true /* oomAdj */);
}
}
}
public boolean isInterestingToUser() {
synchronized (mAtm.mGlobalLock) {
final int size = mActivities.size();
for (int i = 0; i < size; i++) {
ActivityRecord r = mActivities.get(i);
if (r.isInterestingToUserLocked()) {
return true;
}
}
if (isEmbedded()) {
return true;
}
}
return false;
}
/**
* @return {@code true} if this process is rendering content on to a window shown by
* another process.
*/
private boolean isEmbedded() {
for (int i = mHostActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mHostActivities.get(i);
if (r.isInterestingToUserLocked()) {
return true;
}
}
return false;
}
public boolean hasRunningActivity(String packageName) {
synchronized (mAtm.mGlobalLock) {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
if (packageName.equals(r.packageName)) {
return true;
}
}
}
return false;
}
public void clearPackagePreferredForHomeActivities() {
synchronized (mAtm.mGlobalLock) {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
if (r.isActivityTypeHome()) {
Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
try {
ActivityThread.getPackageManager()
.clearPackagePreferredActivities(r.packageName);
} catch (RemoteException c) {
// pm is in same process, this will never happen.
}
}
}
}
}
boolean hasStartedActivity(ActivityRecord launchedActivity) {
for (int i = mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord activity = mActivities.get(i);
if (launchedActivity == activity) {
continue;
}
if (!activity.stopped) {
return true;
}
}
return false;
}
boolean hasResumedActivity() {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord activity = mActivities.get(i);
if (activity.isState(RESUMED)) {
return true;
}
}
return false;
}
void updateIntentForHeavyWeightActivity(Intent intent) {
if (mActivities.isEmpty()) {
return;
}
ActivityRecord hist = mActivities.get(0);
intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().mTaskId);
}
boolean shouldKillProcessForRemovedTask(Task task) {
for (int k = 0; k < mActivities.size(); k++) {
final ActivityRecord activity = mActivities.get(k);
if (!activity.stopped) {
// Don't kill process(es) that has an activity not stopped.
return false;
}
final Task otherTask = activity.getTask();
if (task.mTaskId != otherTask.mTaskId && otherTask.inRecents) {
// Don't kill process(es) that has an activity in a different task that is
// also in recents.
return false;
}
}
return true;
}
void releaseSomeActivities(String reason) {
// Examine all activities currently running in the process.
// Candidate activities that can be destroyed.
ArrayList<ActivityRecord> candidates = null;
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + this);
for (int i = 0; i < mActivities.size(); i++) {
final ActivityRecord r = mActivities.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.isState(DESTROYING, DESTROYED)) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
return;
}
// Don't consider any activities that are currently not in a state where they
// can be destroyed.
if (r.mVisibleRequested || !r.stopped || !r.hasSavedState() || !r.isDestroyable()
|| r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
continue;
}
if (r.getParent() != null) {
if (candidates == null) {
candidates = new ArrayList<>();
}
candidates.add(r);
}
}
if (candidates != null) {
// Sort based on z-order in hierarchy.
candidates.sort(WindowContainer::compareTo);
// Release some older activities
int maxRelease = Math.max(candidates.size(), 1);
do {
final ActivityRecord r = candidates.remove(0);
if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + r
+ " in state " + r.getState() + " for reason " + reason);
r.destroyImmediately(true /*removeFromApp*/, reason);
--maxRelease;
} while (maxRelease > 0);
}
}
/**
* Returns display UI context list which there is any app window shows or starting activities
* int this process.
*/
public void getDisplayContextsWithErrorDialogs(List<Context> displayContexts) {
if (displayContexts == null) {
return;
}
synchronized (mAtm.mGlobalLock) {
final RootWindowContainer root = mAtm.mWindowManager.mRoot;
root.getDisplayContextsWithNonToastVisibleWindows(mPid, displayContexts);
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
final int displayId = r.getDisplayId();
final Context c = root.getDisplayUiContext(displayId);
if (r.mVisibleRequested && !displayContexts.contains(c)) {
displayContexts.add(c);
}
}
}
}
/** Adds an activity that hosts UI drawn by the current process. */
void addHostActivity(ActivityRecord r) {
if (mHostActivities.contains(r)) {
return;
}
mHostActivities.add(r);
}
/** Removes an activity that hosts UI drawn by the current process. */
void removeHostActivity(ActivityRecord r) {
mHostActivities.remove(r);
}
public interface ComputeOomAdjCallback {
void onVisibleActivity();
void onPausedActivity();
void onStoppingActivity(boolean finishing);
void onOtherActivity();
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
// "best" state, the order would be visible, pausing, stopping...
ActivityStack.ActivityState best = DESTROYED;
boolean finishing = true;
boolean visible = false;
synchronized (mAtm.mGlobalLockWithoutBoost) {
final int activitiesSize = mActivities.size();
for (int j = 0; j < activitiesSize; j++) {
final ActivityRecord r = mActivities.get(j);
if (r.app != this) {
Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
+ " instead of expected " + this);
if (r.app == null || (r.app.mUid == mUid)) {
// Only fix things up when they look sane
r.setProcess(this);
} else {
continue;
}
}
if (r.mVisibleRequested) {
final Task task = r.getTask();
if (task != null && minTaskLayer > 0) {
final int layer = task.mLayerRank;
if (layer >= 0 && minTaskLayer > layer) {
minTaskLayer = layer;
}
}
visible = true;
// continue the loop, in case there are multiple visible activities in
// this process, we'd find out the one with the minimal layer, thus it'll
// get a higher adj score.
} else {
if (best != PAUSING && best != PAUSED) {
if (r.isState(PAUSING, PAUSED)) {
best = PAUSING;
} else if (r.isState(STOPPING)) {
best = STOPPING;
// Not "finishing" if any of activity isn't finishing.
finishing &= r.finishing;
}
}
}
}
}
if (visible) {
callback.onVisibleActivity();
} else if (best == PAUSING) {
callback.onPausedActivity();
} else if (best == STOPPING) {
callback.onStoppingActivity(finishing);
} else {
callback.onOtherActivity();
}
return minTaskLayer;
}
public int computeRelaunchReason() {
synchronized (mAtm.mGlobalLock) {
final int activitiesSize = mActivities.size();
for (int i = activitiesSize - 1; i >= 0; i--) {
final ActivityRecord r = mActivities.get(i);
if (r.mRelaunchReason != RELAUNCH_REASON_NONE) {
return r.mRelaunchReason;
}
}
}
return RELAUNCH_REASON_NONE;
}
public long getInputDispatchingTimeout() {
synchronized (mAtm.mGlobalLock) {
return isInstrumenting() || isUsingWrapper()
? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS;
}
}
void clearProfilerIfNeeded() {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
WindowProcessListener::clearProfilerIfNeeded, mListener));
}
void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange,
boolean updateOomAdj) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
final Message m = PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo,
mListener, updateServiceConnectionActivities, activityChange, updateOomAdj);
mAtm.mH.sendMessage(m);
}
void updateServiceConnectionActivities() {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
WindowProcessListener::updateServiceConnectionActivities, mListener));
}
void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
final Message m = PooledLambda.obtainMessage(
WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo,
mListener, newState);
mAtm.mH.sendMessage(m);
}
boolean isRemoved() {
return mListener == null ? false : mListener.isRemoved();
}
private boolean shouldSetProfileProc() {
return mAtm.mProfileApp != null && mAtm.mProfileApp.equals(mName)
&& (mAtm.mProfileProc == null || mAtm.mProfileProc == this);
}
ProfilerInfo createProfilerInfoIfNeeded() {
final ProfilerInfo currentProfilerInfo = mAtm.mProfilerInfo;
if (currentProfilerInfo == null || currentProfilerInfo.profileFile == null
|| !shouldSetProfileProc()) {
return null;
}
if (currentProfilerInfo.profileFd != null) {
try {
currentProfilerInfo.profileFd = currentProfilerInfo.profileFd.dup();
} catch (IOException e) {
currentProfilerInfo.closeFd();
}
}
return new ProfilerInfo(currentProfilerInfo);
}
void onStartActivity(int topProcessState, ActivityInfo info) {
if (mListener == null) return;
String packageName = null;
if ((info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(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.
packageName = info.packageName;
}
// Posting the message at the front of queue so WM lock isn't held when we call into AM,
// and the process state of starting activity can be updated quicker which will give it a
// higher scheduling group.
final Message m = PooledLambda.obtainMessage(WindowProcessListener::onStartActivity,
mListener, topProcessState, shouldSetProfileProc(), packageName,
info.applicationInfo.longVersionCode);
mAtm.mH.sendMessageAtFrontOfQueue(m);
}
void appDied(String reason) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
final Message m = PooledLambda.obtainMessage(
WindowProcessListener::appDied, mListener, reason);
mAtm.mH.sendMessage(m);
}
void registerDisplayConfigurationListener(DisplayContent displayContent) {
if (displayContent == null) {
return;
}
// A process can only register to one display to listen to the override configuration
// change. Unregister existing listener if it has one before register the new one.
unregisterDisplayConfigurationListener();
unregisterActivityConfigurationListener();
mDisplayId = displayContent.mDisplayId;
displayContent.registerConfigurationChangeListener(this);
}
@VisibleForTesting
void unregisterDisplayConfigurationListener() {
if (mDisplayId == INVALID_DISPLAY) {
return;
}
final DisplayContent displayContent =
mAtm.mRootWindowContainer.getDisplayContent(mDisplayId);
if (displayContent != null) {
displayContent.unregisterConfigurationChangeListener(this);
}
mDisplayId = INVALID_DISPLAY;
onMergedOverrideConfigurationChanged(Configuration.EMPTY);
}
private void registerActivityConfigurationListener(ActivityRecord activityRecord) {
if (activityRecord == null || activityRecord.containsListener(this)) {
return;
}
// A process can only register to one activityRecord to listen to the override configuration
// change. Unregister existing listener if it has one before register the new one.
unregisterDisplayConfigurationListener();
unregisterActivityConfigurationListener();
mConfigActivityRecord = activityRecord;
activityRecord.registerConfigurationChangeListener(this);
}
private void unregisterActivityConfigurationListener() {
if (mConfigActivityRecord == null) {
return;
}
mConfigActivityRecord.unregisterConfigurationChangeListener(this);
mConfigActivityRecord = null;
onMergedOverrideConfigurationChanged(Configuration.EMPTY);
}
/**
* Check if activity configuration override for the activity process needs an update and perform
* if needed. By default we try to override the process configuration to match the top activity
* config to increase app compatibility with multi-window and multi-display. The process will
* always track the configuration of the non-finishing activity last added to the process.
*/
private void updateActivityConfigurationListener() {
if (!mIsActivityConfigOverrideAllowed) {
return;
}
for (int i = mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord activityRecord = mActivities.get(i);
if (!activityRecord.finishing) {
// Eligible activity is found, update listener.
registerActivityConfigurationListener(activityRecord);
return;
}
}
// No eligible activities found, let's remove the configuration listener.
unregisterActivityConfigurationListener();
}
@Override
public void onConfigurationChanged(Configuration newGlobalConfig) {
super.onConfigurationChanged(newGlobalConfig);
updateConfiguration();
}
@Override
public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
// Make sure that we don't accidentally override the activity type.
mNewOverrideConfig.setTo(mergedOverrideConfig);
mNewOverrideConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
super.onRequestedOverrideConfigurationChanged(mNewOverrideConfig);
}
private void updateConfiguration() {
final Configuration config = getConfiguration();
if (mLastReportedConfiguration.diff(config) == 0) {
// Nothing changed.
if (Build.IS_DEBUGGABLE && mHasImeService) {
// TODO (b/135719017): Temporary log for debugging IME service.
Slog.w(TAG_CONFIGURATION, "Current config: " + config
+ " unchanged for IME proc " + mName);
}
return;
}
if (mListener.isCached()) {
// This process is in a cached state. We will delay delivering the config change to the
// process until the process is no longer cached.
if (mPendingConfiguration == null) {
mPendingConfiguration = new Configuration(config);
} else {
mPendingConfiguration.setTo(config);
}
return;
}
dispatchConfigurationChange(config);
}
private void dispatchConfigurationChange(Configuration config) {
if (mThread == null) {
if (Build.IS_DEBUGGABLE && mHasImeService) {
// TODO (b/135719017): Temporary log for debugging IME service.
Slog.w(TAG_CONFIGURATION, "Unable to send config for IME proc " + mName
+ ": no app thread");
}
return;
}
if (DEBUG_CONFIGURATION) {
Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName + " new config " + config);
}
if (Build.IS_DEBUGGABLE && mHasImeService) {
// TODO (b/135719017): Temporary log for debugging IME service.
Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config);
}
try {
config.seq = mAtm.increaseConfigurationSeqLocked();
mAtm.getLifecycleManager().scheduleTransaction(mThread,
ConfigurationChangeItem.obtain(config));
setLastReportedConfiguration(config);
} catch (Exception e) {
Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
}
}
private void setLastReportedConfiguration(Configuration config) {
mLastReportedConfiguration.setTo(config);
}
Configuration getLastReportedConfiguration() {
return mLastReportedConfiguration;
}
/** Returns the total time (in milliseconds) spent executing in both user and system code. */
public long getCpuTime() {
return (mListener != null) ? mListener.getCpuTime() : 0;
}
void addRecentTask(Task task) {
mRecentTasks.add(task);
}
void removeRecentTask(Task task) {
mRecentTasks.remove(task);
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean hasRecentTasks() {
synchronized (mAtm.mGlobalLockWithoutBoost) {
return !mRecentTasks.isEmpty();
}
}
void clearRecentTasks() {
for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
mRecentTasks.get(i).clearRootProcess();
}
mRecentTasks.clear();
}
public void appEarlyNotResponding(String annotation, Runnable killAppCallback) {
Runnable targetRunnable = null;
synchronized (mAtm.mGlobalLock) {
if (mAtm.mController == null) {
return;
}
try {
// 0 == continue, -1 = kill process immediately
int res = mAtm.mController.appEarlyNotResponding(mName, mPid, annotation);
if (res < 0 && mPid != MY_PID) {
targetRunnable = killAppCallback;
}
} catch (RemoteException e) {
mAtm.mController = null;
Watchdog.getInstance().setActivityController(null);
}
}
if (targetRunnable != null) {
targetRunnable.run();
}
}
public boolean appNotResponding(String info, Runnable killAppCallback,
Runnable serviceTimeoutCallback) {
Runnable targetRunnable = null;
synchronized (mAtm.mGlobalLock) {
if (mAtm.mController == null) {
return false;
}
try {
// 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
int res = mAtm.mController.appNotResponding(mName, mPid, info);
if (res != 0) {
if (res < 0 && mPid != MY_PID) {
targetRunnable = killAppCallback;
} else {
targetRunnable = serviceTimeoutCallback;
}
}
} catch (RemoteException e) {
mAtm.mController = null;
Watchdog.getInstance().setActivityController(null);
return false;
}
}
if (targetRunnable != null) {
// Execute runnable outside WM lock since the runnable will hold AM lock
targetRunnable.run();
return true;
}
return false;
}
/**
* Called to notify WindowProcessController of a change in the process's cached state.
*
* @param isCached whether or not the process is cached.
*/
public void onProcCachedStateChanged(boolean isCached) {
synchronized (mAtm.mGlobalLock) {
if (!isCached && mPendingConfiguration != null) {
final Configuration config = mPendingConfiguration;
mPendingConfiguration = null;
dispatchConfigurationChange(config);
}
}
}
/**
* Called to notify {@link WindowProcessController} of a started service.
*
* @param serviceInfo information describing the started service.
*/
public void onServiceStarted(ServiceInfo serviceInfo) {
String permission = serviceInfo.permission;
if (permission == null) {
return;
}
// TODO: Audit remaining services for disabling activity override (Wallpaper, Dream, etc).
switch (permission) {
case Manifest.permission.BIND_INPUT_METHOD:
mHasImeService = true;
// Fall-through
case Manifest.permission.BIND_ACCESSIBILITY_SERVICE:
case Manifest.permission.BIND_VOICE_INTERACTION:
// We want to avoid overriding the config of these services with that of the
// activity as it could lead to incorrect display metrics. For ex, IME services
// expect their config to match the config of the display with the IME window
// showing.
mIsActivityConfigOverrideAllowed = false;
break;
default:
break;
}
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public void onTopProcChanged() {
synchronized (mAtm.mGlobalLockWithoutBoost) {
mAtm.mVrController.onTopProcChangedLocked(this);
}
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean isHomeProcess() {
synchronized (mAtm.mGlobalLockWithoutBoost) {
return this == mAtm.mHomeProcess;
}
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean isPreviousProcess() {
synchronized (mAtm.mGlobalLockWithoutBoost) {
return this == mAtm.mPreviousProcess;
}
}
void setRunningRecentsAnimation(boolean running) {
if (mRunningRecentsAnimation == running) {
return;
}
mRunningRecentsAnimation = running;
updateRunningRemoteOrRecentsAnimation();
}
void setRunningRemoteAnimation(boolean running) {
if (mRunningRemoteAnimation == running) {
return;
}
mRunningRemoteAnimation = running;
updateRunningRemoteOrRecentsAnimation();
}
private void updateRunningRemoteOrRecentsAnimation() {
// Posting on handler so WM lock isn't held when we call into AM.
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
WindowProcessListener::setRunningRemoteAnimation, mListener,
mRunningRecentsAnimation || mRunningRemoteAnimation));
}
@Override
public String toString() {
return mOwner != null ? mOwner.toString() : null;
}
public void dump(PrintWriter pw, String prefix) {
synchronized (mAtm.mGlobalLock) {
if (mActivities.size() > 0) {
pw.print(prefix); pw.println("Activities:");
for (int i = 0; i < mActivities.size(); i++) {
pw.print(prefix); pw.print(" - "); pw.println(mActivities.get(i));
}
}
if (mRecentTasks.size() > 0) {
pw.println(prefix + "Recent Tasks:");
for (int i = 0; i < mRecentTasks.size(); i++) {
pw.println(prefix + " - " + mRecentTasks.get(i));
}
}
if (mVrThreadTid != 0) {
pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
}
}
pw.println(prefix + " Configuration=" + getConfiguration());
pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration());
pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration);
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
if (mListener != null) {
mListener.dumpDebug(proto, fieldId);
}
}
}