Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7924782c authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

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
parent 4ea4f63d
Loading
Loading
Loading
Loading
+0 −16
Original line number Diff line number Diff line
@@ -1080,22 +1080,6 @@ public class InputMethodUtils {
            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,
+39 −63
Original line number Diff line number Diff line
@@ -243,7 +243,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
    private PendingIntent mImeSwitchPendingIntent;
    private boolean mShowOngoingImeSwitcherForPhones;
    private boolean mNotificationShown;
    private final boolean mImeSelectedOnBoot;

    static class SessionState {
        final ClientState client;
@@ -566,7 +565,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
        }
    }

    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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                            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,11 +848,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                return;
            }
            mSettings.switchCurrentUser(currentUserId, !mSystemReady);
            if (mSystemReady) {
                // We need to rebuild IMEs.
                buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
                updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
            }
        }
    }

    void onSwitchUser(@UserIdInt int userId) {
        synchronized (mMethodMap) {
@@ -897,13 +902,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub

        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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
        } 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,50 +916,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub

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

    private void resetDefaultImeLocked(Context context) {
        // Do not reset the default (current) IME when it is a 3rd-party IME
        if (mCurMethodId != null && !InputMethodUtils.isSystemIme(mMethodMap.get(mCurMethodId))) {
@@ -1089,6 +1046,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
            }
            if (!mSystemReady) {
                mSystemReady = true;
                mLastSystemLocales = mRes.getConfiguration().getLocales();
                final int currentUserId = mSettings.getCurrentUserId();
                mSettings.switchCurrentUser(currentUserId,
                        !mUserManager.isUserUnlockingOrUnlocked(currentUserId));
@@ -1105,14 +1063,25 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                    mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
                            mHardKeyboardListener);
                }
                if (!mImeSelectedOnBoot) {
                    Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
                    resetStateIfCurrentLocaleChangedLocked();

                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(),
                            mSettings.getCurrentUserId(), mContext.getBasePackageName());
                }
                mLastSystemLocales = mRes.getConfiguration().getLocales();
                        mSettings.getEnabledInputMethodListLocked(), currentUserId,
                        mContext.getBasePackageName());

                try {
                    startInputInnerLocked();
                } catch (RuntimeException e) {
@@ -2624,6 +2593,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
        // 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
            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();