Merge "Add API for IME control by Accessibility Services."

This commit is contained in:
Anna Galusza
2016-01-29 16:58:07 +00:00
committed by Android (Google) Code Review
10 changed files with 576 additions and 154 deletions

View File

@@ -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<android.view.accessibility.AccessibilityWindowInfo> 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);

View File

@@ -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<android.view.accessibility.AccessibilityWindowInfo> 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);

View File

@@ -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<android.view.accessibility.AccessibilityWindowInfo> 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);

View File

@@ -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<OnMagnificationChangedListener, Handler> 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<OnMagnificationChangedListener, Handler> 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<OnMagnificationChangedListener, Handler> 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<OnShowModeChangedListener, Handler> 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<OnShowModeChangedListener, Handler> 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).
* <p>
* <strong>Note:</strong> 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);

View File

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

View File

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

View File

@@ -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 */

View File

@@ -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

View File

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

View File

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