/* * 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. *

* This capability must be declared by the service as * {@link AccessibilityServiceInfo#CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES}. It also requires * the permission {@link android.Manifest.permission#USE_FINGERPRINT}. *

* Because capturing fingerprint gestures may have side effects, services with the capability only * capture gestures when {@link AccessibilityServiceInfo#FLAG_CAPTURE_FINGERPRINT_GESTURES} is set. *

* Note: 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. *

* 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. *

* {@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 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 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 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) {} } }