Accessibility can capture fingerprint gestures

Bug: 27148522
Test: Unit tests for two new classes in this CL, CTS in
linked CL.
Change-Id: Icb5113e00b1f8724814263b3cc7f72fe4a6f0b41
diff --git a/Android.mk b/Android.mk
index e68b310..22323c5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -182,6 +182,7 @@
 	core/java/android/hardware/display/IVirtualDisplayCallback.aidl \
 	core/java/android/hardware/fingerprint/IFingerprintService.aidl \
 	core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl \
+	core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl \
 	core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl \
 	core/java/android/hardware/hdmi/IHdmiControlCallback.aidl \
 	core/java/android/hardware/hdmi/IHdmiControlService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 95e8a5d..4607ee9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -349,6 +349,7 @@
     field public static final int calendarTextColor = 16843931; // 0x101049b
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
+    field public static final int canCaptureFingerprintGestures = 16844111; // 0x101054f
     field public static final int canControlMagnification = 16844039; // 0x1010507
     field public static final int canPerformGestures = 16844045; // 0x101050d
     field public static final int canRecord = 16844060; // 0x101051c
@@ -2692,6 +2693,7 @@
     method public final void disableSelf();
     method public final boolean dispatchGesture(android.accessibilityservice.GestureDescription, android.accessibilityservice.AccessibilityService.GestureResultCallback, android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
+    method public final android.accessibilityservice.FingerprintGestureController getFingerprintGestureController();
     method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
@@ -2783,6 +2785,7 @@
     method public java.lang.String getSettingsActivityName();
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES = 64; // 0x40
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
     field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
     field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
@@ -2798,6 +2801,7 @@
     field public static final int FEEDBACK_HAPTIC = 2; // 0x2
     field public static final int FEEDBACK_SPOKEN = 1; // 0x1
     field public static final int FEEDBACK_VISUAL = 8; // 0x8
+    field public static final int FLAG_CAPTURE_FINGERPRINT_GESTURES = 512; // 0x200
     field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80
     field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
     field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
@@ -2812,6 +2816,22 @@
     field public java.lang.String[] packageNames;
   }
 
+  public final class FingerprintGestureController {
+    method public boolean isGestureDetectionAvailable();
+    method public void registerFingerprintGestureCallback(android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback, android.os.Handler);
+    method public void unregisterFingerprintGestureCallback(android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback);
+    field public static final int FINGERPRINT_GESTURE_SWIPE_DOWN = 8; // 0x8
+    field public static final int FINGERPRINT_GESTURE_SWIPE_LEFT = 2; // 0x2
+    field public static final int FINGERPRINT_GESTURE_SWIPE_RIGHT = 1; // 0x1
+    field public static final int FINGERPRINT_GESTURE_SWIPE_UP = 4; // 0x4
+  }
+
+  public static abstract class FingerprintGestureController.FingerprintGestureCallback {
+    ctor public FingerprintGestureController.FingerprintGestureCallback();
+    method public void onGesture(int);
+    method public void onGestureDetectionAvailabilityChanged(boolean);
+  }
+
   public final class GestureDescription {
     method public static long getMaxGestureDuration();
     method public static int getMaxStrokeCount();
@@ -15247,11 +15267,11 @@
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
     field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
     field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
+    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
     field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
     field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
     field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
-    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
   }
 
   public static abstract interface DisplayManager.DisplayListener {
@@ -42619,11 +42639,11 @@
     method public boolean isValid();
     method public boolean isWideColorGamut();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
+    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_PRESENTATION = 8; // 0x8
     field public static final int FLAG_PRIVATE = 4; // 0x4
     field public static final int FLAG_ROUND = 16; // 0x10
     field public static final int FLAG_SECURE = 2; // 0x2
-    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
     field public static final int INVALID_DISPLAY = -1; // 0xffffffff
     field public static final int STATE_DOZE = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index 4bd3838..7a8b7db 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -461,6 +461,7 @@
     field public static final int calendarTextColor = 16843931; // 0x101049b
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
+    field public static final int canCaptureFingerprintGestures = 16844111; // 0x101054f
     field public static final int canControlMagnification = 16844039; // 0x1010507
     field public static final int canPerformGestures = 16844045; // 0x101050d
     field public static final int canRecord = 16844060; // 0x101051c
@@ -2811,6 +2812,7 @@
     method public final void disableSelf();
     method public final boolean dispatchGesture(android.accessibilityservice.GestureDescription, android.accessibilityservice.AccessibilityService.GestureResultCallback, android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
+    method public final android.accessibilityservice.FingerprintGestureController getFingerprintGestureController();
     method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
@@ -2902,6 +2904,7 @@
     method public java.lang.String getSettingsActivityName();
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES = 64; // 0x40
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
     field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
     field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
@@ -2917,6 +2920,7 @@
     field public static final int FEEDBACK_HAPTIC = 2; // 0x2
     field public static final int FEEDBACK_SPOKEN = 1; // 0x1
     field public static final int FEEDBACK_VISUAL = 8; // 0x8
+    field public static final int FLAG_CAPTURE_FINGERPRINT_GESTURES = 512; // 0x200
     field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80
     field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
     field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
@@ -2931,6 +2935,22 @@
     field public java.lang.String[] packageNames;
   }
 
+  public final class FingerprintGestureController {
+    method public boolean isGestureDetectionAvailable();
+    method public void registerFingerprintGestureCallback(android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback, android.os.Handler);
+    method public void unregisterFingerprintGestureCallback(android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback);
+    field public static final int FINGERPRINT_GESTURE_SWIPE_DOWN = 8; // 0x8
+    field public static final int FINGERPRINT_GESTURE_SWIPE_LEFT = 2; // 0x2
+    field public static final int FINGERPRINT_GESTURE_SWIPE_RIGHT = 1; // 0x1
+    field public static final int FINGERPRINT_GESTURE_SWIPE_UP = 4; // 0x4
+  }
+
+  public static abstract class FingerprintGestureController.FingerprintGestureCallback {
+    ctor public FingerprintGestureController.FingerprintGestureCallback();
+    method public void onGesture(int);
+    method public void onGestureDetectionAvailabilityChanged(boolean);
+  }
+
   public final class GestureDescription {
     method public static long getMaxGestureDuration();
     method public static int getMaxStrokeCount();
@@ -15825,11 +15845,11 @@
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
     field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
     field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
+    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
     field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
     field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
     field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
-    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
   }
 
   public static abstract interface DisplayManager.DisplayListener {
@@ -46022,11 +46042,11 @@
     method public boolean isValid();
     method public boolean isWideColorGamut();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
+    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_PRESENTATION = 8; // 0x8
     field public static final int FLAG_PRIVATE = 4; // 0x4
     field public static final int FLAG_ROUND = 16; // 0x10
     field public static final int FLAG_SECURE = 2; // 0x2
-    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
     field public static final int INVALID_DISPLAY = -1; // 0xffffffff
     field public static final int STATE_DOZE = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 89b098a..2aa5fad 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -349,6 +349,7 @@
     field public static final int calendarTextColor = 16843931; // 0x101049b
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
+    field public static final int canCaptureFingerprintGestures = 16844111; // 0x101054f
     field public static final int canControlMagnification = 16844039; // 0x1010507
     field public static final int canPerformGestures = 16844045; // 0x101050d
     field public static final int canRecord = 16844060; // 0x101051c
@@ -2692,6 +2693,7 @@
     method public final void disableSelf();
     method public final boolean dispatchGesture(android.accessibilityservice.GestureDescription, android.accessibilityservice.AccessibilityService.GestureResultCallback, android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
+    method public final android.accessibilityservice.FingerprintGestureController getFingerprintGestureController();
     method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
@@ -2783,6 +2785,7 @@
     method public java.lang.String getSettingsActivityName();
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES = 64; // 0x40
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
     field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
     field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
@@ -2798,6 +2801,7 @@
     field public static final int FEEDBACK_HAPTIC = 2; // 0x2
     field public static final int FEEDBACK_SPOKEN = 1; // 0x1
     field public static final int FEEDBACK_VISUAL = 8; // 0x8
+    field public static final int FLAG_CAPTURE_FINGERPRINT_GESTURES = 512; // 0x200
     field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80
     field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
     field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
@@ -2812,6 +2816,22 @@
     field public java.lang.String[] packageNames;
   }
 
+  public final class FingerprintGestureController {
+    method public boolean isGestureDetectionAvailable();
+    method public void registerFingerprintGestureCallback(android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback, android.os.Handler);
+    method public void unregisterFingerprintGestureCallback(android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback);
+    field public static final int FINGERPRINT_GESTURE_SWIPE_DOWN = 8; // 0x8
+    field public static final int FINGERPRINT_GESTURE_SWIPE_LEFT = 2; // 0x2
+    field public static final int FINGERPRINT_GESTURE_SWIPE_RIGHT = 1; // 0x1
+    field public static final int FINGERPRINT_GESTURE_SWIPE_UP = 4; // 0x4
+  }
+
+  public static abstract class FingerprintGestureController.FingerprintGestureCallback {
+    ctor public FingerprintGestureController.FingerprintGestureCallback();
+    method public void onGesture(int);
+    method public void onGestureDetectionAvailabilityChanged(boolean);
+  }
+
   public final class GestureDescription {
     method public static long getMaxGestureDuration();
     method public static int getMaxStrokeCount();
@@ -15281,11 +15301,11 @@
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
     field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
     field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
+    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
     field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
     field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
     field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
-    field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
   }
 
   public static abstract interface DisplayManager.DisplayListener {
@@ -42923,11 +42943,11 @@
     method public boolean isValid();
     method public boolean isWideColorGamut();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
+    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_PRESENTATION = 8; // 0x8
     field public static final int FLAG_PRIVATE = 4; // 0x4
     field public static final int FLAG_ROUND = 16; // 0x10
     field public static final int FLAG_SECURE = 2; // 0x2
-    field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
     field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
     field public static final int INVALID_DISPLAY = -1; // 0xffffffff
     field public static final int STATE_DOZE = 3; // 0x3
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3e5cc54..a036b6a 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -20,11 +20,13 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.graphics.Region;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -33,11 +35,9 @@
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.KeyEvent;
-import android.view.MotionEvent;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 import android.view.accessibility.AccessibilityEvent;
@@ -362,19 +362,22 @@
     private static final String LOG_TAG = "AccessibilityService";
 
     /**
+     * Interface used by IAccessibilityServiceWrapper to call the service from its main thread.
      * @hide
      */
     public interface Callbacks {
-        public void onAccessibilityEvent(AccessibilityEvent event);
-        public void onInterrupt();
-        public void onServiceConnected();
-        public void init(int connectionId, IBinder windowToken);
-        public boolean onGesture(int gestureId);
-        public boolean onKeyEvent(KeyEvent event);
-        public void onMagnificationChanged(@NonNull Region region,
+        void onAccessibilityEvent(AccessibilityEvent event);
+        void onInterrupt();
+        void onServiceConnected();
+        void init(int connectionId, IBinder windowToken);
+        boolean onGesture(int gestureId);
+        boolean onKeyEvent(KeyEvent event);
+        void onMagnificationChanged(@NonNull Region region,
                 float scale, float centerX, float centerY);
-        public void onSoftKeyboardShowModeChanged(int showMode);
-        public void onPerformGestureResult(int sequence, boolean completedSuccessfully);
+        void onSoftKeyboardShowModeChanged(int showMode);
+        void onPerformGestureResult(int sequence, boolean completedSuccessfully);
+        void onFingerprintCapturingGesturesChanged(boolean active);
+        void onFingerprintGesture(int gesture);
     }
 
     /**
@@ -404,6 +407,8 @@
 
     private final Object mLock = new Object();
 
+    private FingerprintGestureController mFingerprintGestureController;
+
     /**
      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
      *
@@ -598,6 +603,32 @@
     }
 
     /**
+     * Get the controller for fingerprint gestures. This feature requires {@link
+     * AccessibilityServiceInfo#CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES}.
+     *
+     *<strong>Note: </strong> The service must be connected before this method is called.
+     *
+     * @return The controller for fingerprint gestures, or {@code null} if gestures are unavailable.
+     */
+    @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
+    public final @Nullable FingerprintGestureController getFingerprintGestureController() {
+        if (mFingerprintGestureController == null) {
+            FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class);
+            if ((fingerprintManager != null) && fingerprintManager.isHardwareDetected()) {
+                AccessibilityServiceInfo info = getServiceInfo();
+                int fingerprintCapabilityMask =
+                        AccessibilityServiceInfo.CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES;
+                if ((info.getCapabilities() & fingerprintCapabilityMask) != 0) {
+                    mFingerprintGestureController = new FingerprintGestureController(
+                            AccessibilityInteractionClient.getInstance()
+                                    .getConnection(mConnectionId));
+                }
+            }
+        }
+        return mFingerprintGestureController;
+    }
+
+    /**
      * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
      * the user, this service, or another service, will be cancelled.
      * <p>
@@ -694,6 +725,22 @@
     }
 
     /**
+     * Callback for fingerprint gesture handling
+     * @param active If gesture detection is active
+     */
+    private void onFingerprintCapturingGesturesChanged(boolean active) {
+        getFingerprintGestureController().onGestureDetectionActiveChanged(active);
+    }
+
+    /**
+     * Callback for fingerprint gesture handling
+     * @param gesture The identifier for the gesture performed
+     */
+    private void onFingerprintGesture(int gesture) {
+        getFingerprintGestureController().onGesture(gesture);
+    }
+
+    /**
      * Used to control and query the state of display magnification.
      */
     public static final class MagnificationController {
@@ -1486,6 +1533,16 @@
             public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
                 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
             }
+
+            @Override
+            public void onFingerprintCapturingGesturesChanged(boolean active) {
+                AccessibilityService.this.onFingerprintCapturingGesturesChanged(active);
+            }
+
+            @Override
+            public void onFingerprintGesture(int gesture) {
+                AccessibilityService.this.onFingerprintGesture(gesture);
+            }
         });
     }
 
@@ -1506,6 +1563,8 @@
         private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
         private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8;
         private static final int DO_GESTURE_COMPLETE = 9;
+        private static final int DO_ON_FINGERPRINT_ACTIVE_CHANGED = 10;
+        private static final int DO_ON_FINGERPRINT_GESTURE = 11;
 
         private final HandlerCaller mCaller;
 
@@ -1577,6 +1636,15 @@
             mCaller.sendMessage(message);
         }
 
+        public void onFingerprintCapturingGesturesChanged(boolean active) {
+            mCaller.sendMessage(mCaller.obtainMessageI(
+                    DO_ON_FINGERPRINT_ACTIVE_CHANGED, active ? 1 : 0));
+        }
+
+        public void onFingerprintGesture(int gesture) {
+            mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture));
+        }
+
         @Override
         public void executeMessage(Message message) {
             switch (message.what) {
@@ -1675,6 +1743,12 @@
                     final boolean successfully = message.arg2 == 1;
                     mCallback.onPerformGestureResult(message.arg1, successfully);
                 } return;
+                case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
+                    mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
+                } return;
+                case DO_ON_FINGERPRINT_GESTURE: {
+                    mCallback.onFingerprintGesture(message.arg1);
+                } return;
 
                 default :
                     Log.w(LOG_TAG, "Unknown message type " + message.what);
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index b76aeb7..18e57cb 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -25,6 +25,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -116,34 +117,13 @@
      */
     public static final int CAPABILITY_CAN_PERFORM_GESTURES = 0x00000020;
 
-    private static final SparseArray<CapabilityInfo> sAvailableCapabilityInfos =
-            new SparseArray<CapabilityInfo>();
-    static {
-        sAvailableCapabilityInfos.put(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT,
-                new CapabilityInfo(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT,
-                        R.string.capability_title_canRetrieveWindowContent,
-                        R.string.capability_desc_canRetrieveWindowContent));
-        sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION,
-                new CapabilityInfo(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION,
-                        R.string.capability_title_canRequestTouchExploration,
-                        R.string.capability_desc_canRequestTouchExploration));
-        sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY,
-                new CapabilityInfo(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY,
-                        R.string.capability_title_canRequestEnhancedWebAccessibility,
-                        R.string.capability_desc_canRequestEnhancedWebAccessibility));
-        sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS,
-                new CapabilityInfo(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS,
-                        R.string.capability_title_canRequestFilterKeyEvents,
-                        R.string.capability_desc_canRequestFilterKeyEvents));
-        sAvailableCapabilityInfos.put(CAPABILITY_CAN_CONTROL_MAGNIFICATION,
-                new CapabilityInfo(CAPABILITY_CAN_CONTROL_MAGNIFICATION,
-                        R.string.capability_title_canControlMagnification,
-                        R.string.capability_desc_canControlMagnification));
-        sAvailableCapabilityInfos.put(CAPABILITY_CAN_PERFORM_GESTURES,
-                new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES,
-                        R.string.capability_title_canPerformGestures,
-                        R.string.capability_desc_canPerformGestures));
-    }
+    /**
+     * Capability: This accessibility service can capture gestures from the fingerprint sensor
+     * @see android.R.styleable#AccessibilityService_canCaptureFingerprintGestures
+     */
+    public static final int CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES = 0x00000040;
+
+    private static SparseArray<CapabilityInfo> sAvailableCapabilityInfos;
 
     /**
      * Denotes spoken feedback.
@@ -326,6 +306,12 @@
      */
     public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 0x00000080;
 
+    /**
+     * This flag requests that all fingerprint gestures be sent to the accessibility service.
+     * It is handled in {@link FingerprintGestureController}
+     */
+    public static final int FLAG_CAPTURE_FINGERPRINT_GESTURES = 0x00000200;
+
     /** {@hide} */
     public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
 
@@ -535,6 +521,10 @@
                     .AccessibilityService_canPerformGestures, false)) {
                 mCapabilities |= CAPABILITY_CAN_PERFORM_GESTURES;
             }
+            if (asAttributes.getBoolean(com.android.internal.R.styleable
+                    .AccessibilityService_canCaptureFingerprintGestures, false)) {
+                mCapabilities |= CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES;
+            }
             TypedValue peekedValue = asAttributes.peekValue(
                     com.android.internal.R.styleable.AccessibilityService_description);
             if (peekedValue != null) {
@@ -946,6 +936,8 @@
                 return "FLAG_RETRIEVE_INTERACTIVE_WINDOWS";
             case FLAG_ENABLE_ACCESSIBILITY_VOLUME:
                 return "FLAG_ENABLE_ACCESSIBILITY_VOLUME";
+            case FLAG_CAPTURE_FINGERPRINT_GESTURES:
+                return "FLAG_CAPTURE_FINGERPRINT_GESTURES";
             default:
                 return null;
         }
@@ -973,6 +965,8 @@
                 return "CAPABILITY_CAN_CONTROL_MAGNIFICATION";
             case CAPABILITY_CAN_PERFORM_GESTURES:
                 return "CAPABILITY_CAN_PERFORM_GESTURES";
+            case CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES:
+                return "CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES";
             default:
                 return "UNKNOWN";
         }
@@ -981,17 +975,29 @@
     /**
      * @hide
      * @return The list of {@link CapabilityInfo} objects.
+     * @deprecated The version that takes a context works better.
      */
     public List<CapabilityInfo> getCapabilityInfos() {
+        return getCapabilityInfos(null);
+    }
+
+    /**
+     * @hide
+     * @param context A valid context
+     * @return The list of {@link CapabilityInfo} objects.
+     */
+    public List<CapabilityInfo> getCapabilityInfos(Context context) {
         if (mCapabilities == 0) {
             return Collections.emptyList();
         }
         int capabilities = mCapabilities;
         List<CapabilityInfo> capabilityInfos = new ArrayList<CapabilityInfo>();
+        SparseArray<CapabilityInfo> capabilityInfoSparseArray =
+                getCapabilityInfoSparseArray(context);
         while (capabilities != 0) {
             final int capabilityBit = 1 << Integer.numberOfTrailingZeros(capabilities);
             capabilities &= ~capabilityBit;
-            CapabilityInfo capabilityInfo = sAvailableCapabilityInfos.get(capabilityBit);
+            CapabilityInfo capabilityInfo = capabilityInfoSparseArray.get(capabilityBit);
             if (capabilityInfo != null) {
                 capabilityInfos.add(capabilityInfo);
             }
@@ -999,6 +1005,44 @@
         return capabilityInfos;
     }
 
+    private static SparseArray<CapabilityInfo> getCapabilityInfoSparseArray(Context context) {
+        if (sAvailableCapabilityInfos == null) {
+            sAvailableCapabilityInfos = new SparseArray<CapabilityInfo>();
+            sAvailableCapabilityInfos.put(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT,
+                    new CapabilityInfo(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT,
+                            R.string.capability_title_canRetrieveWindowContent,
+                            R.string.capability_desc_canRetrieveWindowContent));
+            sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION,
+                    new CapabilityInfo(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION,
+                            R.string.capability_title_canRequestTouchExploration,
+                            R.string.capability_desc_canRequestTouchExploration));
+            sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY,
+                    new CapabilityInfo(CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY,
+                            R.string.capability_title_canRequestEnhancedWebAccessibility,
+                            R.string.capability_desc_canRequestEnhancedWebAccessibility));
+            sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS,
+                    new CapabilityInfo(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS,
+                            R.string.capability_title_canRequestFilterKeyEvents,
+                            R.string.capability_desc_canRequestFilterKeyEvents));
+            sAvailableCapabilityInfos.put(CAPABILITY_CAN_CONTROL_MAGNIFICATION,
+                    new CapabilityInfo(CAPABILITY_CAN_CONTROL_MAGNIFICATION,
+                            R.string.capability_title_canControlMagnification,
+                            R.string.capability_desc_canControlMagnification));
+            sAvailableCapabilityInfos.put(CAPABILITY_CAN_PERFORM_GESTURES,
+                    new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES,
+                            R.string.capability_title_canPerformGestures,
+                            R.string.capability_desc_canPerformGestures));
+            if ((context == null)
+                    || context.getSystemService(FingerprintManager.class).isHardwareDetected()) {
+                sAvailableCapabilityInfos.put(CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES,
+                        new CapabilityInfo(CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES,
+                                R.string.capability_title_canCaptureFingerprintGestures,
+                                R.string.capability_desc_canCaptureFingerprintGestures));
+            }
+        }
+        return sAvailableCapabilityInfos;
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/accessibilityservice/FingerprintGestureController.java b/core/java/android/accessibilityservice/FingerprintGestureController.java
new file mode 100644
index 0000000..e203c6d
--- /dev/null
+++ b/core/java/android/accessibilityservice/FingerprintGestureController.java
@@ -0,0 +1,185 @@
+/*
+ * 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_CAPTURE_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_CAPTURE_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.onGesture(gesture));
+            } else {
+                callback.onGesture(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 onGesture(int gesture) {}
+    }
+}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index da16a65..3f778ad 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -46,4 +46,8 @@
     void onSoftKeyboardShowModeChanged(int showMode);
 
     void onPerformGestureResult(int sequence, boolean completedSuccessfully);
+
+    void onFingerprintCapturingGesturesChanged(boolean capturing);
+
+    void onFingerprintGesture(int gesture);
 }
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 81cddba..5499bd5 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -89,4 +89,6 @@
     void setSoftKeyboardCallbackEnabled(boolean enabled);
 
     void sendGesture(int sequence, in ParceledListSlice gestureSteps);
+
+    boolean isFingerprintGestureDetectionAvailable();
 }
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 54cc4a0..6d1d1a3 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1108,6 +1108,16 @@
                 public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
                     /* do nothing */
                 }
+
+                @Override
+                public void onFingerprintCapturingGesturesChanged(boolean active) {
+                    /* do nothing */
+                }
+
+                @Override
+                public void onFingerprintGesture(int gesture) {
+                    /* do nothing */
+                }
             });
         }
     }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl b/core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl
new file mode 100644
index 0000000..5bcf476
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.hardware.fingerprint;
+
+/**
+ * Callback when clients become active or inactive.
+ * @hide
+ */
+oneway interface IFingerprintClientActiveCallback {
+    void onClientActiveChanged(boolean isActive);
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index ae3fc37..4879d54 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -16,6 +16,7 @@
 package android.hardware.fingerprint;
 
 import android.os.Bundle;
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
 import android.hardware.fingerprint.Fingerprint;
@@ -82,4 +83,13 @@
 
     // Enumerate all fingerprints
     void enumerate(IBinder token, int userId, IFingerprintServiceReceiver receiver);
+
+    // Check if a client request is currently being handled
+    boolean isClientActive();
+
+    // Add a callback which gets notified when the service starts and stops handling client requests
+    void addClientActiveCallback(IFingerprintClientActiveCallback callback);
+
+    // Removes a callback set by addClientActiveCallback
+    void removeClientActiveCallback(IFingerprintClientActiveCallback callback);
 }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index bfb8d83..1ef0d17 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -633,6 +633,28 @@
     }
 
     /**
+     * Report a fingerprint gesture to accessibility. Only available for the system process.
+     *
+     * @param keyCode The key code of the gesture
+     * @return {@code true} if accessibility consumes the event. {@code false} if not.
+     * @hide
+     */
+    public boolean sendFingerprintGesture(int keyCode) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return false;
+            }
+        }
+        try {
+            return service.sendFingerprintGesture(keyCode);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Sets the current state and notifies listeners, if necessary.
      *
      * @param stateFlags The state flags.
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index ed77f68..136bbbe 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -1,5 +1,4 @@
-/* //device/java/android/android/app/INotificationManager.aidl
-**
+/*
 ** Copyright 2009, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
@@ -62,4 +61,7 @@
 
     // Requires WRITE_SECURE_SETTINGS
     void performAccessibilityShortcut();
+
+    // System process only
+    boolean sendFingerprintGesture(int gestureKeyCode);
 }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 34f78f3..c548219 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3392,6 +3392,8 @@
             <flag name="flagRetrieveInteractiveWindows" value="0x00000040" />
             <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_ENABLE_ACCESSIBILITY_VOLUME} -->
             <flag name="flagEnableAccessibilityVolume" value="0x00000080" />
+            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_CAPTURE_FINGERPRINT_GESTURES} -->
+            <flag name="flagCaptureFingerprintGestures" value="0x00000200" />
         </attr>
         <!-- Component name of an activity that allows the user to modify
              the settings for this service. This setting cannot be changed at runtime. -->
@@ -3440,6 +3442,14 @@
              </p>
          -->
         <attr name="canPerformGestures" format="boolean" />
+        <!-- Attribute whether the accessibility service wants to be able to capture gestures from
+             the fingerprint sensor.
+             <p>
+             Required to allow setting the {@link android.accessibilityservice
+             #AccessibilityServiceInfo#FLAG_CAN_CAPTURE_FINGERPRINT_GESTURES} flag.
+             </p>
+         -->
+        <attr name="canCaptureFingerprintGestures" format="boolean" />
         <!-- Short description of the accessibility service purpose or behavior.-->
         <attr name="description" />
     </declare-styleable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 737dab0..d795d80 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2791,6 +2791,7 @@
         <public name="colorMode" />
         <public name="isolatedSplits" />
         <public name="targetSandboxVersion" />
+        <public name="canCaptureFingerprintGestures" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ac8c896..81582d2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -681,6 +681,12 @@
     <string name="capability_desc_canPerformGestures">Can tap, swipe, pinch, and perform other
         gestures.</string>
 
+    <!-- Title for the capability of an accessibility service to capture fingerprint gestures. -->
+    <string name="capability_title_canCaptureFingerprintGestures">Fingerprint gestures</string>
+    <!-- Description for the capability of an accessibility service to perform gestures. -->
+    <string name="capability_desc_canCaptureFingerprintGestures">Can capture gestures performed on
+        the device's fingerprint sensor.</string>
+
     <!--  Permissions -->
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 25ebb87..e550236 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2833,4 +2833,8 @@
   <java-symbol type="id" name="autofill_save_title" />
   <java-symbol type="id" name="autofill_save_no" />
   <java-symbol type="id" name="autofill_save_yes" />
+
+  <!-- Accessibility fingerprint gestures -->
+  <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
+  <java-symbol type="string" name="capability_desc_canCaptureFingerprintGestures" />
 </resources>
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ece5149..b68ac3b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -63,11 +63,13 @@
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
+import android.hardware.fingerprint.IFingerprintService;
 import android.text.TextUtils;
 import android.text.TextUtils.SimpleStringSplitter;
 import android.util.Slog;
@@ -203,6 +205,8 @@
 
     private MotionEventInjector mMotionEventInjector;
 
+    private FingerprintGestureDispatcher mFingerprintGestureDispatcher;
+
     private final Set<ComponentName> mTempComponentNameSet = new HashSet<>();
 
     private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
@@ -1374,6 +1378,10 @@
         mMainHandler.obtainMessage(MainHandler.MSG_UPDATE_INPUT_FILTER, userState).sendToTarget();
     }
 
+    private void scheduleUpdateFingerprintGestureHandling(UserState userState) {
+        mMainHandler.obtainMessage(MainHandler.MSG_UPDATE_FINGERPRINT, userState).sendToTarget();
+    }
+
     private void updateInputFilter(UserState userState) {
         boolean setInputFilter = false;
         AccessibilityInputFilter inputFilter = null;
@@ -1501,6 +1509,7 @@
         updateDisplayInversionLocked(userState);
         updateMagnificationLocked(userState);
         updateSoftKeyboardShowModeLocked(userState);
+        scheduleUpdateFingerprintGestureHandling(userState);
         scheduleUpdateInputFilter(userState);
         scheduleUpdateClientsIfNeededLocked(userState);
     }
@@ -1919,6 +1928,35 @@
         }
     }
 
+    private void updateFingerprintGestureHandling(UserState userState) {
+        final List<Service> services;
+        synchronized (mLock) {
+            // Only create the controller when a service wants to use the feature
+            services = userState.mBoundServices;
+            int numServices = services.size();
+            for (int i = 0; i < numServices; i++) {
+                if (services.get(i).isCapturingFingerprintGestures()) {
+                    final long identity = Binder.clearCallingIdentity();
+                    IFingerprintService service = null;
+                    try {
+                        service = IFingerprintService.Stub.asInterface(
+                                ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                    if (service != null) {
+                        mFingerprintGestureDispatcher = new FingerprintGestureDispatcher(
+                                service, mLock);
+                        break;
+                    }
+                }
+            }
+        }
+        if (mFingerprintGestureDispatcher != null) {
+            mFingerprintGestureDispatcher.updateClientList(services);
+        }
+    }
+
     private MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
         IBinder windowToken = mGlobalWindowTokens.get(windowId);
         if (windowToken == null) {
@@ -2001,6 +2039,27 @@
         }
     }
 
+    /**
+     * AIDL-exposed method. System only.
+     * Inform accessibility that a fingerprint gesture was performed
+     *
+     * @param gestureKeyCode The key code corresponding to the fingerprint gesture.
+     * @return {@code true} if accessibility consumes the fingerprint gesture, {@code false} if it
+     * doesn't.
+     */
+    @Override
+    public boolean sendFingerprintGesture(int gestureKeyCode) {
+        synchronized(mLock) {
+            if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+                throw new SecurityException("Only SYSTEM can call sendFingerprintGesture");
+            }
+        }
+        if (mFingerprintGestureDispatcher == null) {
+            return false;
+        }
+        return mFingerprintGestureDispatcher.onFingerprintGesture(gestureKeyCode);
+    }
+
     private class SettingsStringHelper {
         private static final String SETTINGS_DELIMITER = ":";
         private ContentResolver mContentResolver;
@@ -2131,6 +2190,7 @@
         public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8;
         public static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = 9;
         public static final int MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS = 10;
+        public static final int MSG_UPDATE_FINGERPRINT = 11;
 
         public MainHandler(Looper looper) {
             super(looper);
@@ -2199,6 +2259,10 @@
                 case MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS: {
                     notifyClientsOfServicesStateChange();
                 } break;
+
+                case MSG_UPDATE_FINGERPRINT: {
+                    updateFingerprintGestureHandling((UserState) msg.obj);
+                } break;
             }
         }
 
@@ -2329,7 +2393,8 @@
      * connection for the service.
      */
     class Service extends IAccessibilityServiceConnection.Stub
-            implements ServiceConnection, DeathRecipient, KeyEventDispatcher.KeyEventFilter {;
+            implements ServiceConnection, DeathRecipient, KeyEventDispatcher.KeyEventFilter,
+            FingerprintGestureDispatcher.FingerprintGestureClient {
 
         final int mUserId;
 
@@ -2359,6 +2424,8 @@
 
         boolean mRetrieveInteractiveWindows;
 
+        boolean mCaptureFingerprintGestures;
+
         int mFetchFlags;
 
         long mNotificationTimeout;
@@ -2438,6 +2505,47 @@
             return true;
         }
 
+        @Override
+        public boolean isCapturingFingerprintGestures() {
+            return (mServiceInterface != null)
+                    && mSecurityPolicy.canCaptureFingerprintGestures(this)
+                    && mCaptureFingerprintGestures;
+        }
+
+        @Override
+        public void onFingerprintGestureDetectionActiveChanged(boolean active) {
+            if (!isCapturingFingerprintGestures()) {
+                return;
+            }
+            IAccessibilityServiceClient serviceInterface;
+            synchronized (mLock) {
+                serviceInterface = mServiceInterface;
+            }
+            if (serviceInterface != null) {
+                try {
+                    mServiceInterface.onFingerprintCapturingGesturesChanged(active);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
+        @Override
+        public void onFingerprintGesture(int gesture) {
+            if (!isCapturingFingerprintGestures()) {
+                return;
+            }
+            IAccessibilityServiceClient serviceInterface;
+            synchronized (mLock) {
+                serviceInterface = mServiceInterface;
+            }
+            if (serviceInterface != null) {
+                try {
+                    mServiceInterface.onFingerprintGesture(gesture);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
         public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
             mEventTypes = info.eventTypes;
             mFeedbackType = info.feedbackType;
@@ -2471,6 +2579,8 @@
                     & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
             mRetrieveInteractiveWindows = (info.flags
                     & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
+            mCaptureFingerprintGestures = (info.flags
+                    & AccessibilityServiceInfo.FLAG_CAPTURE_FINGERPRINT_GESTURES) != 0;
         }
 
         /**
@@ -3060,6 +3170,13 @@
         }
 
         @Override
+        public boolean isFingerprintGestureDetectionAvailable() {
+            return isCapturingFingerprintGestures()
+                    && (mFingerprintGestureDispatcher != null)
+                    && mFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable();
+        }
+
+        @Override
         public float getMagnificationScale() {
             synchronized (mLock) {
                 if (!isCalledForCurrentUserLocked()) {
@@ -4234,6 +4351,11 @@
                     & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0;
         }
 
+        public boolean canCaptureFingerprintGestures(Service service) {
+            return (service.mAccessibilityServiceInfo.getCapabilities()
+                    & AccessibilityServiceInfo.CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES) != 0;
+        }
+
         private int resolveProfileParentLocked(int userId) {
             if (userId != mCurrentUserId) {
                 final long identity = Binder.clearCallingIdentity();
diff --git a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
new file mode 100644
index 0000000..fe787b3
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
@@ -0,0 +1,212 @@
+/*
+ * 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 com.android.server.accessibility;
+
+import android.accessibilityservice.FingerprintGestureController;
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.hardware.fingerprint.IFingerprintService;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.KeyEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulate fingerprint gesture logic
+ */
+public class FingerprintGestureDispatcher extends IFingerprintClientActiveCallback.Stub
+        implements Handler.Callback{
+    private static final int MSG_REGISTER = 1;
+    private static final int MSG_UNREGISTER = 2;
+    private static final String LOG_TAG = "FingerprintGestureDispatcher";
+
+    private final List<FingerprintGestureClient> mCapturingClients = new ArrayList<>(0);
+    private final Object mLock;
+    private final IFingerprintService mFingerprintService;
+    private final Handler mHandler;
+
+    // This field is ground truth for whether or not we are registered. Only write to it in handler.
+    private boolean mRegisteredReadOnlyExceptInHandler;
+
+    /**
+     * @param fingerprintService The system's fingerprint service
+     * @param lock A lock to use when managing internal state
+     */
+    public FingerprintGestureDispatcher(IFingerprintService fingerprintService, Object lock) {
+        mFingerprintService = fingerprintService;
+        mLock = lock;
+        mHandler = new Handler(this);
+    }
+
+    /**
+     * @param fingerprintService The system's fingerprint service
+     * @param lock A lock to use when managing internal state
+     * @param handler A handler to use internally. Used for testing.
+     */
+    public FingerprintGestureDispatcher(IFingerprintService fingerprintService, Object lock,
+            Handler handler) {
+        mFingerprintService = fingerprintService;
+        mLock = lock;
+        mHandler = handler;
+    }
+
+    /**
+     * Update the list of clients that are interested in fingerprint gestures.
+     *
+     * @param clientList The list of potential clients.
+     */
+    public void updateClientList(List<? extends FingerprintGestureClient> clientList) {
+        synchronized (mLock) {
+            mCapturingClients.clear();
+            for (int i = 0; i < clientList.size(); i++) {
+                FingerprintGestureClient client = clientList.get(i);
+                if (client.isCapturingFingerprintGestures()) {
+                    mCapturingClients.add(client);
+                }
+            }
+            if (mCapturingClients.isEmpty()) {
+                if (mRegisteredReadOnlyExceptInHandler) {
+                    mHandler.obtainMessage(MSG_UNREGISTER).sendToTarget();
+                }
+            } else {
+                if(!mRegisteredReadOnlyExceptInHandler) {
+                    mHandler.obtainMessage(MSG_REGISTER).sendToTarget();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onClientActiveChanged(boolean nonGestureFingerprintClientActive) {
+        synchronized (mLock) {
+            for (int i = 0; i < mCapturingClients.size(); i++) {
+                mCapturingClients.get(i).onFingerprintGestureDetectionActiveChanged(
+                        !nonGestureFingerprintClientActive);
+            }
+        }
+    }
+
+    public boolean isFingerprintGestureDetectionAvailable() {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            return !mFingerprintService.isClientActive();
+        } catch (RemoteException re) {
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Called when the fingerprint sensor detects a gesture
+     *
+     * @param fingerprintKeyCode
+     * @return {@code true} if the gesture is consumed. {@code false} otherwise.
+     */
+    public boolean onFingerprintGesture(int fingerprintKeyCode) {
+        int idForFingerprintGestureManager;
+
+        final List<FingerprintGestureClient> clientList;
+        synchronized (mLock) {
+            if (mCapturingClients.isEmpty()) {
+                return false;
+            }
+            switch (fingerprintKeyCode) {
+                case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
+                    idForFingerprintGestureManager =
+                            FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP;
+                    break;
+                case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
+                    idForFingerprintGestureManager =
+                            FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN;
+                    break;
+                case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT:
+                    idForFingerprintGestureManager =
+                            FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_RIGHT;
+                    break;
+                case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
+                    idForFingerprintGestureManager =
+                            FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_LEFT;
+                    break;
+                default:
+                    return false;
+            }
+            clientList = new ArrayList<>(mCapturingClients);
+        }
+        for (int i = 0; i < clientList.size(); i++) {
+            clientList.get(i).onFingerprintGesture(idForFingerprintGestureManager);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean handleMessage(Message message) {
+        if (message.what == MSG_REGISTER) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mFingerprintService.addClientActiveCallback(this);
+                mRegisteredReadOnlyExceptInHandler = true;
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Failed to register for fingerprint activity callbacks");
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            return false;
+        } else if (message.what == MSG_UNREGISTER) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mFingerprintService.removeClientActiveCallback(this);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Failed to unregister for fingerprint activity callbacks");
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            mRegisteredReadOnlyExceptInHandler = false;
+        } else {
+            Slog.e(LOG_TAG, "Unknown message: " + message.what);
+            return false;
+        }
+        return true;
+    }
+
+    // Interface for potential clients.
+    public interface FingerprintGestureClient {
+        /**
+         * @return {@code true} if the client is capturing fingerprint gestures
+         */
+        boolean isCapturingFingerprintGestures();
+
+        /**
+         * Callback when gesture detection becomes active or inactive.
+         *
+         * @param active {@code true} when detection is active
+         */
+        void onFingerprintGestureDetectionActiveChanged(boolean active);
+
+        /**
+         * Callback when gesture is detected
+         *
+         * @param gesture The identifier for the gesture. For example,
+         * {@link FingerprintGestureController#FINGERPRINT_GESTURE_SWIPE_LEFT}
+         */
+        void onFingerprintGesture(int gesture);
+    }
+}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index e2e0d6b..d1f7cfd 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -30,6 +30,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
 import android.os.Binder;
 import android.os.Bundle;
@@ -82,6 +83,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -109,6 +111,8 @@
 
     private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
             new ArrayList<>();
+    private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
+            new CopyOnWriteArrayList<>();
     private final AppOpsManager mAppOps;
     private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
     private static final int MAX_FAILED_ATTEMPTS = 5;
@@ -338,6 +342,9 @@
             if (DEBUG) Slog.v(TAG, "Done with client: " + client.getOwnerString());
             mCurrentClient = null;
         }
+        if (mPendingClient == null) {
+            notifyClientActiveCallbacks(false);
+        }
     }
 
     private boolean inLockoutMode() {
@@ -407,6 +414,8 @@
                     + newClient.getClass().getSuperclass().getSimpleName()
                     + "(" + newClient.getOwnerString() + ")"
                     + ", initiatedByClient = " + initiatedByClient + ")");
+            notifyClientActiveCallbacks(true);
+
             newClient.start();
         }
     }
@@ -578,6 +587,18 @@
         }
     }
 
+    private void notifyClientActiveCallbacks(boolean isActive) {
+        List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
+        for (int i = 0; i < callbacks.size(); i++) {
+            try {
+                callbacks.get(i).onClientActiveChanged(isActive);
+            } catch (RemoteException re) {
+                // If the remote is dead, stop notifying it
+                mClientActiveCallbacks.remove(callbacks.get(i));
+           }
+        }
+    }
+
     private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
                 IFingerprintServiceReceiver receiver, int flags, boolean restricted,
                 String opPackageName) {
@@ -1047,6 +1068,26 @@
                 }
             });
         }
+
+        @Override
+        public boolean isClientActive() {
+            checkPermission(MANAGE_FINGERPRINT);
+            synchronized(FingerprintService.this) {
+                return (mCurrentClient != null) || (mPendingClient != null);
+            }
+        }
+
+        @Override
+        public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
+            checkPermission(MANAGE_FINGERPRINT);
+            mClientActiveCallbacks.add(callback);
+        }
+
+        @Override
+        public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
+            checkPermission(MANAGE_FINGERPRINT);
+            mClientActiveCallbacks.remove(callback);
+        }
     }
 
     private void dumpInternal(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4350ed9..b69ae98 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6106,13 +6106,18 @@
      * @param event
      */
     private void interceptSystemNavigationKey(KeyEvent event) {
-        if (event.getAction() == KeyEvent.ACTION_UP && areSystemNavigationKeysEnabled()) {
-            IStatusBarService sbar = getStatusBarService();
-            if (sbar != null) {
-                try {
-                    sbar.handleSystemNavigationKey(event.getKeyCode());
-                } catch (RemoteException e1) {
-                    // oops, no statusbar. Ignore event.
+        if (event.getAction() == KeyEvent.ACTION_UP) {
+            if (!mAccessibilityManager.isEnabled()
+                    || !mAccessibilityManager.sendFingerprintGesture(event.getKeyCode())) {
+                if (areSystemNavigationKeysEnabled()) {
+                    IStatusBarService sbar = getStatusBarService();
+                    if (sbar != null) {
+                        try {
+                            sbar.handleSystemNavigationKey(event.getKeyCode());
+                        } catch (RemoteException e1) {
+                            // oops, no statusbar. Ignore event.
+                        }
+                    }
                 }
             }
         }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
new file mode 100644
index 0000000..cf477f2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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 com.android.server.accessibility;
+
+import android.accessibilityservice.FingerprintGestureController;
+import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.os.Looper;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static android.accessibilityservice.FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for FingerprintGestureController.
+ * TODO: These tests aren't really for server code, so this isn't their ideal home.
+ */
+public class FingerprintGestureControllerTest {
+    @Mock IAccessibilityServiceConnection mMockAccessibilityServiceConnection;
+    @Mock FingerprintGestureCallback mMockFingerprintGestureCallback;
+    FingerprintGestureController mFingerprintGestureController;
+
+    @BeforeClass
+    public static void oneTimeInitialization() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mFingerprintGestureController =
+                new FingerprintGestureController(mMockAccessibilityServiceConnection);
+    }
+
+    @Test
+    public void testIsGestureDetectionActive_returnsValueFromServer() throws Exception {
+        when(mMockAccessibilityServiceConnection.isFingerprintGestureDetectionAvailable())
+                .thenReturn(true);
+        assertTrue(mFingerprintGestureController.isGestureDetectionAvailable());
+        when(mMockAccessibilityServiceConnection.isFingerprintGestureDetectionAvailable())
+                .thenReturn(false);
+        assertFalse(mFingerprintGestureController.isGestureDetectionAvailable());
+    }
+
+    @Test
+    public void testCallbacks_withNoListeners_shouldNotCrash() {
+        mFingerprintGestureController.onGestureDetectionActiveChanged(true);
+        mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
+    }
+
+    @Test
+    public void testDetectionActiveCallback_noHandler_shouldCallback() {
+        mFingerprintGestureController.registerFingerprintGestureCallback(
+                mMockFingerprintGestureCallback, null);
+        mFingerprintGestureController.onGestureDetectionActiveChanged(true);
+        verify(mMockFingerprintGestureCallback, times(1))
+                .onGestureDetectionAvailabilityChanged(true);
+        mFingerprintGestureController.onGestureDetectionActiveChanged(false);
+        verify(mMockFingerprintGestureCallback, times(1))
+                .onGestureDetectionAvailabilityChanged(false);
+
+        reset(mMockFingerprintGestureCallback);
+        mFingerprintGestureController.unregisterFingerprintGestureCallback(
+                mMockFingerprintGestureCallback);
+        mFingerprintGestureController.onGestureDetectionActiveChanged(true);
+        mFingerprintGestureController.onGestureDetectionActiveChanged(false);
+        verifyZeroInteractions(mMockFingerprintGestureCallback);
+    }
+
+    @Test
+    public void testDetectionActiveCallback_withHandler_shouldPostRunnableToHandler() {
+        MessageCapturingHandler messageCapturingHandler = new MessageCapturingHandler((message) -> {
+            message.getCallback().run();
+            return true;
+        });
+
+        mFingerprintGestureController.registerFingerprintGestureCallback(
+                mMockFingerprintGestureCallback, messageCapturingHandler);
+        mFingerprintGestureController.onGestureDetectionActiveChanged(true);
+        verify(mMockFingerprintGestureCallback, times(0))
+                .onGestureDetectionAvailabilityChanged(true);
+        messageCapturingHandler.sendLastMessage();
+        verify(mMockFingerprintGestureCallback, times(1))
+                .onGestureDetectionAvailabilityChanged(true);
+
+        mFingerprintGestureController.onGestureDetectionActiveChanged(false);
+        verify(mMockFingerprintGestureCallback, times(0))
+                .onGestureDetectionAvailabilityChanged(false);
+        messageCapturingHandler.sendLastMessage();
+        verify(mMockFingerprintGestureCallback, times(1))
+                .onGestureDetectionAvailabilityChanged(false);
+
+        reset(mMockFingerprintGestureCallback);
+        mFingerprintGestureController.unregisterFingerprintGestureCallback(
+                mMockFingerprintGestureCallback);
+        mFingerprintGestureController.onGestureDetectionActiveChanged(true);
+        mFingerprintGestureController.onGestureDetectionActiveChanged(false);
+        assertFalse(messageCapturingHandler.hasMessages());
+        verifyZeroInteractions(mMockFingerprintGestureCallback);
+    }
+
+    @Test
+    public void testGestureCallback_noHandler_shouldCallListener() {
+        mFingerprintGestureController.registerFingerprintGestureCallback(
+                mMockFingerprintGestureCallback, null);
+        mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
+        verify(mMockFingerprintGestureCallback, times(1)).onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
+
+        reset(mMockFingerprintGestureCallback);
+        mFingerprintGestureController.unregisterFingerprintGestureCallback(
+                mMockFingerprintGestureCallback);
+        mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
+        verifyZeroInteractions(mMockFingerprintGestureCallback);
+    }
+
+    @Test
+    public void testGestureCallback_withHandler_shouldPostRunnableToHandler() {
+        MessageCapturingHandler messageCapturingHandler = new MessageCapturingHandler((message) -> {
+            message.getCallback().run();
+            return true;
+        });
+
+        mFingerprintGestureController.registerFingerprintGestureCallback(
+                mMockFingerprintGestureCallback, messageCapturingHandler);
+        mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
+        verify(mMockFingerprintGestureCallback, times(0)).onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
+        messageCapturingHandler.sendLastMessage();
+        verify(mMockFingerprintGestureCallback, times(1)).onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
+
+        reset(mMockFingerprintGestureCallback);
+        mFingerprintGestureController.unregisterFingerprintGestureCallback(
+                mMockFingerprintGestureCallback);
+        mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
+        assertFalse(messageCapturingHandler.hasMessages());
+        verifyZeroInteractions(mMockFingerprintGestureCallback);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
new file mode 100644
index 0000000..98bf53c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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 com.android.server.accessibility;
+
+import android.accessibilityservice.FingerprintGestureController;
+import android.hardware.fingerprint.IFingerprintService;
+import android.os.Handler;
+import android.os.Message;
+import android.view.KeyEvent;
+
+import com.android.server.accessibility.FingerprintGestureDispatcher.FingerprintGestureClient;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for FingerprintGestureDispatcher
+ */
+public class FingerprintGestureDispatcherTest {
+
+    private @Mock IFingerprintService mMockFingerprintService;
+    private @Mock FingerprintGestureClient mNonGestureCapturingClient;
+    private @Mock FingerprintGestureClient mGestureCapturingClient;
+    private @Mock FingerprintGestureDispatcher mFingerprintGestureDispatcher;
+    private MessageCapturingHandler mMessageCapturingHandler;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mMessageCapturingHandler = new MessageCapturingHandler(
+                msg -> mFingerprintGestureDispatcher.handleMessage(msg));
+        mFingerprintGestureDispatcher = new FingerprintGestureDispatcher(mMockFingerprintService,
+                new Object(), mMessageCapturingHandler);
+        when(mNonGestureCapturingClient.isCapturingFingerprintGestures()).thenReturn(false);
+        when(mGestureCapturingClient.isCapturingFingerprintGestures()).thenReturn(true);
+    }
+
+    @Test
+    public void testNoServices_doesNotCrashOrConsumeGestures() {
+        mFingerprintGestureDispatcher.onClientActiveChanged(true);
+        mFingerprintGestureDispatcher.onClientActiveChanged(false);
+        assertFalse(mFingerprintGestureDispatcher.onFingerprintGesture(
+                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP));
+    }
+
+    @Test
+    public void testOneNonCapturingService_doesNotCrashOrConsumeGestures() {
+        mFingerprintGestureDispatcher.updateClientList(
+                Arrays.asList(mNonGestureCapturingClient));
+        mFingerprintGestureDispatcher.onClientActiveChanged(true);
+        mFingerprintGestureDispatcher.onClientActiveChanged(false);
+        assertFalse(mFingerprintGestureDispatcher.onFingerprintGesture(
+                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP));
+        verify(mNonGestureCapturingClient, times(0))
+                .onFingerprintGestureDetectionActiveChanged(anyBoolean());
+        verify(mNonGestureCapturingClient, times(0)).onFingerprintGesture(anyInt());
+    }
+
+    @Test
+    public void testOneCapturingService_notifiesClientOfActivityChanges() {
+        mFingerprintGestureDispatcher.updateClientList(
+                Arrays.asList(mGestureCapturingClient));
+        mFingerprintGestureDispatcher.onClientActiveChanged(true);
+        // Client active means gesture detection isn't.
+        verify(mGestureCapturingClient, times(1)).onFingerprintGestureDetectionActiveChanged(false);
+        verify(mGestureCapturingClient, times(0)).onFingerprintGestureDetectionActiveChanged(true);
+        mFingerprintGestureDispatcher.onClientActiveChanged(false);
+        verify(mGestureCapturingClient, times(1)).onFingerprintGestureDetectionActiveChanged(false);
+        verify(mGestureCapturingClient, times(1)).onFingerprintGestureDetectionActiveChanged(true);
+    }
+
+    @Test
+    public void testOneCapturingService_consumesGesturesAndPassesThemAlong() {
+        mFingerprintGestureDispatcher.updateClientList(
+                Arrays.asList(mGestureCapturingClient));
+        assertTrue(mFingerprintGestureDispatcher.onFingerprintGesture(
+                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP));
+        verify(mGestureCapturingClient, times(1)).onFingerprintGesture(
+                FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP);
+        assertTrue(mFingerprintGestureDispatcher.onFingerprintGesture(
+                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+        verify(mGestureCapturingClient, times(1)).onFingerprintGesture(
+                FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN);
+        assertTrue(mFingerprintGestureDispatcher.onFingerprintGesture(
+                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT));
+        verify(mGestureCapturingClient, times(1)).onFingerprintGesture(
+                FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_LEFT);
+        assertTrue(mFingerprintGestureDispatcher.onFingerprintGesture(
+                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT));
+        verify(mGestureCapturingClient, times(1)).onFingerprintGesture(
+                FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_RIGHT);
+    }
+
+    @Test
+    public void testInvalidKeyCodes_areNotCaptured() {
+        mFingerprintGestureDispatcher.updateClientList(
+                Arrays.asList(mGestureCapturingClient));
+        assertFalse(mFingerprintGestureDispatcher.onFingerprintGesture(
+                KeyEvent.KEYCODE_SPACE));
+        verify(mGestureCapturingClient, times(0)).onFingerprintGesture(anyInt());
+    }
+
+    @Test
+    public void testWithCapturingService_registersForFingerprintUpdates() throws Exception {
+        verifyNoMoreInteractions(mMockFingerprintService);
+        mFingerprintGestureDispatcher.updateClientList(
+                Arrays.asList(mGestureCapturingClient));
+        mMessageCapturingHandler.sendOneMessage();
+        verify(mMockFingerprintService).addClientActiveCallback(mFingerprintGestureDispatcher);
+    }
+
+    @Test
+    public void testWhenCapturingServiceStops_unregistersForFingerprintUpdates() throws Exception {
+        verifyNoMoreInteractions(mMockFingerprintService);
+        mFingerprintGestureDispatcher.updateClientList(
+                Arrays.asList(mGestureCapturingClient));
+        mMessageCapturingHandler.sendOneMessage();
+        mFingerprintGestureDispatcher.updateClientList(Collections.emptyList());
+        mMessageCapturingHandler.sendOneMessage();
+        verify(mMockFingerprintService).removeClientActiveCallback(mFingerprintGestureDispatcher);
+    }
+
+    @Test
+    public void testIsGestureDetectionActive_dependsOnFingerprintService() throws Exception {
+        when(mMockFingerprintService.isClientActive()).thenReturn(true);
+        assertFalse(mFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable());
+        when(mMockFingerprintService.isClientActive()).thenReturn(false);
+        assertTrue(mFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable());
+    }
+}