1 /*
2  * Copyright (C) 2017 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 com.android.server.accessibility;
18 
19 import android.accessibilityservice.FingerprintGestureController;
20 import android.content.res.Resources;
21 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
22 import android.hardware.fingerprint.IFingerprintService;
23 import android.os.Binder;
24 import android.os.Handler;
25 import android.os.Message;
26 import android.os.RemoteException;
27 import android.util.Slog;
28 import android.view.KeyEvent;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 
33 /**
34  * Encapsulate fingerprint gesture logic
35  */
36 @SuppressWarnings("MissingPermissionAnnotation")
37 public class FingerprintGestureDispatcher extends IFingerprintClientActiveCallback.Stub
38         implements Handler.Callback{
39     private static final int MSG_REGISTER = 1;
40     private static final int MSG_UNREGISTER = 2;
41     private static final String LOG_TAG = "FingerprintGestureDispatcher";
42 
43     private final List<FingerprintGestureClient> mCapturingClients = new ArrayList<>(0);
44     private final Object mLock;
45     private final IFingerprintService mFingerprintService;
46     private final Handler mHandler;
47     private final boolean mHardwareSupportsGestures;
48 
49     // This field is ground truth for whether or not we are registered. Only write to it in handler.
50     private boolean mRegisteredReadOnlyExceptInHandler;
51 
52     /**
53      * @param fingerprintService The system's fingerprint service
54      * @param lock A lock to use when managing internal state
55      */
FingerprintGestureDispatcher(IFingerprintService fingerprintService, Resources resources, Object lock)56     public FingerprintGestureDispatcher(IFingerprintService fingerprintService,
57             Resources resources, Object lock) {
58         mFingerprintService = fingerprintService;
59         mHardwareSupportsGestures = resources.getBoolean(
60                 com.android.internal.R.bool.config_fingerprintSupportsGestures);
61         mLock = lock;
62         mHandler = new Handler(this);
63     }
64 
65     /**
66      * @param fingerprintService The system's fingerprint service
67      * @param lock A lock to use when managing internal state
68      * @param handler A handler to use internally. Used for testing.
69      */
FingerprintGestureDispatcher(IFingerprintService fingerprintService, Resources resources, Object lock, Handler handler)70     public FingerprintGestureDispatcher(IFingerprintService fingerprintService,
71             Resources resources, Object lock, Handler handler) {
72         mFingerprintService = fingerprintService;
73         mHardwareSupportsGestures = resources.getBoolean(
74                 com.android.internal.R.bool.config_fingerprintSupportsGestures);
75         mLock = lock;
76         mHandler = handler;
77     }
78 
79     /**
80      * Update the list of clients that are interested in fingerprint gestures.
81      *
82      * @param clientList The list of potential clients.
83      */
updateClientList(List<? extends FingerprintGestureClient> clientList)84     public void updateClientList(List<? extends FingerprintGestureClient> clientList) {
85         if (!mHardwareSupportsGestures) return;
86 
87         synchronized (mLock) {
88             mCapturingClients.clear();
89             for (int i = 0; i < clientList.size(); i++) {
90                 FingerprintGestureClient client = clientList.get(i);
91                 if (client.isCapturingFingerprintGestures()) {
92                     mCapturingClients.add(client);
93                 }
94             }
95             if (mCapturingClients.isEmpty()) {
96                 if (mRegisteredReadOnlyExceptInHandler) {
97                     mHandler.obtainMessage(MSG_UNREGISTER).sendToTarget();
98                 }
99             } else {
100                 if(!mRegisteredReadOnlyExceptInHandler) {
101                     mHandler.obtainMessage(MSG_REGISTER).sendToTarget();
102                 }
103             }
104         }
105     }
106 
107     @Override
onClientActiveChanged(boolean nonGestureFingerprintClientActive)108     public void onClientActiveChanged(boolean nonGestureFingerprintClientActive) {
109         if (!mHardwareSupportsGestures) return;
110 
111         synchronized (mLock) {
112             for (int i = 0; i < mCapturingClients.size(); i++) {
113                 mCapturingClients.get(i).onFingerprintGestureDetectionActiveChanged(
114                         !nonGestureFingerprintClientActive);
115             }
116         }
117     }
118 
isFingerprintGestureDetectionAvailable()119     public boolean isFingerprintGestureDetectionAvailable() {
120         if (!mHardwareSupportsGestures) return false;
121 
122         final long identity = Binder.clearCallingIdentity();
123         try {
124             return !mFingerprintService.isClientActive();
125         } catch (RemoteException re) {
126             return false;
127         } finally {
128             Binder.restoreCallingIdentity(identity);
129         }
130     }
131 
132     /**
133      * Called when the fingerprint sensor detects a gesture
134      *
135      * @param fingerprintKeyCode
136      * @return {@code true} if the gesture is consumed. {@code false} otherwise.
137      */
onFingerprintGesture(int fingerprintKeyCode)138     public boolean onFingerprintGesture(int fingerprintKeyCode) {
139         int idForFingerprintGestureManager;
140 
141         final List<FingerprintGestureClient> clientList;
142         synchronized (mLock) {
143             if (mCapturingClients.isEmpty()) {
144                 return false;
145             }
146             switch (fingerprintKeyCode) {
147                 case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
148                     idForFingerprintGestureManager =
149                             FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP;
150                     break;
151                 case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
152                     idForFingerprintGestureManager =
153                             FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN;
154                     break;
155                 case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT:
156                     idForFingerprintGestureManager =
157                             FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_RIGHT;
158                     break;
159                 case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
160                     idForFingerprintGestureManager =
161                             FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_LEFT;
162                     break;
163                 default:
164                     return false;
165             }
166             clientList = new ArrayList<>(mCapturingClients);
167         }
168         for (int i = 0; i < clientList.size(); i++) {
169             clientList.get(i).onFingerprintGesture(idForFingerprintGestureManager);
170         }
171         return true;
172     }
173 
174     @Override
handleMessage(Message message)175     public boolean handleMessage(Message message) {
176         if (message.what == MSG_REGISTER) {
177             final long identity = Binder.clearCallingIdentity();
178             try {
179                 mFingerprintService.addClientActiveCallback(this);
180                 mRegisteredReadOnlyExceptInHandler = true;
181             } catch (RemoteException re) {
182                 Slog.e(LOG_TAG, "Failed to register for fingerprint activity callbacks");
183             } finally {
184                 Binder.restoreCallingIdentity(identity);
185             }
186             return false;
187         } else if (message.what == MSG_UNREGISTER) {
188             final long identity = Binder.clearCallingIdentity();
189             try {
190                 mFingerprintService.removeClientActiveCallback(this);
191             } catch (RemoteException re) {
192                 Slog.e(LOG_TAG, "Failed to unregister for fingerprint activity callbacks");
193             } finally {
194                 Binder.restoreCallingIdentity(identity);
195             }
196             mRegisteredReadOnlyExceptInHandler = false;
197         } else {
198             Slog.e(LOG_TAG, "Unknown message: " + message.what);
199             return false;
200         }
201         return true;
202     }
203 
204     // Interface for potential clients.
205     public interface FingerprintGestureClient {
206         /**
207          * @return {@code true} if the client is capturing fingerprint gestures
208          */
isCapturingFingerprintGestures()209         boolean isCapturingFingerprintGestures();
210 
211         /**
212          * Callback when gesture detection becomes active or inactive.
213          *
214          * @param active {@code true} when detection is active
215          */
onFingerprintGestureDetectionActiveChanged(boolean active)216         void onFingerprintGestureDetectionActiveChanged(boolean active);
217 
218         /**
219          * Callback when gesture is detected
220          *
221          * @param gesture The identifier for the gesture. For example,
222          * {@link FingerprintGestureController#FINGERPRINT_GESTURE_SWIPE_LEFT}
223          */
onFingerprintGesture(int gesture)224         void onFingerprintGesture(int gesture);
225     }
226 }
227