diff options
12 files changed, 234 insertions, 36 deletions
diff --git a/api/current.txt b/api/current.txt index f14daa7..20cf89c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -34967,6 +34967,7 @@ package android.view { field public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0; // 0x0 field public static final int SOFT_INPUT_STATE_VISIBLE = 4; // 0x4 field public static final int TITLE_CHANGED = 64; // 0x40 + field public static final int TYPE_ACCESSIBILITY_OVERLAY = 2032; // 0x7f0 field public static final int TYPE_APPLICATION = 2; // 0x2 field public static final int TYPE_APPLICATION_ATTACHED_DIALOG = 1003; // 0x3eb field public static final int TYPE_APPLICATION_MEDIA = 1001; // 0x3e9 @@ -35386,6 +35387,7 @@ package android.view.accessibility { method public void recycle(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; + field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4 field public static final int TYPE_APPLICATION = 1; // 0x1 field public static final int TYPE_INPUT_METHOD = 2; // 0x2 field public static final int TYPE_SYSTEM = 3; // 0x3 diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 13ceb4a..1e1b33f 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -24,13 +24,18 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; +import android.view.Display; import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.os.HandlerCaller; +import com.android.internal.os.SomeArgs; import java.util.List; @@ -366,7 +371,7 @@ public abstract class AccessibilityService extends Service { public void onAccessibilityEvent(AccessibilityEvent event); public void onInterrupt(); public void onServiceConnected(); - public void onSetConnectionId(int connectionId); + public void init(int connectionId, IBinder windowToken); public boolean onGesture(int gestureId); public boolean onKeyEvent(KeyEvent event); } @@ -375,6 +380,10 @@ public abstract class AccessibilityService extends Service { private AccessibilityServiceInfo mInfo; + private IBinder mWindowToken; + + private WindowManager mWindowManager; + /** * Callback for {@link android.view.accessibility.AccessibilityEvent}s. * @@ -611,6 +620,18 @@ public abstract class AccessibilityService extends Service { } } + @Override + public Object getSystemService(String name) { + if (Context.WINDOW_SERVICE.equals(name)) { + if (mWindowManager == null) { + WindowManager wrapped = (WindowManager) super.getSystemService(name); + mWindowManager = new LocalWindowManager(wrapped); + } + return mWindowManager; + } + return super.getSystemService(name); + } + /** * Implement to return the implementation of the internal accessibility * service interface. @@ -634,8 +655,9 @@ public abstract class AccessibilityService extends Service { } @Override - public void onSetConnectionId( int connectionId) { + public void init(int connectionId, IBinder windowToken) { mConnectionId = connectionId; + mWindowToken = windowToken; } @Override @@ -658,7 +680,7 @@ public abstract class AccessibilityService extends Service { */ public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub implements HandlerCaller.Callback { - private static final int DO_SET_SET_CONNECTION = 1; + private static final int DO_INIT = 1; private static final int DO_ON_INTERRUPT = 2; private static final int DO_ON_ACCESSIBILITY_EVENT = 3; private static final int DO_ON_GESTURE = 4; @@ -677,9 +699,10 @@ public abstract class AccessibilityService extends Service { mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); } - public void setConnection(IAccessibilityServiceConnection connection, int connectionId) { - Message message = mCaller.obtainMessageIO(DO_SET_SET_CONNECTION, connectionId, - connection); + public void init(IAccessibilityServiceConnection connection, int connectionId, + IBinder windowToken) { + Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId, + connection, windowToken); mCaller.sendMessage(message); } @@ -730,20 +753,24 @@ public abstract class AccessibilityService extends Service { mCallback.onInterrupt(); } return; - case DO_SET_SET_CONNECTION: { + case DO_INIT: { mConnectionId = message.arg1; + SomeArgs args = (SomeArgs) message.obj; IAccessibilityServiceConnection connection = - (IAccessibilityServiceConnection) message.obj; + (IAccessibilityServiceConnection) args.arg1; + IBinder windowToken = (IBinder) args.arg2; + args.recycle(); if (connection != null) { AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, connection); - mCallback.onSetConnectionId(mConnectionId); + mCallback.init(mConnectionId, windowToken); mCallback.onServiceConnected(); } else { AccessibilityInteractionClient.getInstance().removeConnection( mConnectionId); + mConnectionId = AccessibilityInteractionClient.NO_ID; AccessibilityInteractionClient.getInstance().clearCache(); - mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID); + mCallback.init(AccessibilityInteractionClient.NO_ID, null); } } return; @@ -785,4 +812,53 @@ public abstract class AccessibilityService extends Service { } } } + + private class LocalWindowManager implements WindowManager { + private final WindowManager mImpl; + + private LocalWindowManager(WindowManager impl) { + mImpl = impl; + } + + @Override + public Display getDefaultDisplay() { + return mImpl.getDefaultDisplay(); + } + + @Override + public void addView(View view, ViewGroup.LayoutParams params) { + if (!(params instanceof WindowManager.LayoutParams)) { + throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); + } + WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params; + if (windowParams.type == LayoutParams.TYPE_ACCESSIBILITY_OVERLAY + && windowParams.token == null) { + windowParams.token = mWindowToken; + } + mImpl.addView(view, params); + } + + @Override + public void updateViewLayout(View view, ViewGroup.LayoutParams params) { + if (!(params instanceof WindowManager.LayoutParams)) { + throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); + } + WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params; + if (windowParams.type == LayoutParams.TYPE_ACCESSIBILITY_OVERLAY + && windowParams.token == null) { + windowParams.token = mWindowToken; + } + mImpl.updateViewLayout(view, params); + } + + @Override + public void removeViewImmediate(View view) { + mImpl.removeViewImmediate(view); + } + + @Override + public void removeView(View view) { + mImpl.removeView(view); + } + } } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index 6ce0219..8b503dd 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -28,7 +28,7 @@ import android.view.KeyEvent; */ oneway interface IAccessibilityServiceClient { - void setConnection(in IAccessibilityServiceConnection connection, int connectionId); + void init(in IAccessibilityServiceConnection connection, int connectionId, IBinder windowToken); void onAccessibilityEvent(in AccessibilityEvent event); diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 4aec9e0..b0dd70f 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -25,6 +25,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Point; import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -919,7 +920,7 @@ public final class UiAutomation { public IAccessibilityServiceClientImpl(Looper looper) { super(null, looper, new Callbacks() { @Override - public void onSetConnectionId(int connectionId) { + public void init(int connectionId, IBinder windowToken) { synchronized (mLock) { mConnectionId = connectionId; mLock.notifyAll(); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 3f84c9b..5b48c0d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -542,6 +542,19 @@ public interface WindowManager extends ViewManager { public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31; /** + * Window type: Windows that are overlaid <em>only</em> by an {@link + * android.accessibilityservice.AccessibilityService} for interception of + * user interactions without changing the windows an accessibility service + * can introspect. In particular, an accessibility service can introspect + * only windows that a sighted user can interact with which is they can touch + * these windows or can type into these windows. For example, if there + * is a full screen accessibility overlay that is touchable, the windows + * below it will be introspectable by an accessibility service regardless + * they are covered by a touchable window. + */ + public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32; + + /** * End of types of system windows. */ public static final int LAST_SYSTEM_WINDOW = 2999; diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java index 38e3723..f557b97 100644 --- a/core/java/android/view/WindowManagerInternal.java +++ b/core/java/android/view/WindowManagerInternal.java @@ -173,4 +173,20 @@ public abstract class WindowManagerInternal { * redrawn. */ public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout); + + /** + * Adds a window token for a given window type. + * + * @param token The token to add. + * @param type The window type. + */ + public abstract void addWindowToken(android.os.IBinder token, int type); + + /** + * Removes a window token. + * + * @param token The toke to remove. + * @param removeWindows Whether to also remove the windows associated with the token. + */ + public abstract void removeWindowToken(android.os.IBinder token, boolean removeWindows); } diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java index ad55f5f..e1942be 100644 --- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -51,11 +51,24 @@ public final class AccessibilityWindowInfo implements Parcelable { */ public static final int TYPE_SYSTEM = 3; + /** + * Window type: Windows that are overlaid <em>only</em> by an {@link + * android.accessibilityservice.AccessibilityService} for interception of + * user interactions without changing the windows an accessibility service + * can introspect. In particular, an accessibility service can introspect + * only windows that a sighted user can interact with which they can touch + * these windows or can type into these windows. For example, if there + * is a full screen accessibility overlay that is touchable, the windows + * below it will be introspectable by an accessibility service regardless + * they are covered by a touchable window. + */ + public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; + private static final int UNDEFINED = -1; private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0; private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1; - private static final int BOOLEAN_PROPERTY_ACCESSIBLITY_FOCUSED = 1 << 2; + private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 1 << 2; // Housekeeping. private static final int MAX_POOL_SIZE = 10; @@ -85,6 +98,7 @@ public final class AccessibilityWindowInfo implements Parcelable { * @see #TYPE_APPLICATION * @see #TYPE_INPUT_METHOD * @see #TYPE_SYSTEM + * @see #TYPE_ACCESSIBILITY_OVERLAY */ public int getType() { return mType; @@ -93,7 +107,7 @@ public final class AccessibilityWindowInfo implements Parcelable { /** * Sets the type of the window. * - * @param The type + * @param type The type * * @hide */ @@ -115,7 +129,7 @@ public final class AccessibilityWindowInfo implements Parcelable { * Sets the layer which determines the Z-order of the window. Windows * with greater layer appear on top of windows with lesser layer. * - * @param The window layer. + * @param layer The window layer. * * @hide */ @@ -174,7 +188,7 @@ public final class AccessibilityWindowInfo implements Parcelable { /** * Sets the unique window id. * - * @param windowId The window id. + * @param id The window id. * * @hide */ @@ -230,7 +244,7 @@ public final class AccessibilityWindowInfo implements Parcelable { * the user is currently touching or the window has input focus * and the user is not touching any window. * - * @param Whether this is the active window. + * @param active Whether this is the active window. * * @hide */ @@ -250,7 +264,7 @@ public final class AccessibilityWindowInfo implements Parcelable { /** * Sets if this window has input focus. * - * @param Whether has input focus. + * @param focused Whether has input focus. * * @hide */ @@ -264,18 +278,18 @@ public final class AccessibilityWindowInfo implements Parcelable { * @return Whether has accessibility focus. */ public boolean isAccessibilityFocused() { - return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBLITY_FOCUSED); + return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED); } /** * Sets if this window has accessibility focus. * - * @param Whether has accessibility focus. + * @param focused Whether has accessibility focus. * * @hide */ public void setAccessibilityFocused(boolean focused) { - setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBLITY_FOCUSED, focused); + setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused); } /** @@ -534,6 +548,9 @@ public final class AccessibilityWindowInfo implements Parcelable { case TYPE_SYSTEM: { return "TYPE_SYSTEM"; } + case TYPE_ACCESSIBILITY_OVERLAY: { + return "TYPE_ACCESSIBILITY_OVERLAY"; + } default: return "<UNKNOWN>"; } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 3ca147b..04e2fd1 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -1431,6 +1431,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TYPE_WALLPAPER: case TYPE_PRIVATE_PRESENTATION: case TYPE_VOICE_INTERACTION: + case TYPE_ACCESSIBILITY_OVERLAY: // The window manager will check these. break; case TYPE_PHONE: @@ -1660,15 +1661,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { // the drag layer: input for drag-and-drop is associated with this window, // which sits above all other focusable windows return 25; - case TYPE_SECURE_SYSTEM_OVERLAY: + case TYPE_ACCESSIBILITY_OVERLAY: + // overlay put by accessibility services to intercept user interaction return 26; - case TYPE_BOOT_PROGRESS: + case TYPE_SECURE_SYSTEM_OVERLAY: return 27; + case TYPE_BOOT_PROGRESS: + return 28; case TYPE_POINTER: // the (mouse) pointer layer - return 28; - case TYPE_HIDDEN_NAV_CONSUMER: return 29; + case TYPE_HIDDEN_NAV_CONSUMER: + return 30; } Log.e(TAG, "Unknown window type: " + type); return 2; @@ -1972,7 +1976,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } mKeyguardScrim = win; break; - } return WindowManagerGlobal.ADD_OKAY; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index e1a74d1..2781890 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1040,7 +1040,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void addServiceLocked(Service service, UserState userState) { try { - service.linkToOwnDeathLocked(); + service.onAdded(); userState.mBoundServices.add(service); userState.mComponentNameToServiceMap.put(service.mComponentName, service); } catch (RemoteException re) { @@ -1056,7 +1056,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void removeServiceLocked(Service service, UserState userState) { userState.mBoundServices.remove(service); userState.mComponentNameToServiceMap.remove(service.mComponentName); - service.unlinkToOwnDeathLocked(); + service.onRemoved(); } /** @@ -1931,6 +1931,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final ResolveInfo mResolveInfo; + final IBinder mOverlayWindowToken = new Binder(); + // the events pending events to be dispatched to this service final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<>(); @@ -2112,7 +2114,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mBindingServices.remove(mComponentName); mWasConnectedAndDied = false; try { - mServiceInterface.setConnection(this, mId); + mServiceInterface.init(this, mId, mOverlayWindowToken); onUserStateChangedLocked(userState); } catch (RemoteException re) { Slog.w(LOG_TAG, "Error while setting connection for service: " @@ -2602,6 +2604,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { /* do nothing - #binderDied takes care */ } + public void onAdded() throws RemoteException { + linkToOwnDeathLocked(); + final long identity = Binder.clearCallingIdentity(); + try { + mWindowManagerService.addWindowToken(mOverlayWindowToken, + WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + public void onRemoved() { + final long identity = Binder.clearCallingIdentity(); + try { + mWindowManagerService.removeWindowToken(mOverlayWindowToken, true); + } finally { + Binder.restoreCallingIdentity(identity); + } + unlinkToOwnDeathLocked(); + } + public void linkToOwnDeathLocked() throws RemoteException { mService.linkToDeath(this, 0); } @@ -2614,7 +2637,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { try { // Clear the proxy in the other process so this // IAccessibilityServiceConnection can be garbage collected. - mServiceInterface.setConnection(null, mId); + mServiceInterface.init(null, mId, null); } catch (RemoteException re) { /* ignore */ } @@ -3164,6 +3187,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return AccessibilityWindowInfo.TYPE_SYSTEM; } + case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY: { + return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY; + } + default: { return -1; } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index fde703d..f947b6a 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -992,8 +992,7 @@ final class AccessibilityController { final int flags = windowState.mAttrs.flags; - // If the window is not touchable, do not report it but take into account - // the space it takes since the content behind it cannot be touched. + // If the window is not touchable - ignore. if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { continue; } @@ -1014,9 +1013,14 @@ final class AccessibilityController { } } - // Account for the space this window takes. - unaccountedSpace.op(boundsInScreen, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); + // Account for the space this window takes if the window + // is not an accessibility overlay which does not change + // the reported windows. + if (windowState.mAttrs.type == WindowManager.LayoutParams + .TYPE_ACCESSIBILITY_OVERLAY) { + unaccountedSpace.op(boundsInScreen, unaccountedSpace, + Region.Op.REVERSE_DIFFERENCE); + } // We figured out what is touchable for the entire screen - done. if (unaccountedSpace.isEmpty()) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 57d793f..3fee608 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2337,6 +2337,11 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } + if (type == TYPE_ACCESSIBILITY_OVERLAY) { + Slog.w(TAG, "Attempted to add Accessibility overlay window with unknown token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { @@ -2380,6 +2385,12 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } + } else if (type == TYPE_ACCESSIBILITY_OVERLAY) { + if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) { + Slog.w(TAG, "Attempted to add Accessibility overlay window with bad token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } } else if (token.appWindowToken != null) { Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type); // It is not valid to use an app token with other system types; we will @@ -11627,5 +11638,23 @@ public class WindowManagerService extends IWindowManager.Stub checkDrawnWindowsLocked(); } } + + @Override + public void addWindowToken(IBinder token, int type) { + WindowManagerService.this.addWindowToken(token, type); + } + + @Override + public void removeWindowToken(IBinder token, boolean removeWindows) { + synchronized(mWindowMap) { + if (removeWindows) { + WindowToken wtoken = mTokenMap.remove(token); + if (wtoken != null) { + wtoken.removeAllWindows(); + } + } + WindowManagerService.this.removeWindowToken(token); + } + } } } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 2267123..1a672e6 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -17,6 +17,7 @@ package com.android.server.wm; import android.os.IBinder; +import android.util.Slog; import java.io.PrintWriter; @@ -29,7 +30,7 @@ import java.io.PrintWriter; class WindowToken { // The window manager! final WindowManagerService service; - + // The actual token. final IBinder token; @@ -77,6 +78,15 @@ class WindowToken { explicit = _explicit; } + void removeAllWindows() { + for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { + WindowState win = windows.get(winNdx); + if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) Slog.w(WindowManagerService.TAG, + "removeAllWindows: removing win=" + win); + win.mService.removeWindowLocked(win.mSession, win); + } + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("windows="); pw.println(windows); pw.print(prefix); pw.print("windowType="); pw.print(windowType); |