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();