Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60370aad authored by Robert Carr's avatar Robert Carr Committed by Rob Carr
Browse files

SurfaceView Cleanup (3/n): Extract RemoteAccessibilityController

We extract the SurfaceControlViewHost accessibility logic from
SurfaceView. This is serving both cleaning up the code in SurfaceView
a little, while also serving the goal of one day using this
accessibility code from other components.

Test: Existing tests pass
Change-Id: I0333ab339e54e3766fffab25df92f00bdb9723dc
parent 5a372071
Loading
Loading
Loading
Loading
+168 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.graphics.Matrix;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.accessibility.IAccessibilityEmbeddedConnection;

class RemoteAccessibilityController {
    private static final String TAG = "RemoteAccessibilityController";
    private int mHostId;
    private RemoteAccessibilityEmbeddedConnection mConnectionWrapper;
    private Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
    private final float[] mMatrixValues = new float[9];
    private View mHostView;

    RemoteAccessibilityController(View v) {
        mHostView = v;
    }

    private void runOnUiThread(Runnable runnable) {
        final Handler h = mHostView.getHandler();
        if (h != null && h.getLooper() != Looper.myLooper()) {
            h.post(runnable);
        } else {
            runnable.run();
        }
    }

    void assosciateHierarchy(IAccessibilityEmbeddedConnection connection,
        IBinder leashToken, int hostId) {
        mHostId = hostId;

        try {
            leashToken = connection.associateEmbeddedHierarchy(
                leashToken, mHostId);
            setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
        } catch (RemoteException e) {
            Log.d(TAG, "Error in associateEmbeddedHierarchy " + e);
        }
    }

    void disassosciateHierarchy() {
        setRemoteAccessibilityEmbeddedConnection(null, null);
    }

    boolean alreadyAssociated(IAccessibilityEmbeddedConnection connection) {
        if (mConnectionWrapper == null) {
            return false;
        }
        return mConnectionWrapper.mConnection.equals(connection);
    }

    boolean connected() {
      return mConnectionWrapper != null;
    }

    IBinder getLeashToken() {
        return mConnectionWrapper.getLeashToken();
    }

    /**
     * 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 (mConnectionWrapper == this) {
                    mConnectionWrapper = null;
                }
            });
        }
    }

    private void setRemoteAccessibilityEmbeddedConnection(
          IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
        try {
            if (mConnectionWrapper != null) {
                mConnectionWrapper.getConnection()
                    .disassociateEmbeddedHierarchy();
                mConnectionWrapper.unlinkToDeath();
                mConnectionWrapper = null;
            }
            if (connection != null && leashToken != null) {
                mConnectionWrapper =
                    new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
                mConnectionWrapper.linkToDeath();
            }
        } catch (RemoteException e) {
            Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
        }
    }

    private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
        return mConnectionWrapper;
    }

    void setScreenMatrix(Matrix m) {
        // If the screen matrix is identity or doesn't change, do nothing.
        if (m.isIdentity() || m.equals(mScreenMatrixForEmbeddedHierarchy)) {
            return;
        }

        try {
            final RemoteAccessibilityEmbeddedConnection wrapper =
                    getRemoteAccessibilityEmbeddedConnection();
            if (wrapper == null) {
                return;
            }
            m.getValues(mMatrixValues);
            wrapper.getConnection().setScreenMatrix(mMatrixValues);
            mScreenMatrixForEmbeddedHierarchy.set(m);
        } catch (RemoteException e) {
            Log.d(TAG, "Error while setScreenMatrix " + e);
        }
    }






}
+25 −114
Original line number Diff line number Diff line
@@ -227,9 +227,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
    private int mParentSurfaceGenerationId;

    private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection;
    private RemoteAccessibilityController mRemoteAccessibilityController =
        new RemoteAccessibilityController(this);

    private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
    private final Matrix mTmpMatrix = new Matrix();
    private final float[] mMatrixValues = new float[9];

@@ -930,6 +930,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
        boolean creating, boolean sizeChanged, boolean needBLASTSync) {
        boolean realSizeChanged = false;

        mSurfaceLock.lock();
        try {
            mDrawingStopped = !mVisible;
@@ -1000,7 +1001,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
            }

            mTmpTransaction.apply();
            updateScreenMatrixForEmbeddedHierarchy();
            updateEmbeddedAccessibilityMatrix();

            mSurfaceFrame.left = 0;
            mSurfaceFrame.top = 0;
            if (translator == null) {
@@ -1756,7 +1758,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    @Override
    public void surfaceDestroyed() {
        setWindowStopped(true);
        setRemoteAccessibilityEmbeddedConnection(null, null);
        mRemoteAccessibilityController.disassosciateHierarchy();
    }

    /**
@@ -1836,14 +1838,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    @Override
    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfoInternal(info);
        final RemoteAccessibilityEmbeddedConnection wrapper =
                getRemoteAccessibilityEmbeddedConnection();
        if (wrapper == null) {
        if (!mRemoteAccessibilityController.connected()) {
            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(wrapper.getLeashToken());
        info.addChild(mRemoteAccessibilityController.getLeashToken());
    }

    @Override
@@ -1852,7 +1852,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
        // If developers explicitly set the important mode for it, don't change the mode.
        // Only change the mode to important when this SurfaceView isn't explicitly set and has
        // an embedded hierarchy.
        if (mRemoteAccessibilityEmbeddedConnection == null
        if (!mRemoteAccessibilityController.connected()
                || mode != IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            return mode;
        }
@@ -1861,74 +1861,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall

    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)) {
        if (mRemoteAccessibilityController.alreadyAssociated(connection)) {
            return;
        }

        // If this SurfaceView embeds a different view hierarchy, unlink the previous one first.
        setRemoteAccessibilityEmbeddedConnection(null, null);

        try {
            final IBinder leashToken = connection.associateEmbeddedHierarchy(
        mRemoteAccessibilityController.assosciateHierarchy(connection,
            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() {
        getBoundsOnScreen(mTmpRect);
        mTmpMatrix.reset();
        mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.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);
        }
        updateEmbeddedAccessibilityMatrix();
    }

    private void notifySurfaceDestroyed() {
@@ -1956,6 +1895,18 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
        }
    }

    void updateEmbeddedAccessibilityMatrix() {
        if (!mRemoteAccessibilityController.connected()) {
            return;
        }
        getBoundsOnScreen(mTmpRect);
        mTmpMatrix.reset();
        mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
        mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
                mScreenRect.height() / (float) mSurfaceHeight);
        mRemoteAccessibilityController.setScreenMatrix(mTmpMatrix);
    }

    @Override
    protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
                                  @Nullable Rect previouslyFocusedRect) {
@@ -1972,44 +1923,4 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
                    + "Exception requesting focus on embedded window", 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;
                }
            });
        }
    }
}