Delay tasks from IMMS constructor to later phase

This is	a preparation CL for Bug 32343335.

This CL basically addresses the concern raised in a previous CL [1]
that added several synchnonized blocks in the constructor of
InputMethodManagerService (IMMS).

After hours of code reading and investigation, it is concluded to be
safe IMMS to behave as if there was no IME available until
SystemService.PHASE_ACTIVITY_MANAGER_READY phase. This allows IMMS to
register callbacks not in the constructor but in the later phase to
addresses the concern that object references to IMMS could be leaked
into different threads before IMMS object is properly constructed.

As far as performance is concerned, basically the amount of tasks
to be done in the main thread remains same, or could be reduced a bit
if some unnecessary callback events were skipped. To confirm this, we
can check the total number of the following performance metrics.
 - StartInputMethodManagerLifecycle
 - StartActivityManagerReadyPhase

 [1]: I9d4d3d7232c984432185c10c13fb726a6158cac8
      8f202f82fd86a3b40bc5e7d62779eddff21096b1

Bug: 32343335
Test: Manual: pre-installed IME on Direct-Boot disabled device
Test: Manual: pre-installed IME on Direct-Boot enabled device
Test: Manual: user-installed IME on Direct-Boot enabled device
Test: Manual: user-installed IME on Direct-Boot disabled device
Test: Manual: user switching scenario on Direct-Boot enabled device
Test: Manual: user switching scenario on Direct-Boot disabled device
Test: Manual: device unlocking scenario on Direct-Boot enabled device
Change-Id: I5b37c450db4b25b3e635b6d634293a34eec8b9d4
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 716997f..c08cd72 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -1080,22 +1080,6 @@
             return enabledSubtypes;
         }
 
-        // At the initial boot, the settings for input methods are not set,
-        // so we need to enable IME in that case.
-        public void enableAllIMEsIfThereIsNoEnabledIME() {
-            if (TextUtils.isEmpty(getEnabledInputMethodsStr())) {
-                StringBuilder sb = new StringBuilder();
-                final int N = mMethodList.size();
-                for (int i = 0; i < N; i++) {
-                    InputMethodInfo imi = mMethodList.get(i);
-                    Slog.i(TAG, "Adding: " + imi.getId());
-                    if (i > 0) sb.append(':');
-                    sb.append(imi.getId());
-                }
-                putEnabledInputMethodsStr(sb.toString());
-            }
-        }
-
         public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
             return buildInputMethodsAndSubtypeList(getEnabledInputMethodsStr(),
                     mInputMethodSplitter,
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index f718fa1..bee1f97 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -243,7 +243,6 @@
     private PendingIntent mImeSwitchPendingIntent;
     private boolean mShowOngoingImeSwitcherForPhones;
     private boolean mNotificationShown;
-    private final boolean mImeSelectedOnBoot;
 
     static class SessionState {
         final ClientState client;
@@ -566,7 +565,7 @@
         }
     }
 
-    class ImmsBroadcastReceiver extends android.content.BroadcastReceiver {
+    class ImmsBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
@@ -587,6 +586,10 @@
                             Intent.EXTRA_SETTING_NEW_VALUE);
                     restoreEnabledInputMethods(mContext, prevValue, newValue);
                 }
+            } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
+                synchronized (mMethodMap) {
+                    resetStateIfCurrentLocaleChangedLocked();
+                }
             } else {
                 Slog.w(TAG, "Unexpected intent " + intent);
             }
@@ -845,9 +848,11 @@
                 return;
             }
             mSettings.switchCurrentUser(currentUserId, !mSystemReady);
-            // We need to rebuild IMEs.
-            buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
-            updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
+            if (mSystemReady) {
+                // We need to rebuild IMEs.
+                buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
+                updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
+            }
         }
     }
 
@@ -897,13 +902,6 @@
 
         mShowOngoingImeSwitcherForPhones = false;
 
-        final IntentFilter broadcastFilter = new IntentFilter();
-        broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
-        broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
-        broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED);
-        mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
-
         mNotificationShown = false;
         int userId = 0;
         try {
@@ -911,7 +909,6 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
         }
-        mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
 
         // mSettings should be created before buildInputMethodListLocked
         mSettings = new InputMethodSettings(
@@ -919,48 +916,8 @@
 
         updateCurrentProfileIds();
         mFileManager = new InputMethodFileManager(mMethodMap, userId);
-        synchronized (mMethodMap) {
-            mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
-                    mSettings, context);
-        }
-
-        // Just checking if defaultImiId is empty or not
-        final String defaultImiId = mSettings.getSelectedInputMethod();
-        if (DEBUG) {
-            Slog.d(TAG, "Initial default ime = " + defaultImiId);
-        }
-        mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
-
-        synchronized (mMethodMap) {
-            buildInputMethodListLocked(!mImeSelectedOnBoot /* resetDefaultEnabledIme */);
-        }
-        mSettings.enableAllIMEsIfThereIsNoEnabledIME();
-
-        if (!mImeSelectedOnBoot) {
-            Slog.w(TAG, "No IME selected. Choose the most applicable IME.");
-            synchronized (mMethodMap) {
-                resetDefaultImeLocked(context);
-            }
-        }
-
-        synchronized (mMethodMap) {
-            mSettingsObserver.registerContentObserverLocked(userId);
-            updateFromSettingsLocked(true);
-        }
-
-        // IMMS wants to receive Intent.ACTION_LOCALE_CHANGED in order to update the current IME
-        // according to the new system locale.
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
-        mContext.registerReceiver(
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        synchronized(mMethodMap) {
-                            resetStateIfCurrentLocaleChangedLocked();
-                        }
-                    }
-                }, filter);
+        mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
+                mSettings, context);
     }
 
     private void resetDefaultImeLocked(Context context) {
@@ -1089,6 +1046,7 @@
             }
             if (!mSystemReady) {
                 mSystemReady = true;
+                mLastSystemLocales = mRes.getConfiguration().getLocales();
                 final int currentUserId = mSettings.getCurrentUserId();
                 mSettings.switchCurrentUser(currentUserId,
                         !mUserManager.isUserUnlockingOrUnlocked(currentUserId));
@@ -1105,14 +1063,25 @@
                     mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
                             mHardKeyboardListener);
                 }
-                if (!mImeSelectedOnBoot) {
-                    Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
-                    resetStateIfCurrentLocaleChangedLocked();
-                    InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
-                            mSettings.getEnabledInputMethodListLocked(),
-                            mSettings.getCurrentUserId(), mContext.getBasePackageName());
-                }
-                mLastSystemLocales = mRes.getConfiguration().getLocales();
+
+                mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+                mSettingsObserver.registerContentObserverLocked(currentUserId);
+
+                final IntentFilter broadcastFilter = new IntentFilter();
+                broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+                broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
+                broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
+                broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED);
+                broadcastFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
+                mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
+
+                buildInputMethodListLocked(true /* resetDefaultEnabledIme */);
+                resetDefaultImeLocked(mContext);
+                updateFromSettingsLocked(true);
+                InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
+                        mSettings.getEnabledInputMethodListLocked(), currentUserId,
+                        mContext.getBasePackageName());
+
                 try {
                     startInputInnerLocked();
                 } catch (RuntimeException e) {
@@ -2624,6 +2593,9 @@
         // additional input method subtypes to the IME.
         if (TextUtils.isEmpty(imiId) || subtypes == null) return;
         synchronized (mMethodMap) {
+            if (!mSystemReady) {
+                return;
+            }
             final InputMethodInfo imi = mMethodMap.get(imiId);
             if (imi == null) return;
             final String[] packageInfos;
@@ -3048,6 +3020,10 @@
             Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
                     + " \n ------ caller=" + Debug.getCallers(10));
         }
+        if (!mSystemReady) {
+            Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
+            return;
+        }
         mMethodList.clear();
         mMethodMap.clear();