Support accessibility on embedded hierarchies (3/n)

Link embedded hierarchy for accessibility when #setChildSurfacePackage
is invoked.

Bug: 137593247
Test: a11y CTS & unit tests
Change-Id: I6e9cfb387c863314b0affc5b48fb3c4c62cdf950
This commit is contained in:
Jackal Guo
2020-02-03 15:05:43 +08:00
parent 15bee94b77
commit ac234d6608
3 changed files with 155 additions and 25 deletions

View File

@@ -24,6 +24,7 @@ import android.graphics.PixelFormat;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
/**
* Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy
@@ -40,6 +41,7 @@ public class SurfaceControlViewHost {
private WindowlessWindowManager mWm;
private SurfaceControl mSurfaceControl;
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
/**
* Package encapsulating a Surface hierarchy which contains interactive view
@@ -49,15 +51,18 @@ public class SurfaceControlViewHost {
*/
public static final class SurfacePackage implements Parcelable {
private final SurfaceControl mSurfaceControl;
// TODO: Accessibility ID goes here
private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
SurfacePackage(SurfaceControl sc) {
SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection) {
mSurfaceControl = sc;
mAccessibilityEmbeddedConnection = connection;
}
private SurfacePackage(Parcel in) {
mSurfaceControl = new SurfaceControl();
mSurfaceControl.readFromParcel(in);
mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface(
in.readStrongBinder());
}
/**
@@ -69,6 +74,16 @@ public class SurfaceControlViewHost {
return mSurfaceControl;
}
/**
* Gets an accessibility embedded connection interface for this SurfaceControlViewHost.
*
* @return {@link IAccessibilityEmbeddedConnection} interface.
* @hide
*/
public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() {
return mAccessibilityEmbeddedConnection;
}
@Override
public int describeContents() {
return 0;
@@ -77,6 +92,7 @@ public class SurfaceControlViewHost {
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
mSurfaceControl.writeToParcel(out, flags);
out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder());
}
public static final @NonNull Creator<SurfacePackage> CREATOR
@@ -95,6 +111,7 @@ public class SurfaceControlViewHost {
@NonNull WindowlessWindowManager wwm) {
mWm = wwm;
mViewRoot = new ViewRootImpl(c, d, mWm);
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
/**
@@ -118,6 +135,7 @@ public class SurfaceControlViewHost {
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
mViewRoot = new ViewRootImpl(context, display, mWm);
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
/**
@@ -128,8 +146,8 @@ public class SurfaceControlViewHost {
* are linked.
*/
public @Nullable SurfacePackage getSurfacePackage() {
if (mSurfaceControl != null) {
return new SurfacePackage(mSurfaceControl);
if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection);
} else {
return null;
}

View File

@@ -28,6 +28,7 @@ import android.content.res.CompatibilityInfo.Translator;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -38,12 +39,14 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceControlViewHost;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
import com.android.internal.view.SurfaceCallbackHelper;
@@ -203,8 +206,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
private int mParentSurfaceGenerationId;
// The token of embedded windowless view hierarchy.
private IBinder mEmbeddedViewHierarchy;
private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection;
private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
private final Matrix mTmpMatrix = new Matrix();
private final float[] mMatrixValues = new float[9];
SurfaceControlViewHost.SurfacePackage mSurfacePackage;
public SurfaceView(Context context) {
@@ -923,6 +930,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
mTmpTransaction.apply();
updateScreenMatrixForEmbeddedHierarchy();
if (sizeChanged || creating) {
redrawNeeded = true;
@@ -1510,6 +1518,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
@Override
public void surfaceDestroyed() {
setWindowStopped(true);
setRemoteAccessibilityEmbeddedConnection(null, null);
}
/**
@@ -1568,31 +1577,133 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private void reparentSurfacePackage(SurfaceControl.Transaction t,
SurfaceControlViewHost.SurfacePackage p) {
// TODO: Link accessibility IDs here.
initEmbeddedHierarchyForAccessibility(p);
final SurfaceControl sc = p.getSurfaceControl();
t.reparent(sc, mSurfaceControl).show(sc);
}
/**
* Add the token of embedded view hierarchy. Set {@code null} to clear the embedded view
* hierarchy.
*
* @param token IBinder token.
* @hide
*/
public void setEmbeddedViewHierarchy(IBinder token) {
mEmbeddedViewHierarchy = token;
}
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
if (mEmbeddedViewHierarchy == null) {
final RemoteAccessibilityEmbeddedConnection wrapper =
getRemoteAccessibilityEmbeddedConnection();
if (wrapper == null) {
return;
}
// Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this
// leashed child would return the root node in the embedded hierarchy
info.addChild(mEmbeddedViewHierarchy);
info.addChild(wrapper.getLeashToken());
}
private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) {
final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection();
final RemoteAccessibilityEmbeddedConnection wrapper =
getRemoteAccessibilityEmbeddedConnection();
// Do nothing if package is embedding the same view hierarchy.
if (wrapper != null && wrapper.getConnection().equals(connection)) {
return;
}
// If this SurfaceView embeds a different view hierarchy, unlink the previous one first.
setRemoteAccessibilityEmbeddedConnection(null, null);
try {
final IBinder leashToken = connection.associateEmbeddedHierarchy(
getViewRootImpl().mLeashToken, getAccessibilityViewId());
setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
} catch (RemoteException e) {
Log.d(TAG, "Error while associateEmbeddedHierarchy " + e);
}
updateScreenMatrixForEmbeddedHierarchy();
}
private void setRemoteAccessibilityEmbeddedConnection(
IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
try {
if (mRemoteAccessibilityEmbeddedConnection != null) {
mRemoteAccessibilityEmbeddedConnection.getConnection()
.disassociateEmbeddedHierarchy();
mRemoteAccessibilityEmbeddedConnection.unlinkToDeath();
mRemoteAccessibilityEmbeddedConnection = null;
}
if (connection != null && leashToken != null) {
mRemoteAccessibilityEmbeddedConnection =
new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
mRemoteAccessibilityEmbeddedConnection.linkToDeath();
}
} catch (RemoteException e) {
Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
}
}
private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
return mRemoteAccessibilityEmbeddedConnection;
}
private void updateScreenMatrixForEmbeddedHierarchy() {
mTmpMatrix.reset();
mTmpMatrix.setTranslate(mScreenRect.left, mScreenRect.top);
mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
mScreenRect.height() / (float) mSurfaceHeight);
// If the screen matrix is identity or doesn't change, do nothing.
if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) {
return;
}
try {
final RemoteAccessibilityEmbeddedConnection wrapper =
getRemoteAccessibilityEmbeddedConnection();
if (wrapper == null) {
return;
}
mTmpMatrix.getValues(mMatrixValues);
wrapper.getConnection().setScreenMatrix(mMatrixValues);
mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix);
} catch (RemoteException e) {
Log.d(TAG, "Error while setScreenMatrix " + e);
}
}
/**
* Wrapper of accessibility embedded connection for embedded view hierarchy.
*/
private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
private final IAccessibilityEmbeddedConnection mConnection;
private final IBinder mLeashToken;
RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
IBinder leashToken) {
mConnection = connection;
mLeashToken = leashToken;
}
IAccessibilityEmbeddedConnection getConnection() {
return mConnection;
}
IBinder getLeashToken() {
return mLeashToken;
}
void linkToDeath() throws RemoteException {
mConnection.asBinder().linkToDeath(this, 0);
}
void unlinkToDeath() {
mConnection.asBinder().unlinkToDeath(this, 0);
}
@Override
public void binderDied() {
unlinkToDeath();
runOnUiThread(() -> {
if (mRemoteAccessibilityEmbeddedConnection == this) {
mRemoteAccessibilityEmbeddedConnection = null;
}
});
}
}
}

View File

@@ -655,7 +655,7 @@ public final class ViewRootImpl implements ViewParent,
private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
private IAccessibilityEmbeddedConnection mEmbeddedConnection;
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
static final class SystemUiVisibilityInfo {
int seq;
@@ -9370,11 +9370,12 @@ 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);
public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() {
if (mAccessibilityEmbeddedConnection == null) {
mAccessibilityEmbeddedConnection = new AccessibilityEmbeddedConnection(
ViewRootImpl.this);
}
return mEmbeddedConnection;
return mAccessibilityEmbeddedConnection;
}
private class SendWindowContentChangedAccessibilityEvent implements Runnable {