Merge "Support accessibility on embedded hierarchies (2/n)"
This commit is contained in:
81
core/java/android/view/AccessibilityEmbeddedConnection.java
Normal file
81
core/java/android/view/AccessibilityEmbeddedConnection.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Matrix;
|
||||
import android.os.IBinder;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.IAccessibilityEmbeddedConnection;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* This class is an interface this ViewRootImpl provides to the host view to the latter
|
||||
* can interact with the view hierarchy in SurfaceControlViewHost.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
final class AccessibilityEmbeddedConnection extends IAccessibilityEmbeddedConnection.Stub {
|
||||
private final WeakReference<ViewRootImpl> mViewRootImpl;
|
||||
|
||||
AccessibilityEmbeddedConnection(ViewRootImpl viewRootImpl) {
|
||||
mViewRootImpl = new WeakReference<>(viewRootImpl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable IBinder associateEmbeddedHierarchy(@NonNull IBinder host, int hostViewId) {
|
||||
final ViewRootImpl viewRootImpl = mViewRootImpl.get();
|
||||
if (viewRootImpl != null) {
|
||||
final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
|
||||
viewRootImpl.mContext);
|
||||
viewRootImpl.mAttachInfo.mLeashedParentToken = host;
|
||||
viewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId = hostViewId;
|
||||
if (accessibilityManager.isEnabled()) {
|
||||
accessibilityManager.associateEmbeddedHierarchy(host, viewRootImpl.mLeashToken);
|
||||
}
|
||||
return viewRootImpl.mLeashToken;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disassociateEmbeddedHierarchy() {
|
||||
final ViewRootImpl viewRootImpl = mViewRootImpl.get();
|
||||
if (viewRootImpl != null) {
|
||||
final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
|
||||
viewRootImpl.mContext);
|
||||
viewRootImpl.mAttachInfo.mLeashedParentToken = null;
|
||||
viewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId = View.NO_ID;
|
||||
viewRootImpl.mAttachInfo.mLocationInParentDisplay.set(0, 0);
|
||||
if (accessibilityManager.isEnabled()) {
|
||||
accessibilityManager.disassociateEmbeddedHierarchy(viewRootImpl.mLeashToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScreenMatrix(float[] matrixValues) {
|
||||
final ViewRootImpl viewRootImpl = mViewRootImpl.get();
|
||||
if (viewRootImpl != null) {
|
||||
// TODO(b/148821260): Implement the rest of matrix values.
|
||||
viewRootImpl.mAttachInfo.mLocationInParentDisplay.set(
|
||||
(int) matrixValues[Matrix.MTRANS_X], (int) matrixValues[Matrix.MTRANS_Y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -855,6 +855,36 @@ public final class AccessibilityInteractionController {
|
||||
return mViewRootImpl.mAttachInfo.mLocationInParentDisplay.equals(0, 0);
|
||||
}
|
||||
|
||||
private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) {
|
||||
if (infos == null || shouldBypassAssociateLeashedParent()) {
|
||||
return;
|
||||
}
|
||||
final int infoCount = infos.size();
|
||||
for (int i = 0; i < infoCount; i++) {
|
||||
final AccessibilityNodeInfo info = infos.get(i);
|
||||
associateLeashedParentIfNeeded(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) {
|
||||
if (info == null || shouldBypassAssociateLeashedParent()) {
|
||||
return;
|
||||
}
|
||||
// The node id of root node in embedded maybe not be ROOT_NODE_ID so we compare the id
|
||||
// with root view.
|
||||
if (mViewRootImpl.mView.getAccessibilityViewId()
|
||||
!= AccessibilityNodeInfo.getAccessibilityViewId(info.getSourceNodeId())) {
|
||||
return;
|
||||
}
|
||||
info.setLeashedParent(mViewRootImpl.mAttachInfo.mLeashedParentToken,
|
||||
mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId);
|
||||
}
|
||||
|
||||
private boolean shouldBypassAssociateLeashedParent() {
|
||||
return (mViewRootImpl.mAttachInfo.mLeashedParentToken == null
|
||||
&& mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId == View.NO_ID);
|
||||
}
|
||||
|
||||
private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
|
||||
MagnificationSpec spec) {
|
||||
if (info == null) {
|
||||
@@ -914,6 +944,7 @@ public final class AccessibilityInteractionController {
|
||||
MagnificationSpec spec, Region interactiveRegion) {
|
||||
try {
|
||||
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
|
||||
associateLeashedParentIfNeeded(infos);
|
||||
adjustBoundsInScreenIfNeeded(infos);
|
||||
// To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
|
||||
// then impact the visibility result, we need to adjust visibility before apply scale.
|
||||
@@ -935,6 +966,7 @@ public final class AccessibilityInteractionController {
|
||||
MagnificationSpec spec, Region interactiveRegion) {
|
||||
try {
|
||||
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
|
||||
associateLeashedParentIfNeeded(info);
|
||||
adjustBoundsInScreenIfNeeded(info);
|
||||
// To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
|
||||
// then impact the visibility result, we need to adjust visibility before apply scale.
|
||||
|
||||
@@ -28988,6 +28988,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
*/
|
||||
OnContentApplyWindowInsetsListener mContentOnApplyWindowInsetsListener;
|
||||
|
||||
/**
|
||||
* The leash token of this view's parent when it's in an embedded hierarchy that is
|
||||
* re-parented to another window.
|
||||
*/
|
||||
IBinder mLeashedParentToken;
|
||||
|
||||
/**
|
||||
* The accessibility view id of this view's parent when it's in an embedded
|
||||
* hierarchy that is re-parented to another window.
|
||||
*/
|
||||
int mLeashedParentAccessibilityViewId;
|
||||
|
||||
/**
|
||||
* Creates a new set of attachment information with the specified
|
||||
* events handler and thread.
|
||||
|
||||
@@ -135,6 +135,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
||||
import android.view.accessibility.AccessibilityNodeProvider;
|
||||
import android.view.accessibility.AccessibilityWindowInfo;
|
||||
import android.view.accessibility.IAccessibilityEmbeddedConnection;
|
||||
import android.view.accessibility.IAccessibilityInteractionConnection;
|
||||
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
@@ -355,6 +356,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
|
||||
final W mWindow;
|
||||
|
||||
final IBinder mLeashToken;
|
||||
|
||||
final int mTargetSdkVersion;
|
||||
|
||||
int mSeq;
|
||||
@@ -652,6 +655,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
|
||||
private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
|
||||
|
||||
private IAccessibilityEmbeddedConnection mEmbeddedConnection;
|
||||
|
||||
static final class SystemUiVisibilityInfo {
|
||||
int seq;
|
||||
int globalVisibility;
|
||||
@@ -685,6 +690,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
mVisRect = new Rect();
|
||||
mWinFrame = new Rect();
|
||||
mWindow = new W(this);
|
||||
mLeashToken = new Binder();
|
||||
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
|
||||
mViewVisibility = View.GONE;
|
||||
mTransparentRegion = new Region();
|
||||
@@ -9157,6 +9163,10 @@ public final class ViewRootImpl implements ViewParent,
|
||||
focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
|
||||
}
|
||||
}
|
||||
if (mAttachInfo.mLeashedParentToken != null) {
|
||||
mAccessibilityManager.associateEmbeddedHierarchy(
|
||||
mAttachInfo.mLeashedParentToken, mLeashToken);
|
||||
}
|
||||
} else {
|
||||
ensureNoConnection();
|
||||
mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget();
|
||||
@@ -9169,6 +9179,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
if (!registered) {
|
||||
mAttachInfo.mAccessibilityWindowId =
|
||||
mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
|
||||
mLeashToken,
|
||||
mContext.getPackageName(),
|
||||
new AccessibilityInteractionConnection(ViewRootImpl.this));
|
||||
}
|
||||
@@ -9355,6 +9366,17 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an accessibility embedded connection interface for this ViewRootImpl.
|
||||
* @hide
|
||||
*/
|
||||
public IAccessibilityEmbeddedConnection getEmbeddedConnection() {
|
||||
if (mEmbeddedConnection == null) {
|
||||
mEmbeddedConnection = new AccessibilityEmbeddedConnection(ViewRootImpl.this);
|
||||
}
|
||||
return mEmbeddedConnection;
|
||||
}
|
||||
|
||||
private class SendWindowContentChangedAccessibilityEvent implements Runnable {
|
||||
private int mChangeTypes = 0;
|
||||
|
||||
|
||||
@@ -1085,6 +1085,50 @@ public final class AccessibilityManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the connection between the host View and the embedded SurfaceControlViewHost.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
|
||||
final IAccessibilityManager service;
|
||||
synchronized (mLock) {
|
||||
service = getServiceLocked();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
service.associateEmbeddedHierarchy(host, embedded);
|
||||
} catch (RemoteException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disassociate the connection between the host View and the embedded SurfaceControlViewHost.
|
||||
* The given token could be either from host side or embedded side.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
|
||||
if (token == null) {
|
||||
return;
|
||||
}
|
||||
final IAccessibilityManager service;
|
||||
synchronized (mLock) {
|
||||
service = getServiceLocked();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
service.disassociateEmbeddedHierarchy(token);
|
||||
} catch (RemoteException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current state and notifies listeners, if necessary.
|
||||
*
|
||||
@@ -1147,11 +1191,12 @@ public final class AccessibilityManager {
|
||||
/**
|
||||
* Adds an accessibility interaction connection interface for a given window.
|
||||
* @param windowToken The window token to which a connection is added.
|
||||
* @param leashToken The leash token to which a connection is added.
|
||||
* @param connection The connection.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public int addAccessibilityInteractionConnection(IWindow windowToken,
|
||||
public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
|
||||
String packageName, IAccessibilityInteractionConnection connection) {
|
||||
final IAccessibilityManager service;
|
||||
final int userId;
|
||||
@@ -1163,8 +1208,8 @@ public final class AccessibilityManager {
|
||||
userId = mUserId;
|
||||
}
|
||||
try {
|
||||
return service.addAccessibilityInteractionConnection(windowToken, connection,
|
||||
packageName, userId);
|
||||
return service.addAccessibilityInteractionConnection(windowToken, leashToken,
|
||||
connection, packageName, userId);
|
||||
} catch (RemoteException re) {
|
||||
Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view.accessibility;
|
||||
|
||||
/**
|
||||
* Interface used by host View to talk to the view root of the embedded SurfaceControlViewHost
|
||||
* that actually implements the functionality.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
interface IAccessibilityEmbeddedConnection {
|
||||
|
||||
IBinder associateEmbeddedHierarchy(IBinder hostToken, int sourceId);
|
||||
|
||||
void disassociateEmbeddedHierarchy();
|
||||
|
||||
oneway void setScreenMatrix(in float[] matrixValues);
|
||||
}
|
||||
@@ -47,7 +47,7 @@ interface IAccessibilityManager {
|
||||
@UnsupportedAppUsage
|
||||
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
|
||||
|
||||
int addAccessibilityInteractionConnection(IWindow windowToken,
|
||||
int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
|
||||
in IAccessibilityInteractionConnection connection,
|
||||
String packageName, int userId);
|
||||
|
||||
@@ -88,4 +88,8 @@ interface IAccessibilityManager {
|
||||
oneway void registerSystemAction(in RemoteAction action, int actionId);
|
||||
oneway void unregisterSystemAction(int actionId);
|
||||
oneway void setWindowMagnificationConnection(in IWindowMagnificationConnection connection);
|
||||
|
||||
void associateEmbeddedHierarchy(IBinder host, IBinder embedded);
|
||||
|
||||
void disassociateEmbeddedHierarchy(IBinder token);
|
||||
}
|
||||
|
||||
@@ -1128,8 +1128,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
|
||||
@Override
|
||||
public int getWindowIdForLeashToken(@NonNull IBinder token) {
|
||||
synchronized (mLock) {
|
||||
// TODO: Add a method to lookup window ID by given leash token.
|
||||
return -1;
|
||||
return mA11yWindowManager.getWindowIdLocked(token);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -761,11 +761,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addAccessibilityInteractionConnection(IWindow windowToken,
|
||||
public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
|
||||
IAccessibilityInteractionConnection connection, String packageName,
|
||||
int userId) throws RemoteException {
|
||||
return mA11yWindowManager.addAccessibilityInteractionConnection(
|
||||
windowToken, connection, packageName, userId);
|
||||
windowToken, leashToken, connection, packageName, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2642,6 +2642,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
|
||||
synchronized (mLock) {
|
||||
mA11yWindowManager.associateEmbeddedHierarchyLocked(host, embedded);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
|
||||
synchronized (mLock) {
|
||||
mA11yWindowManager.disassociateEmbeddedHierarchyLocked(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
|
||||
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
|
||||
|
||||
@@ -443,8 +443,8 @@ public class AccessibilitySecurityPolicy {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// TODO: Check parent windowId if the giving windowId is from embedded view hierarchy.
|
||||
if (windowId == mAccessibilityWindowManager.getActiveWindowId(userId)) {
|
||||
if (mAccessibilityWindowManager.resolveParentWindowIdLocked(windowId)
|
||||
== mAccessibilityWindowManager.getActiveWindowId(userId)) {
|
||||
return true;
|
||||
}
|
||||
return mAccessibilityWindowManager.findA11yWindowInfoByIdLocked(windowId) != null;
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
@@ -99,6 +100,19 @@ public class AccessibilityWindowManager {
|
||||
private final SparseArray<DisplayWindowsObserver> mDisplayWindowsObservers =
|
||||
new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Map of host view and embedded hierarchy, mapping from leash token of its ViewRootImpl.
|
||||
* The key is the token from embedded hierarchy, and the value is the token from its host.
|
||||
*/
|
||||
private final ArrayMap<IBinder, IBinder> mHostEmbeddedMap = new ArrayMap<>();
|
||||
|
||||
/**
|
||||
* Map of window id and view hierarchy.
|
||||
* The key is the window id when the ViewRootImpl register to accessibility, and the value is
|
||||
* its leash token.
|
||||
*/
|
||||
private final SparseArray<IBinder> mWindowIdMap = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to
|
||||
* receive {@link WindowInfo}s from window manager when there's an accessibility change in
|
||||
@@ -913,19 +927,21 @@ public class AccessibilityWindowManager {
|
||||
* Adds accessibility interaction connection according to given window token, package name and
|
||||
* window token.
|
||||
*
|
||||
* @param windowToken The window token of accessibility interaction connection
|
||||
* @param window The window token of accessibility interaction connection
|
||||
* @param leashToken The leash token of accessibility interaction connection
|
||||
* @param connection The accessibility interaction connection
|
||||
* @param packageName The package name
|
||||
* @param userId The userId
|
||||
* @return The windowId of added connection
|
||||
* @throws RemoteException
|
||||
*/
|
||||
public int addAccessibilityInteractionConnection(@NonNull IWindow windowToken,
|
||||
@NonNull IAccessibilityInteractionConnection connection, @NonNull String packageName,
|
||||
int userId) throws RemoteException {
|
||||
public int addAccessibilityInteractionConnection(@NonNull IWindow window,
|
||||
@NonNull IBinder leashToken, @NonNull IAccessibilityInteractionConnection connection,
|
||||
@NonNull String packageName, int userId) throws RemoteException {
|
||||
final int windowId;
|
||||
boolean shouldComputeWindows = false;
|
||||
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken.asBinder());
|
||||
final IBinder token = window.asBinder();
|
||||
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token);
|
||||
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
|
||||
@@ -947,35 +963,33 @@ public class AccessibilityWindowManager {
|
||||
windowId, connection, packageName, resolvedUid, UserHandle.USER_ALL);
|
||||
wrapper.linkToDeath();
|
||||
mGlobalInteractionConnections.put(windowId, wrapper);
|
||||
mGlobalWindowTokens.put(windowId, windowToken.asBinder());
|
||||
mGlobalWindowTokens.put(windowId, token);
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "Added global connection for pid:" + Binder.getCallingPid()
|
||||
+ " with windowId: " + windowId + " and token: "
|
||||
+ windowToken.asBinder());
|
||||
+ " with windowId: " + windowId + " and token: " + token);
|
||||
}
|
||||
} else {
|
||||
RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection(
|
||||
windowId, connection, packageName, resolvedUid, resolvedUserId);
|
||||
wrapper.linkToDeath();
|
||||
getInteractionConnectionsForUserLocked(resolvedUserId).put(windowId, wrapper);
|
||||
getWindowTokensForUserLocked(resolvedUserId).put(windowId, windowToken.asBinder());
|
||||
getWindowTokensForUserLocked(resolvedUserId).put(windowId, token);
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "Added user connection for pid:" + Binder.getCallingPid()
|
||||
+ " with windowId: " + windowId
|
||||
+ " and token: " + windowToken.asBinder());
|
||||
+ " with windowId: " + windowId + " and token: " + token);
|
||||
}
|
||||
}
|
||||
|
||||
if (isTrackingWindowsLocked(displayId)) {
|
||||
shouldComputeWindows = true;
|
||||
}
|
||||
registerIdLocked(leashToken, windowId);
|
||||
}
|
||||
if (shouldComputeWindows) {
|
||||
mWindowManagerInternal.computeWindowsForAccessibility(displayId);
|
||||
}
|
||||
|
||||
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
|
||||
windowToken.asBinder(), windowId);
|
||||
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId);
|
||||
return windowId;
|
||||
}
|
||||
|
||||
@@ -1098,6 +1112,7 @@ public class AccessibilityWindowManager {
|
||||
* Invoked when accessibility interaction connection of window is removed.
|
||||
*
|
||||
* @param windowId Removed windowId
|
||||
* @param binder Removed window token
|
||||
*/
|
||||
private void onAccessibilityInteractionConnectionRemovedLocked(
|
||||
int windowId, @Nullable IBinder binder) {
|
||||
@@ -1110,6 +1125,7 @@ public class AccessibilityWindowManager {
|
||||
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
|
||||
binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
|
||||
}
|
||||
unregisterIdLocked(windowId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1132,7 +1148,7 @@ public class AccessibilityWindowManager {
|
||||
* Returns the userId that owns the given window token, {@link UserHandle#USER_NULL}
|
||||
* if not found.
|
||||
*
|
||||
* @param windowToken The winodw token
|
||||
* @param windowToken The window token
|
||||
* @return The userId
|
||||
*/
|
||||
public int getWindowOwnerUserId(@NonNull IBinder windowToken) {
|
||||
@@ -1160,6 +1176,50 @@ public class AccessibilityWindowManager {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish the relationship between the host and the embedded view hierarchy.
|
||||
*
|
||||
* @param host The token of host hierarchy
|
||||
* @param embedded The token of the embedded hierarchy
|
||||
*/
|
||||
public void associateEmbeddedHierarchyLocked(@NonNull IBinder host, @NonNull IBinder embedded) {
|
||||
// Use embedded window as key, since one host window may have multiple embedded windows.
|
||||
associateLocked(embedded, host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the relationship by given token.
|
||||
*
|
||||
* @param token The token
|
||||
*/
|
||||
public void disassociateEmbeddedHierarchyLocked(@NonNull IBinder token) {
|
||||
disassociateLocked(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent windowId of the window according to the specified windowId.
|
||||
*
|
||||
* @param windowId The windowId to check
|
||||
* @return The windowId of the parent window, or self if no parent exists
|
||||
*/
|
||||
public int resolveParentWindowIdLocked(int windowId) {
|
||||
final IBinder token = getTokenLocked(windowId);
|
||||
if (token == null) {
|
||||
return windowId;
|
||||
}
|
||||
final IBinder resolvedToken = resolveTopParentTokenLocked(token);
|
||||
final int resolvedWindowId = getWindowIdLocked(resolvedToken);
|
||||
return resolvedWindowId != -1 ? resolvedWindowId : windowId;
|
||||
}
|
||||
|
||||
private IBinder resolveTopParentTokenLocked(IBinder token) {
|
||||
final IBinder hostToken = getHostTokenLocked(token);
|
||||
if (hostToken == null) {
|
||||
return token;
|
||||
}
|
||||
return resolveTopParentTokenLocked(hostToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes partial interactive region of given windowId.
|
||||
*
|
||||
@@ -1357,6 +1417,7 @@ public class AccessibilityWindowManager {
|
||||
*/
|
||||
@Nullable
|
||||
public AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) {
|
||||
windowId = resolveParentWindowIdLocked(windowId);
|
||||
final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
|
||||
if (observer != null) {
|
||||
return observer.findA11yWindowInfoByIdLocked(windowId);
|
||||
@@ -1583,6 +1644,88 @@ public class AccessibilityWindowManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the token of the embedded view hierarchy to the host view hierarchy.
|
||||
*
|
||||
* @param embedded The leash token from the view root of embedded hierarchy
|
||||
* @param host The leash token from the view root of host hierarchy
|
||||
*/
|
||||
void associateLocked(IBinder embedded, IBinder host) {
|
||||
mHostEmbeddedMap.put(embedded, host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the relationship of given token.
|
||||
*
|
||||
* @param token The leash token
|
||||
*/
|
||||
void disassociateLocked(IBinder token) {
|
||||
mHostEmbeddedMap.remove(token);
|
||||
for (int i = mHostEmbeddedMap.size() - 1; i >= 0; i--) {
|
||||
if (mHostEmbeddedMap.valueAt(i).equals(token)) {
|
||||
mHostEmbeddedMap.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the leash token with its windowId.
|
||||
*
|
||||
* @param token The token.
|
||||
* @param windowId The windowID.
|
||||
*/
|
||||
void registerIdLocked(IBinder token, int windowId) {
|
||||
mWindowIdMap.put(windowId, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the windowId and also disassociate its token.
|
||||
*
|
||||
* @param windowId The windowID
|
||||
*/
|
||||
void unregisterIdLocked(int windowId) {
|
||||
final IBinder token = mWindowIdMap.get(windowId);
|
||||
if (token == null) {
|
||||
return;
|
||||
}
|
||||
disassociateLocked(token);
|
||||
mWindowIdMap.remove(windowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the leash token by given windowID.
|
||||
*
|
||||
* @param windowId The windowID.
|
||||
* @return The token, or {@code NULL} if this windowID doesn't exist
|
||||
*/
|
||||
IBinder getTokenLocked(int windowId) {
|
||||
return mWindowIdMap.get(windowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the windowId by given leash token.
|
||||
*
|
||||
* @param token The token
|
||||
* @return The windowID, or -1 if the token doesn't exist
|
||||
*/
|
||||
int getWindowIdLocked(IBinder token) {
|
||||
final int index = mWindowIdMap.indexOfValue(token);
|
||||
if (index == -1) {
|
||||
return index;
|
||||
}
|
||||
return mWindowIdMap.keyAt(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the leash token of the host hierarchy by given token.
|
||||
*
|
||||
* @param token The token
|
||||
* @return The token of host hierarchy, or {@code NULL} if no host exists
|
||||
*/
|
||||
IBinder getHostTokenLocked(IBinder token) {
|
||||
return mHostEmbeddedMap.get(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps all {@link AccessibilityWindowInfo}s here.
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,9 @@ import static org.hamcrest.Matchers.emptyArray;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.AdditionalAnswers.returnsFirstArg;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -133,6 +135,8 @@ public class AccessibilitySecurityPolicyTest {
|
||||
mA11ySecurityPolicy = new AccessibilitySecurityPolicy(mMockContext, mMockA11yUserManager);
|
||||
mA11ySecurityPolicy.setAccessibilityWindowManager(mMockA11yWindowManager);
|
||||
mA11ySecurityPolicy.setAppWidgetManager(mMockAppWidgetManager);
|
||||
|
||||
when(mMockA11yWindowManager.resolveParentWindowIdLocked(anyInt())).then(returnsFirstArg());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -88,6 +88,10 @@ public class AccessibilityWindowManagerTest {
|
||||
private static final int DEFAULT_FOCUSED_INDEX = 1;
|
||||
private static final int SCREEN_WIDTH = 1080;
|
||||
private static final int SCREEN_HEIGHT = 1920;
|
||||
private static final int INVALID_ID = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
|
||||
private static final int HOST_WINDOW_ID = 10;
|
||||
private static final int EMBEDDED_WINDOW_ID = 11;
|
||||
private static final int OTHER_WINDOW_ID = 12;
|
||||
|
||||
private AccessibilityWindowManager mA11yWindowManager;
|
||||
// Window manager will support multiple focused window if config_perDisplayFocusEnabled is true,
|
||||
@@ -117,6 +121,10 @@ public class AccessibilityWindowManagerTest {
|
||||
@Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
|
||||
@Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
|
||||
|
||||
@Mock private IBinder mMockHostToken;
|
||||
@Mock private IBinder mMockEmbeddedToken;
|
||||
@Mock private IBinder mMockInvalidToken;
|
||||
|
||||
@Before
|
||||
public void setUp() throws RemoteException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -140,6 +148,8 @@ public class AccessibilityWindowManagerTest {
|
||||
// AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged.
|
||||
// Resets it for mockito verify of further test case.
|
||||
Mockito.reset(mMockA11yEventSender);
|
||||
|
||||
registerLeashedTokenAndWindowId();
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -406,6 +416,51 @@ public class AccessibilityWindowManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveParentWindowId_windowIsNotEmbedded_shouldReturnGivenId()
|
||||
throws RemoteException {
|
||||
final int windowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false,
|
||||
Mockito.mock(IBinder.class), USER_SYSTEM_ID);
|
||||
assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveParentWindowId_windowIsNotRegistered_shouldReturnGivenId() {
|
||||
final int windowId = -1;
|
||||
assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveParentWindowId_windowIsAssociated_shouldReturnParentWindowId()
|
||||
throws RemoteException {
|
||||
final IBinder mockHostToken = Mockito.mock(IBinder.class);
|
||||
final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
|
||||
final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
|
||||
false, mockHostToken, USER_SYSTEM_ID);
|
||||
final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
|
||||
false, mockEmbeddedToken, USER_SYSTEM_ID);
|
||||
mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
|
||||
final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
|
||||
embeddedWindowId);
|
||||
assertEquals(hostWindowId, resolvedWindowId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveParentWindowId_windowIsDisassociated_shouldReturnGivenId()
|
||||
throws RemoteException {
|
||||
final IBinder mockHostToken = Mockito.mock(IBinder.class);
|
||||
final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
|
||||
final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
|
||||
false, mockHostToken, USER_SYSTEM_ID);
|
||||
final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
|
||||
false, mockEmbeddedToken, USER_SYSTEM_ID);
|
||||
mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
|
||||
mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken);
|
||||
final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
|
||||
embeddedWindowId);
|
||||
assertEquals(embeddedWindowId, resolvedWindowId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() {
|
||||
// Updates top 2 z-order WindowInfo are whole visible.
|
||||
@@ -726,6 +781,64 @@ public class AccessibilityWindowManagerTest {
|
||||
token.asBinder(), -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHostTokenLocked_hierarchiesAreAssociated_shouldReturnHostToken() {
|
||||
mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
|
||||
final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
|
||||
assertEquals(hostToken, mMockHostToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHostTokenLocked_hierarchiesAreNotAssociated_shouldReturnNull() {
|
||||
final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
|
||||
assertNull(hostToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHostTokenLocked_embeddedHierarchiesAreDisassociated_shouldReturnNull() {
|
||||
mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
|
||||
mA11yWindowManager.disassociateLocked(mMockEmbeddedToken);
|
||||
final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
|
||||
assertNull(hostToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHostTokenLocked_hostHierarchiesAreDisassociated_shouldReturnNull() {
|
||||
mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
|
||||
mA11yWindowManager.disassociateLocked(mMockHostToken);
|
||||
final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockHostToken);
|
||||
assertNull(hostToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWindowIdLocked_windowIsRegistered_shouldReturnWindowId() {
|
||||
final int windowId = mA11yWindowManager.getWindowIdLocked(mMockHostToken);
|
||||
assertEquals(windowId, HOST_WINDOW_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWindowIdLocked_windowIsNotRegistered_shouldReturnInvalidWindowId() {
|
||||
final int windowId = mA11yWindowManager.getWindowIdLocked(mMockInvalidToken);
|
||||
assertEquals(windowId, INVALID_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTokenLocked_windowIsRegistered_shouldReturnToken() {
|
||||
final IBinder token = mA11yWindowManager.getTokenLocked(HOST_WINDOW_ID);
|
||||
assertEquals(token, mMockHostToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTokenLocked_windowIsNotRegistered_shouldReturnNull() {
|
||||
final IBinder token = mA11yWindowManager.getTokenLocked(OTHER_WINDOW_ID);
|
||||
assertNull(token);
|
||||
}
|
||||
|
||||
private void registerLeashedTokenAndWindowId() {
|
||||
mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
|
||||
mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);
|
||||
}
|
||||
|
||||
private void startTrackingPerDisplay(int displayId) throws RemoteException {
|
||||
ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
|
||||
// Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
|
||||
@@ -784,6 +897,7 @@ public class AccessibilityWindowManagerTest {
|
||||
IAccessibilityInteractionConnection.class);
|
||||
final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
|
||||
final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
|
||||
final IBinder mockLeashToken = Mockito.mock(IBinder.class);
|
||||
when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
|
||||
when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
|
||||
when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
|
||||
@@ -792,11 +906,31 @@ public class AccessibilityWindowManagerTest {
|
||||
.thenReturn(displayId);
|
||||
|
||||
int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
|
||||
mockWindowToken, mockA11yConnection, PACKAGE_NAME, userId);
|
||||
mockWindowToken, mockLeashToken, mockA11yConnection, PACKAGE_NAME, userId);
|
||||
mA11yWindowTokens.put(windowId, mockWindowToken);
|
||||
return mockWindowToken;
|
||||
}
|
||||
|
||||
private int addAccessibilityInteractionConnection(int displayId, boolean bGlobal,
|
||||
IBinder leashToken, int userId) throws RemoteException {
|
||||
final IWindow mockWindowToken = Mockito.mock(IWindow.class);
|
||||
final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
|
||||
IAccessibilityInteractionConnection.class);
|
||||
final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
|
||||
final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
|
||||
when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
|
||||
when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
|
||||
when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
|
||||
.thenReturn(bGlobal);
|
||||
when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
|
||||
.thenReturn(displayId);
|
||||
|
||||
int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
|
||||
mockWindowToken, leashToken, mockA11yConnection, PACKAGE_NAME, userId);
|
||||
mA11yWindowTokens.put(windowId, mockWindowToken);
|
||||
return windowId;
|
||||
}
|
||||
|
||||
private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) {
|
||||
final WindowInfo windowInfo = WindowInfo.obtain();
|
||||
windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
|
||||
|
||||
Reference in New Issue
Block a user