Merge "Support accessibility on embedded hierarchies (2/n)"

This commit is contained in:
Jackal Guo
2020-02-11 02:20:50 +00:00
committed by Android (Google) Code Review
13 changed files with 547 additions and 25 deletions

View 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]);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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