diff --git a/core/api/current.txt b/core/api/current.txt index f80c09596fb11..e7194df0f97d5 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -3087,6 +3087,7 @@ package android.accessibilityservice { public abstract class AccessibilityService extends android.app.Service { ctor public AccessibilityService(); + method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl); method public boolean clearCache(); method public boolean clearCachedSubtree(@NonNull android.view.accessibility.AccessibilityNodeInfo); method public final void disableSelf(); @@ -49980,6 +49981,7 @@ package android.view { public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable { ctor public SurfaceControlViewHost.SurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage); method public int describeContents(); + method @NonNull public android.view.SurfaceControl getSurfaceControl(); method public void notifyConfigurationChanged(@NonNull android.content.res.Configuration); method public void notifyDetachedFromWindow(); method public void release(); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 2fe5d5140371a..02a81acf791a0 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -56,7 +56,7 @@ import android.util.SparseArray; import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.SurfaceView; +import android.view.SurfaceControl; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityCache; @@ -2467,8 +2467,8 @@ public abstract class AccessibilityService extends Service { *

*

* Note: If the view with {@link AccessibilityNodeInfo#FOCUS_INPUT} - * is on an embedded view hierarchy which is embedded in a {@link SurfaceView} via - * {@link SurfaceView#setChildSurfacePackage}, there is a limitation that this API + * is on an embedded view hierarchy which is embedded in a {@link android.view.SurfaceView} via + * {@link android.view.SurfaceView#setChildSurfacePackage}, there is a limitation that this API * won't be able to find the node for the view. It's because views don't know about * the embedded hierarchies. Instead, you could traverse all the nodes to find the * focus. @@ -3379,4 +3379,28 @@ public abstract class AccessibilityService extends Service { controller.onStateChanged(state); } } + + /** + * Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the + * specified display. This type of overlay should be used for content that does not need to + * track the location and size of Views in the currently active app e.g. service configuration + * or general service UI. To remove this overlay and free the associated resources, use + * new SurfaceControl.Transaction().reparent(sc, null).apply();. + * + * @param displayId the display to which the SurfaceControl should be attached. + * @param sc the SurfaceControl containing the overlay content + */ + public void attachAccessibilityOverlayToDisplay(int displayId, @NonNull SurfaceControl sc) { + Preconditions.checkNotNull(sc, "SurfaceControl cannot be null"); + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getConnection(mConnectionId); + if (connection == null) { + return; + } + try { + connection.attachAccessibilityOverlayToDisplay(displayId, sc); + } catch (RemoteException re) { + throw new RuntimeException(re); + } + } } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index da14b50e64817..da13e7353056d 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -25,6 +25,7 @@ import android.os.Bundle; import android.os.RemoteCallback; import android.view.MagnificationSpec; import android.view.MotionEvent; +import android.view.SurfaceControl; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; @@ -155,4 +156,5 @@ interface IAccessibilityServiceConnection { void setInstalledAndEnabledServices(in List infos); List getInstalledAndEnabledServices(); + void attachAccessibilityOverlayToDisplay(int displayId, in SurfaceControl sc); } diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 3acb0534e388e..18897725e98f5 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -159,9 +159,11 @@ public class SurfaceControlViewHost { } /** - * Use {@link SurfaceView#setChildSurfacePackage} or manually fix - * accessibility (see SurfaceView implementation). - * @hide + * Returns the {@link android.view.SurfaceControl} associated with this SurfacePackage for + * cases where more control is required. + * + * @return the SurfaceControl associated with this SurfacePackage and its containing + * SurfaceControlViewHost */ public @NonNull SurfaceControl getSurfaceControl() { return mSurfaceControl; diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 7a5ab0458406a..6443ac18e7dce 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -27,6 +27,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteException; +import android.view.SurfaceControl; import android.window.ScreenCapture; import java.util.Collections; @@ -222,4 +223,7 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public List getInstalledAndEnabledServices() throws RemoteException { return null; } + + @Override + public void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc) {} } diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index c77b59717678c..786d4076191e5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -76,6 +76,7 @@ import android.util.SparseArray; import android.view.Display; import android.view.KeyEvent; import android.view.MagnificationSpec; +import android.view.SurfaceControl; import android.view.View; import android.view.WindowInfo; import android.view.accessibility.AccessibilityCache; @@ -289,6 +290,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ void requestImeLocked(AbstractAccessibilityServiceConnection connection); void unbindImeLocked(AbstractAccessibilityServiceConnection connection); + + void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc); } public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName, @@ -2486,4 +2489,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } -} \ No newline at end of file + + @Override + public void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc) { + mSystemSupport.attachAccessibilityOverlayToDisplay(displayId, sc); + } +} diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index e3ae03cbcdd80..87d166815323d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -109,6 +109,7 @@ import android.view.IWindow; import android.view.KeyEvent; import android.view.MagnificationSpec; import android.view.MotionEvent; +import android.view.SurfaceControl; import android.view.WindowInfo; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; @@ -291,12 +292,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private Point mTempPoint = new Point(); private boolean mIsAccessibilityButtonShown; - private boolean mInputBound; IRemoteAccessibilityInputConnection mRemoteInputConnection; EditorInfo mEditorInfo; boolean mRestarting; boolean mInputSessionRequested; + private SparseArray mA11yOverlayLayers = new SparseArray<>(); private AccessibilityUserState getCurrentUserStateLocked() { return getUserStateLocked(mCurrentUserId); @@ -3967,6 +3968,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { mDisplaysList.add(display); + mA11yOverlayLayers.put( + displayId, mWindowManagerService.getA11yOverlayLayer(displayId)); if (mInputFilter != null) { mInputFilter.onDisplayAdded(display); } @@ -3990,6 +3993,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (!removeDisplayFromList(displayId)) { return; } + mA11yOverlayLayers.remove(displayId); if (mInputFilter != null) { mInputFilter.onDisplayRemoved(displayId); } @@ -4691,4 +4695,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } } + + @Override + public void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc) { + mMainHandler.sendMessage( + obtainMessage( + AccessibilityManagerService::attachAccessibilityOverlayToDisplayInternal, + this, + displayId, + sc)); + } + + void attachAccessibilityOverlayToDisplayInternal(int displayId, SurfaceControl sc) { + if (!mA11yOverlayLayers.contains(displayId)) { + mA11yOverlayLayers.put(displayId, mWindowManagerService.getA11yOverlayLayer(displayId)); + } + SurfaceControl parent = mA11yOverlayLayers.get(displayId); + if (parent == null) { + Slog.e(LOG_TAG, "Unable to get accessibility overlay SurfaceControl."); + mA11yOverlayLayers.remove(displayId); + return; + } + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + transaction.reparent(sc, parent); + transaction.apply(); + transaction.close(); + } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 12efe0dc074f7..e65ea53ac4599 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -311,6 +311,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ private SurfaceControl mOverlayLayer; + /** A surfaceControl specifically for accessibility overlays. */ + private SurfaceControl mA11yOverlayLayer; + /** * The direct child layer of the display to put all non-overlay windows. This is also used for * screen rotation animation so that there is a parent layer to put the animation leash. @@ -1269,12 +1272,21 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp transaction.reparent(mOverlayLayer, mSurfaceControl); } + if (mA11yOverlayLayer == null) { + mA11yOverlayLayer = + b.setName("Accessibility Overlays").setParent(mSurfaceControl).build(); + } else { + transaction.reparent(mA11yOverlayLayer, mSurfaceControl); + } + transaction .setLayer(mSurfaceControl, 0) .setLayerStack(mSurfaceControl, mDisplayId) .show(mSurfaceControl) .setLayer(mOverlayLayer, Integer.MAX_VALUE) - .show(mOverlayLayer); + .show(mOverlayLayer) + .setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 1) + .show(mA11yOverlayLayer); } boolean isReady() { @@ -3250,6 +3262,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp setRemoteInsetsController(null); mWmService.mAnimator.removeDisplayLocked(mDisplayId); mOverlayLayer.release(); + mA11yOverlayLayer.release(); mWindowingLayer.release(); mInputMonitor.onDisplayRemoved(); mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this); @@ -5502,6 +5515,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mOverlayLayer; } + SurfaceControl getA11yOverlayLayer() { + return mA11yOverlayLayer; + } + SurfaceControl[] findRoundedCornerOverlays() { List roundedCornerOverlays = new ArrayList<>(); for (WindowToken token : mTokenMap.values()) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index bab3a0553e63c..1282acbc9e5a2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -903,4 +903,7 @@ public abstract class WindowManagerInternal { * could not be prepared and the session needs to be torn down. */ public abstract boolean setContentRecordingSession(ContentRecordingSession incomingSession); + + /** Returns the SurfaceControl accessibility services should use for accessibility overlays. */ + public abstract SurfaceControl getA11yOverlayLayer(int displayId); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index df343db8b3bf4..1289634d0a1d3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -736,7 +736,6 @@ public class WindowManagerService extends IWindowManager.Stub @VisibleForTesting final ContentRecordingController mContentRecordingController = new ContentRecordingController(); - @VisibleForTesting final class SettingsObserver extends ContentObserver { private final Uri mDisplayInversionEnabledUri = @@ -8301,6 +8300,17 @@ public class WindowManagerService extends IWindowManager.Stub return true; } } + + @Override + public SurfaceControl getA11yOverlayLayer(int displayId) { + synchronized (mGlobalLock) { + DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc != null) { + return dc.getA11yOverlayLayer(); + } + } + return null; + } } void registerAppFreezeListener(AppFreezeListener listener) {