| /* | 
 |  * Copyright (C) 2017 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.accessibilityservice; | 
 |  | 
 | import android.annotation.NonNull; | 
 | import android.annotation.Nullable; | 
 | import android.os.Handler; | 
 | import android.os.RemoteException; | 
 | import android.util.ArrayMap; | 
 | import android.util.Log; | 
 | import com.android.internal.annotations.VisibleForTesting; | 
 |  | 
 | /** | 
 |  * An {@link AccessibilityService} can capture gestures performed on a device's fingerprint | 
 |  * sensor, as long as the device has a sensor capable of detecting gestures. | 
 |  * <p> | 
 |  * This capability must be declared by the service as | 
 |  * {@link AccessibilityServiceInfo#CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES}. It also requires | 
 |  * the permission {@link android.Manifest.permission#USE_FINGERPRINT}. | 
 |  * <p> | 
 |  * Because capturing fingerprint gestures may have side effects, services with the capability only | 
 |  * capture gestures when {@link AccessibilityServiceInfo#FLAG_REQUEST_FINGERPRINT_GESTURES} is set. | 
 |  * <p> | 
 |  * <strong>Note: </strong>The fingerprint sensor is used for authentication in critical use cases, | 
 |  * so services must carefully design their user's experience when performing gestures on the sensor. | 
 |  * When the sensor is in use by an app, for example, when authenticating or enrolling a user, | 
 |  * the sensor will not detect gestures. Services need to ensure that users understand when the | 
 |  * sensor is in-use for authentication to prevent users from authenticating unintentionally when | 
 |  * trying to interact with the service. They can use | 
 |  * {@link FingerprintGestureCallback#onGestureDetectionAvailabilityChanged(boolean)} to learn when | 
 |  * gesture detection becomes unavailable. | 
 |  * <p> | 
 |  * Multiple accessibility services may listen for fingerprint gestures simultaneously, so services | 
 |  * should provide a way for the user to disable the use of this feature so multiple services don't | 
 |  * conflict with each other. | 
 |  * <p> | 
 |  * {@see android.hardware.fingerprint.FingerprintManager#isHardwareDetected} | 
 |  */ | 
 | public final class FingerprintGestureController { | 
 |     /** Identifier for a swipe right on the fingerprint sensor */ | 
 |     public static final int FINGERPRINT_GESTURE_SWIPE_RIGHT = 0x00000001; | 
 |  | 
 |     /** Identifier for a swipe left on the fingerprint sensor */ | 
 |     public static final int FINGERPRINT_GESTURE_SWIPE_LEFT = 0x00000002; | 
 |  | 
 |     /** Identifier for a swipe up on the fingerprint sensor */ | 
 |     public static final int FINGERPRINT_GESTURE_SWIPE_UP = 0x00000004; | 
 |  | 
 |     /** Identifier for a swipe down on the fingerprint sensor */ | 
 |     public static final int FINGERPRINT_GESTURE_SWIPE_DOWN = 0x00000008; | 
 |  | 
 |     private static final String LOG_TAG = "FingerprintGestureController"; | 
 |     private final Object mLock = new Object(); | 
 |     private final IAccessibilityServiceConnection mAccessibilityServiceConnection; | 
 |  | 
 |     private final ArrayMap<FingerprintGestureCallback, Handler> mCallbackHandlerMap = | 
 |             new ArrayMap<>(1); | 
 |  | 
 |     /** | 
 |      * @param connection The connection to use for system interactions | 
 |      * @hide | 
 |      */ | 
 |     @VisibleForTesting | 
 |     public FingerprintGestureController(IAccessibilityServiceConnection connection) { | 
 |         mAccessibilityServiceConnection = connection; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Gets if the fingerprint sensor's gesture detection is available. | 
 |      * | 
 |      * @return {@code true} if the sensor's gesture detection is available. {@code false} if it is | 
 |      * not currently detecting gestures (for example, if it is enrolling a finger). | 
 |      */ | 
 |     public boolean isGestureDetectionAvailable() { | 
 |         try { | 
 |             return mAccessibilityServiceConnection.isFingerprintGestureDetectionAvailable(); | 
 |         } catch (RemoteException re) { | 
 |             Log.w(LOG_TAG, "Failed to check if fingerprint gestures are active", re); | 
 |             re.rethrowFromSystemServer(); | 
 |             return false; | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Register a callback to be informed of fingerprint sensor gesture events. | 
 |      * | 
 |      * @param callback The listener to be added. | 
 |      * @param handler The handler to use for the callback. If {@code null}, callbacks will happen | 
 |      * on the service's main thread. | 
 |      */ | 
 |     public void registerFingerprintGestureCallback( | 
 |             @NonNull FingerprintGestureCallback callback, @Nullable Handler handler) { | 
 |         synchronized (mLock) { | 
 |             mCallbackHandlerMap.put(callback, handler); | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Unregister a listener added with {@link #registerFingerprintGestureCallback}. | 
 |      * | 
 |      * @param callback The callback to remove. Removing a callback that was never added has no | 
 |      * effect. | 
 |      */ | 
 |     public void unregisterFingerprintGestureCallback(FingerprintGestureCallback callback) { | 
 |         synchronized (mLock) { | 
 |             mCallbackHandlerMap.remove(callback); | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Called when gesture detection becomes active or inactive | 
 |      * @hide | 
 |      */ | 
 |     public void onGestureDetectionActiveChanged(boolean active) { | 
 |         final ArrayMap<FingerprintGestureCallback, Handler> handlerMap; | 
 |         synchronized (mLock) { | 
 |             handlerMap = new ArrayMap<>(mCallbackHandlerMap); | 
 |         } | 
 |         int numListeners = handlerMap.size(); | 
 |         for (int i = 0; i < numListeners; i++) { | 
 |             FingerprintGestureCallback callback = handlerMap.keyAt(i); | 
 |             Handler handler = handlerMap.valueAt(i); | 
 |             if (handler != null) { | 
 |                 handler.post(() -> callback.onGestureDetectionAvailabilityChanged(active)); | 
 |             } else { | 
 |                 callback.onGestureDetectionAvailabilityChanged(active); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Called when gesture is detected. | 
 |      * @hide | 
 |      */ | 
 |     public void onGesture(int gesture) { | 
 |         final ArrayMap<FingerprintGestureCallback, Handler> handlerMap; | 
 |         synchronized (mLock) { | 
 |             handlerMap = new ArrayMap<>(mCallbackHandlerMap); | 
 |         } | 
 |         int numListeners = handlerMap.size(); | 
 |         for (int i = 0; i < numListeners; i++) { | 
 |             FingerprintGestureCallback callback = handlerMap.keyAt(i); | 
 |             Handler handler = handlerMap.valueAt(i); | 
 |             if (handler != null) { | 
 |                 handler.post(() -> callback.onGestureDetected(gesture)); | 
 |             } else { | 
 |                 callback.onGestureDetected(gesture); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Class that is called back when fingerprint gestures are being used for accessibility. | 
 |      */ | 
 |     public abstract static class FingerprintGestureCallback { | 
 |         /** | 
 |          * Called when the fingerprint sensor's gesture detection becomes available or unavailable. | 
 |          * | 
 |          * @param available Whether or not the sensor's gesture detection is now available. | 
 |          */ | 
 |         public void onGestureDetectionAvailabilityChanged(boolean available) {} | 
 |  | 
 |         /** | 
 |          * Called when the fingerprint sensor detects gestures. | 
 |          * | 
 |          * @param gesture The id of the gesture that was detected. For example, | 
 |          * {@link #FINGERPRINT_GESTURE_SWIPE_RIGHT}. | 
 |          */ | 
 |         public void onGestureDetected(int gesture) {} | 
 |     } | 
 | } |