diff --git a/api/current.txt b/api/current.txt index a501ce7d13466..0f90986de083c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2625,6 +2625,7 @@ package android.accessibilityservice { method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController(); method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); + method public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController(); method public java.util.List getWindows(); method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public final android.os.IBinder onBind(android.content.Intent); @@ -2683,6 +2684,18 @@ package android.accessibilityservice { method public abstract void onMagnificationChanged(android.accessibilityservice.AccessibilityService.MagnificationController, android.graphics.Region, float, float, float); } + public static final class AccessibilityService.SoftKeyboardController { + method public void addOnShowModeChangedListener(android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener); + method public void addOnShowModeChangedListener(android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener, android.os.Handler); + method public int getShowMode(); + method public boolean removeOnShowModeChangedListener(android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener); + method public boolean setShowMode(int); + } + + public static abstract interface AccessibilityService.SoftKeyboardController.OnShowModeChangedListener { + method public abstract void onShowModeChanged(android.accessibilityservice.AccessibilityService.SoftKeyboardController, int); + } + public class AccessibilityServiceInfo implements android.os.Parcelable { ctor public AccessibilityServiceInfo(); method public static java.lang.String capabilityToString(int); diff --git a/api/system-current.txt b/api/system-current.txt index dc8e5da04ffde..1091ff13c4356 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -2727,6 +2727,7 @@ package android.accessibilityservice { method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController(); method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); + method public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController(); method public java.util.List getWindows(); method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public final android.os.IBinder onBind(android.content.Intent); @@ -2785,6 +2786,18 @@ package android.accessibilityservice { method public abstract void onMagnificationChanged(android.accessibilityservice.AccessibilityService.MagnificationController, android.graphics.Region, float, float, float); } + public static final class AccessibilityService.SoftKeyboardController { + method public void addOnShowModeChangedListener(android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener); + method public void addOnShowModeChangedListener(android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener, android.os.Handler); + method public int getShowMode(); + method public boolean removeOnShowModeChangedListener(android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener); + method public boolean setShowMode(int); + } + + public static abstract interface AccessibilityService.SoftKeyboardController.OnShowModeChangedListener { + method public abstract void onShowModeChanged(android.accessibilityservice.AccessibilityService.SoftKeyboardController, int); + } + public class AccessibilityServiceInfo implements android.os.Parcelable { ctor public AccessibilityServiceInfo(); method public static java.lang.String capabilityToString(int); diff --git a/api/test-current.txt b/api/test-current.txt index d400113c7fe11..2c5cc3ad7fdab 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2625,6 +2625,7 @@ package android.accessibilityservice { method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController(); method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); + method public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController(); method public java.util.List getWindows(); method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public final android.os.IBinder onBind(android.content.Intent); @@ -2683,6 +2684,18 @@ package android.accessibilityservice { method public abstract void onMagnificationChanged(android.accessibilityservice.AccessibilityService.MagnificationController, android.graphics.Region, float, float, float); } + public static final class AccessibilityService.SoftKeyboardController { + method public void addOnShowModeChangedListener(android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener); + method public void addOnShowModeChangedListener(android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener, android.os.Handler); + method public int getShowMode(); + method public boolean removeOnShowModeChangedListener(android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener); + method public boolean setShowMode(int); + } + + public static abstract interface AccessibilityService.SoftKeyboardController.OnShowModeChangedListener { + method public abstract void onShowModeChanged(android.accessibilityservice.AccessibilityService.SoftKeyboardController, int); + } + public class AccessibilityServiceInfo implements android.os.Parcelable { ctor public AccessibilityServiceInfo(); method public static java.lang.String capabilityToString(int); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index be939264c7e9f..c96cca27be55d 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -17,6 +17,7 @@ package android.accessibilityservice; import android.accessibilityservice.GestureDescription.MotionEventGenerator; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Service; @@ -29,6 +30,7 @@ import android.os.IBinder; 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.Pair; @@ -46,6 +48,8 @@ import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; /** @@ -363,9 +367,26 @@ public abstract class AccessibilityService extends Service { public boolean onKeyEvent(KeyEvent event); public void onMagnificationChanged(@NonNull Region region, float scale, float centerX, float centerY); + public void onSoftKeyboardShowModeChanged(int showMode); public void onPerformGestureResult(int sequence, boolean completedSuccessfully); } + /** + * Annotations for Soft Keyboard show modes so tools can catch invalid show modes. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({SHOW_MODE_AUTO, SHOW_MODE_HIDDEN}) + public @interface SoftKeyboardShowMode {}; + /** + * @hide + */ + public static final int SHOW_MODE_AUTO = 0; + /** + * @hide + */ + public static final int SHOW_MODE_HIDDEN = 1; + private int mConnectionId; private AccessibilityServiceInfo mInfo; @@ -375,6 +396,7 @@ public abstract class AccessibilityService extends Service { private WindowManager mWindowManager; private MagnificationController mMagnificationController; + private SoftKeyboardController mSoftKeyboardController; private int mGestureStatusCallbackSequence; @@ -563,10 +585,12 @@ public abstract class AccessibilityService extends Service { */ @NonNull public final MagnificationController getMagnificationController() { - if (mMagnificationController == null) { - mMagnificationController = new MagnificationController(this); + synchronized (mLock) { + if (mMagnificationController == null) { + mMagnificationController = new MagnificationController(this, mLock); + } + return mMagnificationController; } - return mMagnificationController; } /** @@ -670,17 +694,21 @@ public abstract class AccessibilityService extends Service { * first magnification listener. */ private ArrayMap mListeners; + private final Object mLock; - MagnificationController(@NonNull AccessibilityService service) { + MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) { mService = service; + mLock = lock; } /** * Called when the service is connected. */ void onServiceConnected() { - if (mListeners != null && !mListeners.isEmpty()) { - setMagnificationCallbackEnabled(true); + synchronized (mLock) { + if (mListeners != null && !mListeners.isEmpty()) { + setMagnificationCallbackEnabled(true); + } } } @@ -707,17 +735,19 @@ public abstract class AccessibilityService extends Service { */ public void addListener(@NonNull OnMagnificationChangedListener listener, @Nullable Handler handler) { - if (mListeners == null) { - mListeners = new ArrayMap<>(); - } + synchronized (mLock) { + if (mListeners == null) { + mListeners = new ArrayMap<>(); + } - final boolean shouldEnableCallback = mListeners.isEmpty(); - mListeners.put(listener, handler); + final boolean shouldEnableCallback = mListeners.isEmpty(); + mListeners.put(listener, handler); - if (shouldEnableCallback) { - // This may fail if the service is not connected yet, but if we - // still have listeners when it connects then we can try again. - setMagnificationCallbackEnabled(true); + if (shouldEnableCallback) { + // This may fail if the service is not connected yet, but if we + // still have listeners when it connects then we can try again. + setMagnificationCallbackEnabled(true); + } } } @@ -734,19 +764,21 @@ public abstract class AccessibilityService extends Service { return false; } - final int keyIndex = mListeners.indexOfKey(listener); - final boolean hasKey = keyIndex >= 0; - if (hasKey) { - mListeners.removeAt(keyIndex); - } + synchronized (mLock) { + final int keyIndex = mListeners.indexOfKey(listener); + final boolean hasKey = keyIndex >= 0; + if (hasKey) { + mListeners.removeAt(keyIndex); + } - if (hasKey && mListeners.isEmpty()) { - // We just removed the last listener, so we don't need - // callbacks from the service anymore. - setMagnificationCallbackEnabled(false); - } + if (hasKey && mListeners.isEmpty()) { + // We just removed the last listener, so we don't need + // callbacks from the service anymore. + setMagnificationCallbackEnabled(false); + } - return hasKey; + return hasKey; + } } private void setMagnificationCallbackEnabled(boolean enabled) { @@ -768,17 +800,19 @@ public abstract class AccessibilityService extends Service { */ void dispatchMagnificationChanged(final @NonNull Region region, final float scale, final float centerX, final float centerY) { - if (mListeners == null || mListeners.isEmpty()) { - Slog.d(LOG_TAG, "Received magnification changed " - + "callback with no listeners registered!"); - setMagnificationCallbackEnabled(false); - return; - } + final ArrayMap entries; + synchronized (mLock) { + if (mListeners == null || mListeners.isEmpty()) { + Slog.d(LOG_TAG, "Received magnification changed " + + "callback with no listeners registered!"); + setMagnificationCallbackEnabled(false); + return; + } - // Listeners may remove themselves. Perform a shallow copy to avoid - // concurrent modification. - final ArrayMap entries = - new ArrayMap<>(mListeners); + // Listeners may remove themselves. Perform a shallow copy to avoid concurrent + // modification. + entries = new ArrayMap<>(mListeners); + } for (int i = 0, count = entries.size(); i < count; i++) { final OnMagnificationChangedListener listener = entries.keyAt(i); @@ -1010,6 +1044,243 @@ public abstract class AccessibilityService extends Service { } } + /** + * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard + * show mode. + * + * @return the soft keyboard controller + */ + @NonNull + public final SoftKeyboardController getSoftKeyboardController() { + synchronized (mLock) { + if (mSoftKeyboardController == null) { + mSoftKeyboardController = new SoftKeyboardController(this, mLock); + } + return mSoftKeyboardController; + } + } + + private void onSoftKeyboardShowModeChanged(int showMode) { + if (mSoftKeyboardController != null) { + mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode); + } + } + + /** + * Used to control and query the soft keyboard show mode. + */ + public static final class SoftKeyboardController { + private final AccessibilityService mService; + + /** + * Map of listeners to their handlers. Lazily created when adding the first + * soft keyboard change listener. + */ + private ArrayMap mListeners; + private final Object mLock; + + SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) { + mService = service; + mLock = lock; + } + + /** + * Called when the service is connected. + */ + void onServiceConnected() { + synchronized(mLock) { + if (mListeners != null && !mListeners.isEmpty()) { + setSoftKeyboardCallbackEnabled(true); + } + } + } + + /** + * Adds the specified change listener to the list of show mode change listeners. The + * callback will occur on the service's main thread. Listener is not called on registration. + */ + public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) { + addOnShowModeChangedListener(listener, null); + } + + /** + * Adds the specified change listener to the list of soft keyboard show mode change + * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the + * services's main thread if the handler is {@code null}. + * + * @param listener the listener to add, must be non-null + * @param handler the handler on which to callback should execute, or {@code null} to + * execute on the service's main thread + */ + public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener, + @Nullable Handler handler) { + synchronized (mLock) { + if (mListeners == null) { + mListeners = new ArrayMap<>(); + } + + final boolean shouldEnableCallback = mListeners.isEmpty(); + mListeners.put(listener, handler); + + if (shouldEnableCallback) { + // This may fail if the service is not connected yet, but if we still have + // listeners when it connects, we can try again. + setSoftKeyboardCallbackEnabled(true); + } + } + } + + /** + * Removes all instances of the specified change listener from teh list of magnification + * change listeners. + * + * @param listener the listener to remove, must be non-null + * @return {@code true} if at least one instance of the listener was removed + */ + public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) { + if (mListeners == null) { + return false; + } + + synchronized (mLock) { + final int keyIndex = mListeners.indexOfKey(listener); + final boolean hasKey = keyIndex >= 0; + if (hasKey) { + mListeners.removeAt(keyIndex); + } + + if (hasKey && mListeners.isEmpty()) { + // We just removed the last listener, so we don't need callbacks from the + // service anymore. + setSoftKeyboardCallbackEnabled(false); + } + + return hasKey; + } + } + + private void setSoftKeyboardCallbackEnabled(boolean enabled) { + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection( + mService.mConnectionId); + if (connection != null) { + try { + connection.setSoftKeyboardCallbackEnabled(enabled); + } catch (RemoteException re) { + throw new RuntimeException(re); + } + } + } + + /** + * Dispatches the soft keyboard show mode change to any registered listeners. This should + * be called on the service's main thread. + */ + void dispatchSoftKeyboardShowModeChanged(final int showMode) { + final ArrayMap entries; + synchronized (mLock) { + if (mListeners == null || mListeners.isEmpty()) { + Slog.d(LOG_TAG, "Received soft keyboard show mode changed callback" + + " with no listeners registered!"); + setSoftKeyboardCallbackEnabled(false); + return; + } + + // Listeners may remove themselves. Perform a shallow copy to avoid concurrent + // modification. + entries = new ArrayMap<>(mListeners); + } + + for (int i = 0, count = entries.size(); i < count; i++) { + final OnShowModeChangedListener listener = entries.keyAt(i); + final Handler handler = entries.valueAt(i); + if (handler != null) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onShowModeChanged(SoftKeyboardController.this, showMode); + } + }); + } else { + // We're already on the main thread, just run the listener. + listener.onShowModeChanged(this, showMode); + } + } + } + + /** + * Returns the show mode of the soft keyboard. The default show mode is + * {@code Settings.Secure.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 Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. + * + * @return the current soft keyboard show mode + * + * @see Settings#Secure#SHOW_MODE_AUTO + * @see Settings#Secure#SHOW_MODE_HIDDEN + */ + @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; + } + } + + /** + * Sets the soft keyboard show mode. The default show mode is + * {@code Settings.Secure.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 Settings.Secure.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). + *

+ * Note: If the service is not yet conected (e.g. + * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the + * service has been disconnected, this method will hav no effect and return {@code false}. + * + * @param showMode the new show mode for the soft keyboard + * @return {@code true} on success + * + * @see Settings#Secure#SHOW_MODE_AUTO + * @see Settings#Secure#SHOW_MODE_HIDDEN + */ + public boolean setShowMode(@SoftKeyboardShowMode int showMode) { + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection( + mService.mConnectionId); + if (connection != null) { + try { + return connection.setSoftKeyboardShowMode(showMode); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Falied to set soft keyboard behavior", re); + } + } + return false; + } + + /** + * Listener for changes in the soft keyboard show mode. + */ + public interface OnShowModeChangedListener { + /** + * Called when the soft keyboard behavior changes. The default show mode is + * {@code Settings.Secure.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 Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. + * + * @param controller the soft keyboard controller + * @param showMode the current soft keyboard show mode + */ + void onShowModeChanged(@NonNull SoftKeyboardController controller, + @SoftKeyboardShowMode int showMode); + } + } + /** * Performs a global action. Such an action can be performed * at any moment regardless of the current application or user @@ -1183,6 +1454,11 @@ public abstract class AccessibilityService extends Service { AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY); } + @Override + public void onSoftKeyboardShowModeChanged(int showMode) { + AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode); + } + @Override public void onPerformGestureResult(int sequence, boolean completedSuccessfully) { AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully); @@ -1205,7 +1481,8 @@ public abstract class AccessibilityService extends Service { private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; private static final int DO_ON_KEY_EVENT = 6; private static final int DO_ON_MAGNIFICATION_CHANGED = 7; - private static final int DO_GESTURE_COMPLETE = 8; + private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8; + private static final int DO_GESTURE_COMPLETE = 9; private final HandlerCaller mCaller; @@ -1264,6 +1541,12 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } + public void onSoftKeyboardShowModeChanged(int showMode) { + final Message message = + mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode); + mCaller.sendMessage(message); + } + public void onPerformGestureResult(int sequence, boolean successfully) { Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence, successfully ? 1 : 0); @@ -1354,6 +1637,11 @@ public abstract class AccessibilityService extends Service { mCallback.onMagnificationChanged(region, scale, centerX, centerY); } return; + case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: { + final int showMode = (int) message.arg1; + mCallback.onSoftKeyboardShowModeChanged(showMode); + } return; + case DO_GESTURE_COMPLETE: { final boolean successfully = message.arg2 == 1; mCallback.onPerformGestureResult(message.arg1, successfully); diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index 6280542e6ce46..ef05d6f524c76 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -43,5 +43,7 @@ import android.view.KeyEvent; void onMagnificationChanged(in Region region, float scale, float centerX, float centerY); + void onSoftKeyboardShowModeChanged(int showMode); + void onPerformGestureResult(int sequence, boolean completedSuccessfully); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index e58ef2fec0105..d5e8a112a485e 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -84,5 +84,9 @@ interface IAccessibilityServiceConnection { void setMagnificationCallbackEnabled(boolean enabled); + boolean setSoftKeyboardShowMode(int showMode); + + void setSoftKeyboardCallbackEnabled(boolean enabled); + void sendMotionEvents(int sequence, in ParceledListSlice events); } diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 79d383c53cf6a..5ec59b4675107 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -1087,6 +1087,11 @@ public final class UiAutomation { /* do nothing */ } + @Override + public void onSoftKeyboardShowModeChanged(int showMode) { + /* do nothing */ + } + @Override public void onPerformGestureResult(int sequence, boolean completedSuccessfully) { /* do nothing */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c9c0cdeff17cf..6e92ec157e0e1 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4855,6 +4855,29 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE = "accessibility_display_magnification_auto_update"; + /** + * Setting that specifies what mode the soft keyboard is in (default or hidden). Can be + * modified from an AccessibilityService using the SoftKeyboardController. + * + * @hide + */ + public static final String ACCESSIBILITY_SOFT_KEYBOARD_MODE = + "accessibility_soft_keyboard_mode"; + + /** + * Default soft keyboard behavior. + * + * @hide + */ + public static final int SHOW_MODE_AUTO = 0; + + /** + * Soft keyboard is never shown. + * + * @hide + */ + public static final int SHOW_MODE_HIDDEN = 1; + /** * Setting that specifies whether timed text (captions) should be * displayed in video content. Text display properties are controlled by diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 2a8672dd099d7..53504cc2f6c64 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -958,7 +958,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final UserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final Service service = state.mBoundServices.get(i); - service.notifyMagnificationChanged(region, scale, centerX, centerY); + service.notifyMagnificationChangedLocked(region, scale, centerX, centerY); + } + } + + private void notifySoftKeyboardShowModeChangedLocked(int showMode) { + final UserState state = getCurrentUserStateLocked(); + for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { + final Service service = state.mBoundServices.get(i); + service.notifySoftKeyboardShowModeChangedLocked(showMode); } } @@ -1407,6 +1415,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { updateEnhancedWebAccessibilityLocked(userState); updateDisplayColorAdjustmentSettingsLocked(userState); updateMagnificationLocked(userState); + updateSoftKeyboardShowModeLocked(userState); scheduleUpdateInputFilter(userState); scheduleUpdateClientsIfNeededLocked(userState); } @@ -1623,6 +1632,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { 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 = false; final int serviceCount = userState.mBoundServices.size(); @@ -1757,6 +1778,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } + private void updateSoftKeyboardShowModeLocked(UserState userState) { + final int userId = userState.mUserId; + if (userId == mCurrentUserId) { + // Check whether any Accessibility Services are still enabled and, if not, remove flag + // requesting no soft keyboard + final boolean accessibilityRequestingNoIme = userState.mSoftKeyboardShowMode == 1; + if (accessibilityRequestingNoIme && !userState.mIsAccessibilityEnabled) { + // No active Accessibility Services can be requesting the soft keyboard to be hidden + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + 0, + userState.mUserId); + userState.mSoftKeyboardShowMode = 0; + } + + notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode); + } + } + private MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) { IBinder windowToken = mGlobalWindowTokens.get(windowId); if (windowToken == null) { @@ -2102,7 +2142,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { }; // Handler for scheduling method invocations on the main thread. - public InvocationHandler mInvocationHandler = new InvocationHandler( + public final InvocationHandler mInvocationHandler = new InvocationHandler( mMainHandler.getLooper()); public Service(int userId, ComponentName componentName, @@ -2297,17 +2337,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private boolean isCalledForCurrentUserLocked() { + // We treat calls from a profile as if made by its parent as profiles + // share the accessibility state of the parent. The call below + // performs the current profile parent resolution. + final int resolvedUserId = mSecurityPolicy + .resolveCallingUserIdEnforcingPermissionsLocked(UserHandle.USER_CURRENT); + return resolvedUserId == mCurrentUserId; + } + @Override public List getWindows() { ensureWindowsAvailableTimed(); synchronized (mLock) { - // We treat calls from a profile as if made by its perent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return null; } final boolean permissionGranted = @@ -2335,13 +2378,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public AccessibilityWindowInfo getWindow(int windowId) { ensureWindowsAvailableTimed(); synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return null; } final boolean permissionGranted = @@ -2368,13 +2405,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnection connection = null; Region partialInteractiveRegion = Region.obtain(); synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return false; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); @@ -2425,13 +2456,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnection connection = null; Region partialInteractiveRegion = Region.obtain(); synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return false; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); @@ -2482,13 +2507,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnection connection = null; Region partialInteractiveRegion = Region.obtain(); synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return false; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); @@ -2539,13 +2558,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnection connection = null; Region partialInteractiveRegion = Region.obtain(); synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return false; } resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked( @@ -2597,13 +2610,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnection connection = null; Region partialInteractiveRegion = Region.obtain(); synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return false; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); @@ -2670,13 +2677,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int resolvedWindowId; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return false; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); @@ -2709,13 +2710,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public boolean performGlobalAction(int action) { synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return false; } } @@ -2750,13 +2745,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public float getMagnificationScale() { synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return 1.0f; } } @@ -2771,13 +2760,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public Region getMagnifiedRegion() { synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return Region.obtain(); } } @@ -2794,13 +2777,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public float getMagnificationCenterX() { synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return 0.0f; } } @@ -2815,13 +2792,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public float getMagnificationCenterY() { synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return 0.0f; } } @@ -2836,13 +2807,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public boolean resetMagnification(boolean animate) { synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return false; } final boolean permissionGranted = mSecurityPolicy.canControlMagnification(this); @@ -2862,13 +2827,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY, boolean animate) { synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { + if (!isCalledForCurrentUserLocked()) { return false; } final boolean permissionGranted = mSecurityPolicy.canControlMagnification(this); @@ -2890,6 +2849,33 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mInvocationHandler.setMagnificationCallbackEnabled(enabled); } + @Override + public boolean setSoftKeyboardShowMode(int showMode) { + final UserState userState; + synchronized (mLock) { + if (!isCalledForCurrentUserLocked()) { + return false; + } + + userState = getCurrentUserStateLocked(); + } + + final long identity = Binder.clearCallingIdentity(); + try { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, showMode, + userState.mUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } + return true; + } + + @Override + public void setSoftKeyboardCallbackEnabled(boolean enabled) { + mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled); + } + @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP); @@ -3080,9 +3066,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE); } - public void notifyMagnificationChanged(@NonNull Region region, + public void notifyMagnificationChangedLocked(@NonNull Region region, float scale, float centerX, float centerY) { - mInvocationHandler.notifyMagnificationChanged(region, scale, centerX, centerY); + mInvocationHandler + .notifyMagnificationChangedLocked(region, scale, centerX, centerY); + } + + public void notifySoftKeyboardShowModeChangedLocked(int showState) { + mInvocationHandler.notifySoftKeyboardShowModeChangedLocked(showState); } /** @@ -3104,6 +3095,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + /** + * Called by the invocation handler to notify the service that the state of the soft + * keyboard show mode has changed. + */ + private void notifySoftKeyboardShowModeChangedInternal(int showState) { + final IAccessibilityServiceClient listener; + synchronized (mLock) { + listener = mServiceInterface; + } + if (listener != null) { + try { + listener.onSoftKeyboardShowModeChanged(showState); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Error sending soft keyboard show mode changes to " + mService, + re); + } + } + } + private void notifyGestureInternal(int gestureId) { final IAccessibilityServiceClient listener; synchronized (mLock) { @@ -3239,8 +3249,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2; private static final int MSG_ON_MAGNIFICATION_CHANGED = 5; + private static final int MSG_ON_SOFT_KEYBOARD_STATE_CHANGED = 6; private boolean mIsMagnificationCallbackEnabled = false; + private boolean mIsSoftKeyboardCallbackEnabled = false; public InvocationHandler(Looper looper) { super(looper, null, true); @@ -3268,13 +3280,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { notifyMagnificationChangedInternal(region, scale, centerX, centerY); } break; + case MSG_ON_SOFT_KEYBOARD_STATE_CHANGED: { + final int showState = (int) message.arg1; + notifySoftKeyboardShowModeChangedInternal(showState); + } break; + default: { throw new IllegalArgumentException("Unknown message: " + type); } } } - public void notifyMagnificationChanged(@NonNull Region region, float scale, + public void notifyMagnificationChangedLocked(@NonNull Region region, float scale, float centerX, float centerY) { if (!mIsMagnificationCallbackEnabled) { // Callback is disabled, don't bother packing args. @@ -3294,8 +3311,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public void setMagnificationCallbackEnabled(boolean enabled) { mIsMagnificationCallbackEnabled = enabled; } - } + public void notifySoftKeyboardShowModeChangedLocked(int showState) { + if (!mIsSoftKeyboardCallbackEnabled) { + return; + } + + final Message msg = obtainMessage(MSG_ON_SOFT_KEYBOARD_STATE_CHANGED, showState, 0); + msg.sendToTarget(); + } + + public void setSoftKeyboardCallbackEnabled(boolean enabled) { + mIsSoftKeyboardCallbackEnabled = enabled; + } + } } final class WindowsForAccessibilityCallback implements @@ -3976,6 +4005,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public int mLastSentClientState = -1; + public int mSoftKeyboardShowMode = 0; + public boolean mIsAccessibilityEnabled; public boolean mIsTouchExplorationEnabled; public boolean mIsTextHighContrastEnabled; @@ -4048,6 +4079,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mIsEnhancedWebAccessibilityEnabled = false; mIsDisplayMagnificationEnabled = false; mIsAutoclickEnabled = false; + mSoftKeyboardShowMode = 0; } public void destroyUiAutomationService() { @@ -4105,6 +4137,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Uri mHighTextContrastUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED); + private final Uri mAccessibilitySoftKeyboardModeUri = Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE); + public AccessibilityContentObserver(Handler handler) { super(handler); } @@ -4135,6 +4170,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mDisplayColorMatrixUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mHighTextContrastUri, false, this, UserHandle.USER_ALL); + contentResolver.registerContentObserver( + mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL); } @Override @@ -4189,6 +4226,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (readHighTextContrastEnabledSettingLocked(userState)) { onUserStateChangedLocked(userState); } + } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)) { + if (readSoftKeyboardShowModeChangedLocked(userState)) { + onUserStateChangedLocked(userState); + } } } } diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 4a123df997205..d77def67ab52e 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -448,6 +448,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private int[] mSubtypeIds; private Locale mLastSystemLocale; private boolean mShowImeWithHardKeyboard; + private boolean mAccessibilityRequestingNoSoftKeyboard; private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); private final IPackageManager mIPackageManager; private final String mSlotIme; @@ -485,15 +486,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, userId); resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId); mRegistered = true; } @Override public void onChange(boolean selfChange, Uri uri) { - final Uri showImeUri = - Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD); + final Uri showImeUri = Settings.Secure.getUriFor( + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD); + final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE); synchronized (mMethodMap) { if (showImeUri.equals(uri)) { updateKeyboardFromSettingsLocked(); + } else if (accessibilityRequestingNoImeUri.equals(uri)) { + mAccessibilityRequestingNoSoftKeyboard = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + 0, mUserId) == 1; + if (mAccessibilityRequestingNoSoftKeyboard) { + final boolean showRequested = mShowRequested; + hideCurrentInputLocked(0, null); + mShowRequested = showRequested; + } else if (mShowRequested) { + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + } } else { boolean enabledChanged = false; String newEnabled = mSettings.getEnabledInputMethodsStr(); @@ -655,7 +672,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean changed = false; if (curIm != null) { - int change = isPackageDisappearing(curIm.getPackageName()); + int change = isPackageDisappearing(curIm.getPackageName()); if (change == PACKAGE_TEMPORARY_CHANGE || change == PACKAGE_PERMANENT_CHANGE) { ServiceInfo si = null; @@ -2073,12 +2090,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) { mShowRequested = true; - if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) { - mShowExplicitlyRequested = true; + if (mAccessibilityRequestingNoSoftKeyboard) { + return false; } + if ((flags&InputMethodManager.SHOW_FORCED) != 0) { mShowExplicitlyRequested = true; mShowForced = true; + } else if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) { + mShowExplicitlyRequested = true; } if (!mSystemReady) { @@ -3265,7 +3285,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub "Requires permission " + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - + long ident = Binder.clearCallingIdentity(); try { return setInputMethodEnabledLocked(id, enabled);