blob: c30030d6f6fb7d31b9f36acb76c52feab2294593 [file] [log] [blame]
/*
* 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) {}
}
}