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