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