1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import android.graphics.Matrix;
20 import android.os.Handler;
21 import android.os.IBinder;
22 import android.os.Looper;
23 import android.os.RemoteException;
24 import android.util.Log;
25 import android.view.accessibility.IAccessibilityEmbeddedConnection;
26 
27 class RemoteAccessibilityController {
28     private static final String TAG = "RemoteAccessibilityController";
29     private int mHostId;
30     private RemoteAccessibilityEmbeddedConnection mConnectionWrapper;
31     private Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
32     private final float[] mMatrixValues = new float[9];
33     private View mHostView;
34 
RemoteAccessibilityController(View v)35     RemoteAccessibilityController(View v) {
36         mHostView = v;
37     }
38 
runOnUiThread(Runnable runnable)39     private void runOnUiThread(Runnable runnable) {
40         final Handler h = mHostView.getHandler();
41         if (h != null && h.getLooper() != Looper.myLooper()) {
42             h.post(runnable);
43         } else {
44             runnable.run();
45         }
46     }
47 
assosciateHierarchy(IAccessibilityEmbeddedConnection connection, IBinder leashToken, int hostId)48     void assosciateHierarchy(IAccessibilityEmbeddedConnection connection,
49         IBinder leashToken, int hostId) {
50         mHostId = hostId;
51 
52         try {
53             leashToken = connection.associateEmbeddedHierarchy(
54                 leashToken, mHostId);
55             setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
56         } catch (RemoteException e) {
57             Log.d(TAG, "Error in associateEmbeddedHierarchy " + e);
58         }
59     }
60 
disassosciateHierarchy()61     void disassosciateHierarchy() {
62         setRemoteAccessibilityEmbeddedConnection(null, null);
63     }
64 
alreadyAssociated(IAccessibilityEmbeddedConnection connection)65     boolean alreadyAssociated(IAccessibilityEmbeddedConnection connection) {
66         if (mConnectionWrapper == null) {
67             return false;
68         }
69         return mConnectionWrapper.mConnection.equals(connection);
70     }
71 
connected()72     boolean connected() {
73       return mConnectionWrapper != null;
74     }
75 
getLeashToken()76     IBinder getLeashToken() {
77         return mConnectionWrapper.getLeashToken();
78     }
79 
80     /**
81      * Wrapper of accessibility embedded connection for embedded view hierarchy.
82      */
83     private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
84         private final IAccessibilityEmbeddedConnection mConnection;
85         private final IBinder mLeashToken;
86 
RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection, IBinder leashToken)87         RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
88                 IBinder leashToken) {
89             mConnection = connection;
90             mLeashToken = leashToken;
91         }
92 
getConnection()93         IAccessibilityEmbeddedConnection getConnection() {
94             return mConnection;
95         }
96 
getLeashToken()97         IBinder getLeashToken() {
98             return mLeashToken;
99         }
100 
linkToDeath()101         void linkToDeath() throws RemoteException {
102             mConnection.asBinder().linkToDeath(this, 0);
103         }
104 
unlinkToDeath()105         void unlinkToDeath() {
106             mConnection.asBinder().unlinkToDeath(this, 0);
107         }
108 
109         @Override
binderDied()110         public void binderDied() {
111             unlinkToDeath();
112             runOnUiThread(() -> {
113                 if (mConnectionWrapper == this) {
114                     mConnectionWrapper = null;
115                 }
116             });
117         }
118     }
119 
setRemoteAccessibilityEmbeddedConnection( IAccessibilityEmbeddedConnection connection, IBinder leashToken)120     private void setRemoteAccessibilityEmbeddedConnection(
121           IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
122         try {
123             if (mConnectionWrapper != null) {
124                 mConnectionWrapper.getConnection()
125                     .disassociateEmbeddedHierarchy();
126                 mConnectionWrapper.unlinkToDeath();
127                 mConnectionWrapper = null;
128             }
129             if (connection != null && leashToken != null) {
130                 mConnectionWrapper =
131                     new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
132                 mConnectionWrapper.linkToDeath();
133             }
134         } catch (RemoteException e) {
135             Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
136         }
137     }
138 
getRemoteAccessibilityEmbeddedConnection()139     private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
140         return mConnectionWrapper;
141     }
142 
setScreenMatrix(Matrix m)143     void setScreenMatrix(Matrix m) {
144         // If the screen matrix is identity or doesn't change, do nothing.
145         if (m.isIdentity() || m.equals(mScreenMatrixForEmbeddedHierarchy)) {
146             return;
147         }
148 
149         try {
150             final RemoteAccessibilityEmbeddedConnection wrapper =
151                     getRemoteAccessibilityEmbeddedConnection();
152             if (wrapper == null) {
153                 return;
154             }
155             m.getValues(mMatrixValues);
156             wrapper.getConnection().setScreenMatrix(mMatrixValues);
157             mScreenMatrixForEmbeddedHierarchy.set(m);
158         } catch (RemoteException e) {
159             Log.d(TAG, "Error while setScreenMatrix " + e);
160         }
161     }
162 
163 
164 
165 
166 
167 
168 }
169