Protect against assumptions of a top focused stack.
RootWindowContainer#getTopDisplayFocusedStack() has the potential to
return null when there are no focusable stacks. This guards against
many assumptions that a top focused stack exists.
Bug: 144085050
Bug: 148125390
Bug: 150754510
Fixes: 144085050
Test: ActivityStarterTests
Test: ActivityStackTests
Test: ActivityTaskManagerServiceTests
Test: RootWindowContainerTests
Change-Id: Ibefc3a802aba5a90898837359ec59a48f39834dc
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 5ddd7311..cda1e4f 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1470,11 +1470,11 @@
if (resumeNext) {
final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
- if (!topStack.shouldSleepOrShutDownActivities()) {
+ if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {
mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
} else {
checkReadyForSleep();
- ActivityRecord top = topStack.topRunningActivity();
+ final ActivityRecord top = topStack != null ? topStack.topRunningActivity() : null;
if (top == null || (prev != null && top != prev)) {
// If there are no more activities available to run, do resume anyway to start
// something. Also if the top activity on the stack is not the just paused
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3210304..984ae21 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -615,12 +615,15 @@
int res;
synchronized (mService.mGlobalLock) {
- final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
- stack.mConfigWillChange = mRequest.globalConfig != null
+ final boolean globalConfigWillChange = mRequest.globalConfig != null
&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
+ final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
+ if (stack != null) {
+ stack.mConfigWillChange = globalConfigWillChange;
+ }
if (DEBUG_CONFIGURATION) {
Slog.v(TAG_CONFIGURATION, "Starting activity when config will change = "
- + stack.mConfigWillChange);
+ + globalConfigWillChange);
}
final long origId = Binder.clearCallingIdentity();
@@ -633,7 +636,7 @@
Binder.restoreCallingIdentity(origId);
- if (stack.mConfigWillChange) {
+ if (globalConfigWillChange) {
// If the caller also wants to switch to a new configuration, do so now.
// This allows a clean switch, as we are waiting for the current activity
// to pause (so we will not destroy it), and have not yet started the
@@ -641,7 +644,9 @@
mService.mAmInternal.enforceCallingPermission(
android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
- stack.mConfigWillChange = false;
+ if (stack != null) {
+ stack.mConfigWillChange = false;
+ }
if (DEBUG_CONFIGURATION) {
Slog.v(TAG_CONFIGURATION,
"Updating to new configuration after starting activity.");
@@ -1536,9 +1541,11 @@
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
- startResult = deliverToCurrentTopIfNeeded(topStack);
- if (startResult != START_SUCCESS) {
- return startResult;
+ if (topStack != null) {
+ startResult = deliverToCurrentTopIfNeeded(topStack);
+ if (startResult != START_SUCCESS) {
+ return startResult;
+ }
}
if (mTargetStack == null) {
@@ -2126,10 +2133,13 @@
if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
ActivityRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
- checkedCaller = mRootWindowContainer.getTopDisplayFocusedStack()
- .topRunningNonDelayedActivityLocked(mNotTop);
+ ActivityStack topFocusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
+ if (topFocusedStack != null) {
+ checkedCaller = topFocusedStack.topRunningNonDelayedActivityLocked(mNotTop);
+ }
}
- if (!checkedCaller.mActivityComponent.equals(r.mActivityComponent)) {
+ if (checkedCaller == null
+ || !checkedCaller.mActivityComponent.equals(r.mActivityComponent)) {
// Caller is not the same as launcher, so always needed.
mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 770dabf..4688187 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1104,8 +1104,8 @@
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
final ActivityStack stack = getTopDisplayFocusedStack();
- if (stack.mResumedActivity != null &&
- stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
+ if (stack != null && stack.mResumedActivity != null
+ && stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
}
}
@@ -1951,8 +1951,13 @@
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("isTopActivityImmersive");
synchronized (mGlobalLock) {
- final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivity();
- return (r != null) ? r.immersive : false;
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (topFocusedStack == null) {
+ return false;
+ }
+
+ final ActivityRecord r = topFocusedStack.topRunningActivity();
+ return r != null && r.immersive;
}
}
@@ -1981,7 +1986,8 @@
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
synchronized (mGlobalLock) {
- final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivity();
+ final ActivityStack stack = getTopDisplayFocusedStack();
+ final ActivityRecord r = stack != null ? stack.topRunningActivity() : null;
if (r == null) {
return ActivityManager.COMPAT_MODE_UNKNOWN;
}
@@ -1995,7 +2001,8 @@
"setFrontActivityScreenCompatMode");
ApplicationInfo ai;
synchronized (mGlobalLock) {
- final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivity();
+ final ActivityStack stack = getTopDisplayFocusedStack();
+ final ActivityRecord r = stack != null ? stack.topRunningActivity() : null;
if (r == null) {
Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
return;
@@ -2383,7 +2390,10 @@
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- getTopDisplayFocusedStack().unhandledBackLocked();
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (topFocusedStack != null) {
+ topFocusedStack.unhandledBackLocked();
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3622,7 +3632,8 @@
"enqueueAssistContext()");
synchronized (mGlobalLock) {
- ActivityRecord activity = getTopDisplayFocusedStack().getTopNonFinishingActivity();
+ final ActivityStack stack = getTopDisplayFocusedStack();
+ ActivityRecord activity = stack != null ? stack.getTopNonFinishingActivity() : null;
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras failed: no top activity");
return null;
@@ -7043,9 +7054,9 @@
mRootWindowContainer.dumpDisplayConfigs(pw, " ");
}
if (dumpAll) {
- if (dumpPackage == null) {
- pw.println(" mConfigWillChange: "
- + getTopDisplayFocusedStack().mConfigWillChange);
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (dumpPackage == null && topFocusedStack != null) {
+ pw.println(" mConfigWillChange: " + topFocusedStack.mConfigWillChange);
}
if (mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
@@ -7126,7 +7137,10 @@
synchronized (mGlobalLock) {
if (dumpPackage == null) {
getGlobalConfiguration().dumpDebug(proto, GLOBAL_CONFIGURATION);
- proto.write(CONFIG_WILL_CHANGE, getTopDisplayFocusedStack().mConfigWillChange);
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (topFocusedStack != null) {
+ proto.write(CONFIG_WILL_CHANGE, topFocusedStack.mConfigWillChange);
+ }
writeSleepStateToProto(proto, wakeFullness, testPssMode);
if (mRunningVoice != null) {
final long vrToken = proto.start(
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ada5685..74c3044 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1980,7 +1980,9 @@
}
boolean switchUser(int userId, UserState uss) {
- final int focusStackId = getTopDisplayFocusedStack().getRootTaskId();
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ final int focusStackId = topFocusedStack != null
+ ? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
// We dismiss the docked stack whenever we switch users.
final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
if (dockedStack != null) {
@@ -3455,7 +3457,12 @@
ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly,
boolean dumpFocusedStackOnly) {
if (dumpFocusedStackOnly) {
- return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (topFocusedStack != null) {
+ return topFocusedStack.getDumpActivitiesLocked(name);
+ } else {
+ return new ArrayList<>();
+ }
} else {
ArrayList<ActivityRecord> activities = new ArrayList<>();
int numDisplays = getChildCount();