Enhance a11y soft keyboard controller

Allow a11y services to request that the soft keyboard be
shown even when the hard keyboard is attached.

Defer to users who override this behavior, and put things
back the way they were when a service requesting this
behavior stops.

Bug: 31012180
Test: Adding CTS tests in linked CL, ran a11y unit tests,
modified TestBack to use the new flag and verified behavior
with a hard keyboard and verfied that settings behave as
expected when overriding and rebooting.
Change-Id: I530481e102ac376a4506b662862ee1ee74815b40
diff --git a/api/current.txt b/api/current.txt
index c641a8c..cf579b8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2818,6 +2818,7 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
     field public static final int SHOW_MODE_AUTO = 0; // 0x0
     field public static final int SHOW_MODE_HIDDEN = 1; // 0x1
+    field public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2; // 0x2
   }
 
   public static abstract class AccessibilityService.GestureResultCallback {
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 829a9444..300a530 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -31,7 +31,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -398,13 +397,49 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "SHOW_MODE_" }, value = {
             SHOW_MODE_AUTO,
-            SHOW_MODE_HIDDEN
+            SHOW_MODE_HIDDEN,
+            SHOW_MODE_WITH_HARD_KEYBOARD
     })
     public @interface SoftKeyboardShowMode {}
 
+    /**
+     * Allow the system to control when the soft keyboard is shown.
+     * @see SoftKeyboardController
+     */
     public static final int SHOW_MODE_AUTO = 0;
+
+    /**
+     * Never show the soft keyboard.
+     * @see SoftKeyboardController
+     */
     public static final int SHOW_MODE_HIDDEN = 1;
 
+    /**
+     * Allow the soft keyboard to be shown, even if a hard keyboard is connected
+     * @see SoftKeyboardController
+     */
+    public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2;
+
+    /**
+     * Mask used to cover the show modes supported in public API
+     * @hide
+     */
+    public static final int SHOW_MODE_MASK = 0x03;
+
+    /**
+     * Bit used to hold the old value of the hard IME setting to restore when a service is shut
+     * down.
+     * @hide
+     */
+    public static final int SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE = 0x20000000;
+
+    /**
+     * Bit for show mode setting to indicate that the user has overridden the hard keyboard
+     * behavior.
+     * @hide
+     */
+    public static final int SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN = 0x40000000;
+
     private int mConnectionId = AccessibilityInteractionClient.NO_ID;
 
     private AccessibilityServiceInfo mInfo;
@@ -1147,7 +1182,27 @@
     }
 
     /**
-     * Used to control and query the soft keyboard show mode.
+     * Used to control, query, and listen for changes to the soft keyboard show mode.
+     * <p>
+     * Accessibility services may request to override the decisions normally made about whether or
+     * not the soft keyboard is shown.
+     * <p>
+     * If multiple services make conflicting requests, the last request is honored. A service may
+     * register a listener to find out if the mode has changed under it.
+     * <p>
+     * If the user takes action to override the behavior behavior requested by an accessibility
+     * service, the user's request takes precendence, the show mode will be reset to
+     * {@link AccessibilityService#SHOW_MODE_AUTO}, and services will no longer be able to control
+     * that aspect of the soft keyboard's behavior.
+     * <p>
+     * Note: Because soft keyboards are independent apps, the framework does not have total control
+     * over their behavior. They may choose to show themselves, or not, without regard to requests
+     * made here. So the framework will make a best effort to deliver the behavior requested, but
+     * cannot guarantee success.
+     *
+     * @see AccessibilityService#SHOW_MODE_AUTO
+     * @see AccessibilityService#SHOW_MODE_HIDDEN
+     * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
      */
     public static final class SoftKeyboardController {
         private final AccessibilityService mService;
@@ -1217,7 +1272,8 @@
          * @param listener the listener to remove, must be non-null
          * @return {@code true} if the listener was removed, {@code false} otherwise
          */
-        public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
+        public boolean removeOnShowModeChangedListener(
+                @NonNull OnShowModeChangedListener listener) {
             if (mListeners == null) {
                 return false;
             }
@@ -1289,32 +1345,32 @@
         }
 
         /**
-         * Returns the show mode of the soft keyboard. The default show mode is
-         * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
-         * focused. An AccessibilityService can also request the show mode
-         * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
+         * Returns the show mode of the soft keyboard.
          *
          * @return the current soft keyboard show mode
+         *
+         * @see AccessibilityService#SHOW_MODE_AUTO
+         * @see AccessibilityService#SHOW_MODE_HIDDEN
+         * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
          */
         @SoftKeyboardShowMode
         public int getShowMode() {
-           try {
-               return Settings.Secure.getInt(mService.getContentResolver(),
-                       Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
-           } catch (Settings.SettingNotFoundException e) {
-               Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e);
-               // The settings hasn't been changed yet, so it's value is null. Return the default.
-               return 0;
-           }
+            final IAccessibilityServiceConnection connection =
+                    AccessibilityInteractionClient.getInstance().getConnection(
+                            mService.mConnectionId);
+            if (connection != null) {
+                try {
+                    return connection.getSoftKeyboardShowMode();
+                } catch (RemoteException re) {
+                    Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
+                    re.rethrowFromSystemServer();
+                }
+            }
+            return SHOW_MODE_AUTO;
         }
 
         /**
-         * Sets the soft keyboard show mode. The default show mode is
-         * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
-         * focused. An AccessibilityService can also request the show mode
-         * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
-         * The lastto this method will be honored, regardless of any previous calls (including those
-         * made by other AccessibilityServices).
+         * Sets the soft keyboard show mode.
          * <p>
          * <strong>Note:</strong> If the service is not yet connected (e.g.
          * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
@@ -1322,6 +1378,10 @@
          *
          * @param showMode the new show mode for the soft keyboard
          * @return {@code true} on success
+         *
+         * @see AccessibilityService#SHOW_MODE_AUTO
+         * @see AccessibilityService#SHOW_MODE_HIDDEN
+         * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
          */
         public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
            final IAccessibilityServiceConnection connection =
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 037aeb0..276131f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -87,6 +87,8 @@
 
     boolean setSoftKeyboardShowMode(int showMode);
 
+    int getSoftKeyboardShowMode();
+
     void setSoftKeyboardCallbackEnabled(boolean enabled);
 
     boolean isAccessibilityButtonAvailable();
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 44b1f08..0dd7685 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -119,6 +119,10 @@
         return false;
     }
 
+    public int getSoftKeyboardShowMode() {
+        return 0;
+    }
+
     public void setSoftKeyboardCallbackEnabled(boolean enabled) {}
 
     public boolean isAccessibilityButtonAvailable() {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 376d16b..613b4f1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,12 +16,18 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_WITH_HARD_KEYBOARD;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
 
 import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
@@ -1779,7 +1785,6 @@
         updateDisplayDaltonizerLocked(userState);
         updateDisplayInversionLocked(userState);
         updateMagnificationLocked(userState);
-        updateSoftKeyboardShowModeLocked(userState);
         scheduleUpdateFingerprintGestureHandling(userState);
         scheduleUpdateInputFilter(userState);
         scheduleUpdateClientsIfNeededLocked(userState);
@@ -1973,18 +1978,6 @@
         return false;
     }
 
-    private boolean readSoftKeyboardShowModeChangedLocked(UserState userState) {
-        final int softKeyboardShowMode = Settings.Secure.getIntForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0,
-                userState.mUserId);
-        if (softKeyboardShowMode != userState.mSoftKeyboardShowMode) {
-            userState.mSoftKeyboardShowMode = softKeyboardShowMode;
-            return true;
-        }
-        return false;
-    }
-
     private void updateTouchExplorationLocked(UserState userState) {
         boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
         final int serviceCount = userState.mBoundServices.size();
@@ -2183,34 +2176,6 @@
         return false;
     }
 
-    private void updateSoftKeyboardShowModeLocked(UserState userState) {
-        final int userId = userState.mUserId;
-        // Only check whether we need to reset the soft keyboard mode if it is not set to the
-        // default.
-        if ((userId == mCurrentUserId) && (userState.mSoftKeyboardShowMode != 0)) {
-            // Check whether the last AccessibilityService that changed the soft keyboard mode to
-            // something other than the default is still enabled and, if not, remove flag and
-            // reset to the default soft keyboard behavior.
-            boolean serviceChangingSoftKeyboardModeIsEnabled =
-                    userState.mEnabledServices.contains(userState.mServiceChangingSoftKeyboardMode);
-
-            if (!serviceChangingSoftKeyboardModeIsEnabled) {
-                final long identity = Binder.clearCallingIdentity();
-                try {
-                    Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                            Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                            0,
-                            userState.mUserId);
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-                userState.mSoftKeyboardShowMode = 0;
-                userState.mServiceChangingSoftKeyboardMode = null;
-                notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
-            }
-        }
-    }
-
     private void updateFingerprintGestureHandling(UserState userState) {
         final List<AccessibilityServiceConnection> services;
         synchronized (mLock) {
@@ -2445,6 +2410,15 @@
         }
     }
 
+    private void putSecureIntForUser(String key, int value, int userid) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     class RemoteAccessibilityConnection implements DeathRecipient {
         private final int mUid;
         private final String mPackageName;
@@ -3667,7 +3641,7 @@
 
         public int mLastSentClientState = -1;
 
-        public int mSoftKeyboardShowMode = 0;
+        private int mSoftKeyboardShowMode = 0;
 
         public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
         public ComponentName mServiceAssignedToAccessibilityButton;
@@ -3728,7 +3702,6 @@
             mServiceAssignedToAccessibilityButton = null;
             mIsNavBarMagnificationAssignedToAccessibilityButton = false;
             mIsAutoclickEnabled = false;
-            mSoftKeyboardShowMode = 0;
         }
 
         public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -3750,6 +3723,11 @@
         public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
             mBoundServices.remove(serviceConnection);
             serviceConnection.onRemoved();
+            if ((mServiceChangingSoftKeyboardMode != null)
+                    && (mServiceChangingSoftKeyboardMode.equals(
+                            serviceConnection.getServiceInfo().getComponentName()))) {
+                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+            }
             // It may be possible to bind a service twice, which confuses the map. Rebuild the map
             // to make sure we can still reach a service
             mComponentNameToServiceMap.clear();
@@ -3776,6 +3754,134 @@
         public Set<ComponentName> getBindingServicesLocked() {
             return mBindingServices;
         }
+
+        public int getSoftKeyboardShowMode() {
+            return mSoftKeyboardShowMode;
+        }
+
+        /**
+         * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
+         * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
+         * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
+         * setting can be changed by the user, and prevents the system from suppressing the soft
+         * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
+         * to the user's preference, if they have supplied one.
+         *
+         * @param newMode The new mode
+         * @param requester The service requesting the change, so we can undo it when the
+         *                  service stops. Set to null if something other than a service is forcing
+         *                  the change.
+         *
+         * @return Whether or not the soft keyboard mode equals the new mode after the call
+         */
+        public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) {
+            if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN)
+                    && (newMode != SHOW_MODE_WITH_HARD_KEYBOARD))
+            {
+                Slog.w(LOG_TAG, "Invalid soft keyboard mode");
+                return false;
+            }
+            if (mSoftKeyboardShowMode == newMode) return true;
+
+            if (newMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+                if (hasUserOverriddenHardKeyboardSettingLocked()) {
+                    // The user has specified a default for this setting
+                    return false;
+                }
+                // Save the original value. But don't do this if the value in settings is already
+                // the new mode. That happens when we start up after a reboot, and we don't want
+                // to overwrite the value we had from when we first started controlling the setting.
+                if (getSoftKeyboardValueFromSettings() != SHOW_MODE_WITH_HARD_KEYBOARD) {
+                    setOriginalHardKeyboardValue(
+                            Settings.Secure.getInt(mContext.getContentResolver(),
+                                    Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0);
+                }
+                putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
+            } else if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+                putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                        getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
+            }
+
+            saveSoftKeyboardValueToSettings(newMode);
+            mSoftKeyboardShowMode = newMode;
+            mServiceChangingSoftKeyboardMode = requester;
+            notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
+            return true;
+        }
+
+        /**
+         * If the settings are inconsistent with the internal state, make the internal state
+         * match the settings.
+         */
+        public void reconcileSoftKeyboardModeWithSettingsLocked() {
+            final ContentResolver cr = mContext.getContentResolver();
+            final boolean showWithHardKeyboardSettings =
+                    Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
+            if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+                if (!showWithHardKeyboardSettings) {
+                    // The user has overridden the setting. Respect that and prevent further changes
+                    // to this behavior.
+                    setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+                    setUserOverridesHardKeyboardSettingLocked();
+                }
+            }
+
+            // If the setting and the internal state are out of sync, set both to default
+            if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode)
+            {
+                Slog.e(LOG_TAG,
+                        "Show IME setting inconsistent with internal state. Overwriting");
+                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+                putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                        SHOW_MODE_AUTO, mUserId);
+            }
+        }
+
+        private void setUserOverridesHardKeyboardSettingLocked() {
+            final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+                    mUserId);
+        }
+
+        private boolean hasUserOverriddenHardKeyboardSettingLocked() {
+            final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+            return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
+                    != 0;
+        }
+
+        private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
+            final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+            final int newSoftKeyboardSetting = oldSoftKeyboardSetting
+                    & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
+                    | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
+            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    newSoftKeyboardSetting, mUserId);
+        }
+
+        private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
+            final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+            final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
+                    | softKeyboardShowMode;
+            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    newSoftKeyboardSetting, mUserId);
+        }
+
+        private int getSoftKeyboardValueFromSettings() {
+            return Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    SHOW_MODE_AUTO) & SHOW_MODE_MASK;
+        }
+
+        private boolean getOriginalHardKeyboardValue() {
+            return (Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0)
+                    & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
+        }
     }
 
     private final class AccessibilityContentObserver extends ContentObserver {
@@ -3813,6 +3919,9 @@
         private final Uri mAccessibilitySoftKeyboardModeUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
 
+        private final Uri mShowImeWithHardKeyboardUri = Settings.Secure.getUriFor(
+                Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
+
         private final Uri mAccessibilityShortcutServiceIdUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
 
@@ -3848,6 +3957,8 @@
             contentResolver.registerContentObserver(
                     mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
+                    mShowImeWithHardKeyboardUri, false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(
                     mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL);
@@ -3890,11 +4001,9 @@
                     if (readHighTextContrastEnabledSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
-                } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)) {
-                    if (readSoftKeyboardShowModeChangedLocked(userState)) {
-                        notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
-                        onUserStateChangedLocked(userState);
-                    }
+                } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)
+                        || mShowImeWithHardKeyboardUri.equals(uri)) {
+                    userState.reconcileSoftKeyboardModeWithSettingsLocked();
                 } else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
                     if (readAccessibilityShortcutSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 105df92..dda18e8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -218,26 +218,20 @@
             if (!isCalledForCurrentUserLocked()) {
                 return false;
             }
+            final UserState userState = mUserStateWeakReference.get();
+            if (userState == null) return false;
+            return userState.setSoftKeyboardModeLocked(showMode, mComponentName);
         }
-        UserState userState = mUserStateWeakReference.get();
-        if (userState == null) return false;
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            // Keep track of the last service to request a non-default show mode. The show mode
-            // should be restored to default should this service be disabled.
-            userState.mServiceChangingSoftKeyboardMode = (showMode == SHOW_MODE_AUTO)
-                    ? null : mComponentName;
-
-            Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, showMode,
-                    userState.mUserId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        return true;
     }
 
     @Override
+    public int getSoftKeyboardShowMode() {
+        final UserState userState = mUserStateWeakReference.get();
+        return (userState != null) ? userState.getSoftKeyboardShowMode() : 0;
+    }
+
+
+    @Override
     public boolean isAccessibilityButtonAvailable() {
         synchronized (mLock) {
             if (!isCalledForCurrentUserLocked()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index ed3b3e7..ff29311 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -259,6 +259,11 @@
         }
 
         @Override
+        public int getSoftKeyboardShowMode() {
+            return 0;
+        }
+
+        @Override
         public boolean isAccessibilityButtonAvailable() {
             return false;
         }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 4b8ece9..582718e 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -51,6 +51,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.Manifest;
+import android.accessibilityservice.AccessibilityService;
 import android.annotation.AnyThread;
 import android.annotation.BinderThread;
 import android.annotation.ColorInt;
@@ -888,10 +889,12 @@
                 if (showImeUri.equals(uri)) {
                     updateKeyboardFromSettingsLocked();
                 } else if (accessibilityRequestingNoImeUri.equals(uri)) {
-                    mAccessibilityRequestingNoSoftKeyboard = Settings.Secure.getIntForUser(
+                    final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
                             mContext.getContentResolver(),
-                            Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                            0, mUserId) == 1;
+                            Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId);
+                    mAccessibilityRequestingNoSoftKeyboard =
+                            (accessibilitySoftKeyboardSetting & AccessibilityService.SHOW_MODE_MASK)
+                                    == AccessibilityService.SHOW_MODE_HIDDEN;
                     if (mAccessibilityRequestingNoSoftKeyboard) {
                         final boolean showRequested = mShowRequested;
                         hideCurrentInputLocked(0, null);