Let IME be aware of display override configuration updates

This patch contains three parts:
- Register a listener to corresponding ActivityDisplay to let IME get
  updated display override configuration.
- Isolate IME display override configuration update from general global
  default display configuration update.
- Fix a possible NPE in getProcessGlobalConfiguratoin().

With the patch, IME will keep to get correct process global
configuration of the display it's registered to.

Bug: 113253755
Bug: 116870598
Test: See repro steps in b/111364446#comment10.
Test: go/wm-smoke
Change-Id: I7fb58ed23e58e4696a2fe336806c4bf323891074
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index f79d9aa..4897c05 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -19,7 +19,6 @@
 import static android.Manifest.permission.BIND_VOICE_INTERACTION;
 import static android.Manifest.permission.CHANGE_CONFIGURATION;
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
-import static android.Manifest.permission.FILTER_EVENTS;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
@@ -28,9 +27,9 @@
 import static android.Manifest.permission.STOP_APP_SWITCHES;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.AppOpsManager.OP_NONE;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -71,6 +70,10 @@
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
 
+import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityManagerService.dumpStackTraces;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONTROLLER;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER;
@@ -81,12 +84,21 @@
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HOME_PROC;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto
+        .PREVIOUS_PROC_VISIBLE_TIME_MS;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
+        .MODE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
+        .PACKAGE;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_IMMERSIVE;
@@ -104,19 +116,8 @@
 import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.MY_PID;
-import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
-import static com.android.server.am.ActivityManagerService.dumpStackTraces;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
 import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
@@ -4761,7 +4762,8 @@
         }
 
         for (int i = mPidMap.size() - 1; i >= 0; i--) {
-            WindowProcessController app = mPidMap.get(mPidMap.keyAt(i));
+            final int pid = mPidMap.keyAt(i);
+            final WindowProcessController app = mPidMap.get(pid);
             if (DEBUG_CONFIGURATION) {
                 Slog.v(TAG_CONFIGURATION, "Update process config of "
                         + app.mName + " to new config " + configCopy);
@@ -6015,7 +6017,7 @@
          * @param displayId The ID of the display showing the IME.
          */
         @Override
-        public void onImeWindowSetOnDisplay(int pid, int displayId) {
+        public void onImeWindowSetOnDisplay(final int pid, final int displayId) {
             if (pid == MY_PID || pid < 0) {
                 if (DEBUG_CONFIGURATION) {
                     Slog.w(TAG,
@@ -6025,29 +6027,28 @@
             }
             mH.post(() -> {
                 synchronized (mGlobalLock) {
-                    // Check if display is initialized in AM.
-                    if (!mStackSupervisor.isDisplayAdded(displayId)) {
-                        // Call come when display is not yet added or has already been removed.
+                    final ActivityDisplay activityDisplay =
+                            mStackSupervisor.getActivityDisplay(displayId);
+                    if (activityDisplay == null) {
+                        // Call might come when display is not yet added or has been removed.
                         if (DEBUG_CONFIGURATION) {
                             Slog.w(TAG, "Trying to update display configuration for non-existing "
-                                            + "displayId=" + displayId);
+                                    + "displayId=" + displayId);
                         }
                         return;
                     }
-                    final WindowProcessController imeProcess = mPidMap.get(pid);
-                    if (imeProcess == null) {
+                    final WindowProcessController process = mPidMap.get(pid);
+                    if (process == null) {
                         if (DEBUG_CONFIGURATION) {
-                            Slog.w(TAG, "Trying to update display configuration for invalid pid: "
-                                            + pid);
+                            Slog.w(TAG, "Trying to update display configuration for invalid "
+                                    + "process, pid=" + pid);
                         }
                         return;
                     }
-                    // Fetch the current override configuration of the display and set it to the
-                    // process global configuration.
-                    imeProcess.onConfigurationChanged(
-                            mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+                    process.registerDisplayConfigurationListenerLocked(activityDisplay);
                 }
             });
+
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
index 1743dde..94f1002 100644
--- a/services/core/java/com/android/server/am/WindowProcessController.java
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -17,20 +17,22 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.view.Display.INVALID_DISPLAY;
 
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 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.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.STOPPING;
-import static com.android.server.am.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerService
+        .INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
 import static com.android.server.am.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
 import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
@@ -47,11 +49,12 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
-
 import android.util.proto.ProtoOutputStream;
+
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.wm.ConfigurationContainer;
+import com.android.server.wm.ConfigurationContainerListener;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -67,7 +70,8 @@
  * 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> {
+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;
@@ -147,6 +151,8 @@
 
     // Last configuration that was reported to the process.
     private final Configuration mLastReportedConfiguration;
+    // Registered display id as a listener to override config change
+    private int mDisplayId;
 
     WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info, String name,
             int uid, int userId, Object owner, WindowProcessListener listener,
@@ -159,6 +165,7 @@
         mListener = listener;
         mAtm = atm;
         mLastReportedConfiguration = new Configuration();
+        mDisplayId = INVALID_DISPLAY;
         if (config != null) {
             onConfigurationChanged(config);
         }
@@ -704,6 +711,30 @@
         mListener.appDied();
     }
 
+    void registerDisplayConfigurationListenerLocked(ActivityDisplay activityDisplay) {
+        if (activityDisplay == null) {
+            return;
+        }
+        // A process can only register to one display to listener to the override configuration
+        // change. Unregister existing listener if it has one before register the new one.
+        unregisterDisplayConfigurationListenerLocked();
+        mDisplayId = activityDisplay.mDisplayId;
+        activityDisplay.registerConfigurationChangeListener(this);
+    }
+
+    private void unregisterDisplayConfigurationListenerLocked() {
+        if (mDisplayId == INVALID_DISPLAY) {
+            return;
+        }
+        final ActivityDisplay activityDisplay =
+                mAtm.mStackSupervisor.getActivityDisplay(mDisplayId);
+        if (activityDisplay != null) {
+            mAtm.mStackSupervisor.getActivityDisplay(
+                    mDisplayId).unregisterConfigurationChangeListener(this);
+        }
+        mDisplayId = INVALID_DISPLAY;
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newGlobalConfig) {
         super.onConfigurationChanged(newGlobalConfig);