Update and cleanup FingerprintManager API to new spec

Fixes bug 21040383

Change-Id: Ib97d486535869814d5b449cccb31670e8f412552
diff --git a/api/current.txt b/api/current.txt
index 8680c49..4c775a4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13942,16 +13942,8 @@
 
 package android.hardware.fingerprint {
 
-  public final class Fingerprint implements android.os.Parcelable {
-    ctor public Fingerprint(java.lang.CharSequence, int, int, long);
-    method public int describeContents();
-    method public java.lang.CharSequence getName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.hardware.fingerprint.Fingerprint> CREATOR;
-  }
-
   public class FingerprintManager {
-    method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, int);
+    method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
     method public boolean hasEnrolledFingerprints();
     method public boolean isHardwareDetected();
     field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
@@ -13960,14 +13952,12 @@
     field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
     field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
     field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
-    field public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; // 0x3e8
     field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
     field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
     field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
     field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
     field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
     field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
-    field public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000; // 0x3e8
   }
 
   public static abstract class FingerprintManager.AuthenticationCallback {
@@ -13979,11 +13969,10 @@
   }
 
   public static final class FingerprintManager.AuthenticationResult {
-    ctor public FingerprintManager.AuthenticationResult(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.hardware.fingerprint.Fingerprint);
     method public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
   }
 
-  public static class FingerprintManager.CryptoObject {
+  public static final class FingerprintManager.CryptoObject {
     ctor public FingerprintManager.CryptoObject(java.security.Signature);
     ctor public FingerprintManager.CryptoObject(javax.crypto.Cipher);
     ctor public FingerprintManager.CryptoObject(javax.crypto.Mac);
diff --git a/api/system-current.txt b/api/system-current.txt
index 44baba2..5184825 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -14260,16 +14260,8 @@
 
 package android.hardware.fingerprint {
 
-  public final class Fingerprint implements android.os.Parcelable {
-    ctor public Fingerprint(java.lang.CharSequence, int, int, long);
-    method public int describeContents();
-    method public java.lang.CharSequence getName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.hardware.fingerprint.Fingerprint> CREATOR;
-  }
-
   public class FingerprintManager {
-    method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, int);
+    method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
     method public boolean hasEnrolledFingerprints();
     method public boolean isHardwareDetected();
     field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
@@ -14278,14 +14270,12 @@
     field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
     field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
     field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
-    field public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; // 0x3e8
     field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
     field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
     field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
     field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
     field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
     field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
-    field public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000; // 0x3e8
   }
 
   public static abstract class FingerprintManager.AuthenticationCallback {
@@ -14297,11 +14287,10 @@
   }
 
   public static final class FingerprintManager.AuthenticationResult {
-    ctor public FingerprintManager.AuthenticationResult(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.hardware.fingerprint.Fingerprint);
     method public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
   }
 
-  public static class FingerprintManager.CryptoObject {
+  public static final class FingerprintManager.CryptoObject {
     ctor public FingerprintManager.CryptoObject(java.security.Signature);
     ctor public FingerprintManager.CryptoObject(javax.crypto.Cipher);
     ctor public FingerprintManager.CryptoObject(javax.crypto.Mac);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index caf21d5..e61813c 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -18,32 +18,30 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.app.ActivityManagerNative;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Binder;
 import android.os.CancellationSignal;
 import android.os.CancellationSignal.OnCancelListener;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.Settings;
-import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
 import android.security.keystore.AndroidKeyStoreProvider;
 import android.util.Log;
 import android.util.Slog;
 
 import java.security.Signature;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 import javax.crypto.Cipher;
 import javax.crypto.Mac;
 
+import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.Manifest.permission.MANAGE_FINGERPRINT;
+
 /**
  * A class that coordinates access to the fingerprint hardware.
  * <p>
@@ -57,9 +55,10 @@
     private static final boolean DEBUG = true;
     private static final int MSG_ENROLL_RESULT = 100;
     private static final int MSG_ACQUIRED = 101;
-    private static final int MSG_AUTHENTICATED = 102;
-    private static final int MSG_ERROR = 103;
-    private static final int MSG_REMOVED = 104;
+    private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
+    private static final int MSG_AUTHENTICATION_FAILED = 103;
+    private static final int MSG_ERROR = 104;
+    private static final int MSG_REMOVED = 105;
 
     //
     // Error messages from fingerprint hardware during initilization, enrollment, authentication or
@@ -112,6 +111,7 @@
     /**
      * Hardware vendors may extend this list if there are conditions that do not fall under one of
      * the above categories. Vendors are responsible for providing error strings for these errors.
+     * @hide
      */
     public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
 
@@ -162,6 +162,7 @@
     /**
      * Hardware vendors may extend this list if there are conditions that do not fall under one of
      * the above categories. Vendors are responsible for providing error strings for these errors.
+     * @hide
      */
     public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
 
@@ -173,6 +174,7 @@
     private RemovalCallback mRemovalCallback;
     private CryptoObject mCryptoObject;
     private Fingerprint mRemovalFingerprint;
+    private Handler mHandler;
 
     private class OnEnrollCancelListener implements OnCancelListener {
         @Override
@@ -198,72 +200,71 @@
      * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
      * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
      */
-    public static class CryptoObject {
+    public static final class CryptoObject {
 
         public CryptoObject(@NonNull Signature signature) {
-            mSignature = signature;
-            mCipher = null;
-            mMac = null;
+            mCrypto = signature;
         }
 
         public CryptoObject(@NonNull Cipher cipher) {
-            mCipher = cipher;
-            mSignature = null;
-            mMac = null;
+            mCrypto = cipher;
         }
 
         public CryptoObject(@NonNull Mac mac) {
-            mMac = mac;
-            mCipher = null;
-            mSignature = null;
+            mCrypto = mac;
         }
 
         /**
          * Get {@link Signature} object.
          * @return {@link Signature} object or null if this doesn't contain one.
          */
-        public Signature getSignature() { return mSignature; }
+        public Signature getSignature() {
+            return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+        }
 
         /**
          * Get {@link Cipher} object.
          * @return {@link Cipher} object or null if this doesn't contain one.
          */
-        public Cipher getCipher() { return mCipher; }
+        public Cipher getCipher() {
+            return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+        }
 
         /**
          * Get {@link Mac} object.
          * @return {@link Mac} object or null if this doesn't contain one.
          */
-        public Mac getMac() { return mMac; }
+        public Mac getMac() {
+            return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+        }
 
         /**
          * @hide
          * @return the opId associated with this object or 0 if none
          */
         public long getOpId() {
-            if (mSignature != null) {
-                return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mSignature);
-            } else if (mCipher != null) {
-                return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCipher);
-            } else if (mMac != null) {
-                return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mMac);
-            }
-            return 0;
+            return mCrypto != null ?
+                    AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
         }
 
-        private final Signature mSignature;
-        private final Cipher mCipher;
-        private final Mac mMac;
+        private final Object mCrypto;
     };
 
     /**
      * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
-     *     CancellationSignal, AuthenticationCallback, int)}.
+     *     CancellationSignal, int, AuthenticationCallback, Handler)}.
      */
     public static final class AuthenticationResult {
         private Fingerprint mFingerprint;
         private CryptoObject mCryptoObject;
 
+        /**
+         * Authentication result
+         *
+         * @param crypto the crypto object
+         * @param fingerprint the recognized fingerprint data, if allowed.
+         * @hide
+         */
         public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint) {
             mCryptoObject = crypto;
             mFingerprint = fingerprint;
@@ -272,7 +273,7 @@
         /**
          * Obtain the crypto object associated with this transaction
          * @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject,
-         *     CancellationSignal, AuthenticationCallback, int)}.
+         *     CancellationSignal, int, AuthenticationCallback, Handler)}.
          */
         public CryptoObject getCryptoObject() { return mCryptoObject; }
 
@@ -287,28 +288,28 @@
 
     /**
      * Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject,
-     * CancellationSignal, AuthenticationCallback, int)}. Users of {@link
+     * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
      * FingerprintManager#authenticate(CryptoObject, CancellationSignal,
-     * AuthenticationCallback, int) } must provide an implementation of this for listening to
+     * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
      * fingerprint events.
      */
     public static abstract class AuthenticationCallback {
         /**
          * Called when an unrecoverable error has been encountered and the operation is complete.
          * No further callbacks will be made on this object.
-         * @param errMsgId An integer identifying the error message
+         * @param errorCode An integer identifying the error message
          * @param errString A human-readable error string that can be shown in UI
          */
-        public void onAuthenticationError(int errMsgId, CharSequence errString) { }
+        public void onAuthenticationError(int errorCode, CharSequence errString) { }
 
         /**
          * Called when a recoverable error has been encountered during authentication. The help
          * string is provided to give the user guidance for what went wrong, such as
          * "Sensor dirty, please clean it."
-         * @param helpMsgId An integer identifying the error message
+         * @param helpCode An integer identifying the error message
          * @param helpString A human-readable string that can be shown in UI
          */
-        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }
+        public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
 
         /**
          * Called when a fingerprint is recognized.
@@ -326,7 +327,7 @@
      * Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback,
      * CancellationSignal, int). Users of {@link #FingerprintManager()}
      * must provide an implementation of this to {@link FingerprintManager#enroll(long,
-     * CancellationSignal, EnrollmentCallback, int) for listening to fingerprint events.
+     * CancellationSignal, int, EnrollmentCallback) for listening to fingerprint events.
      *
      * @hide
      */
@@ -392,31 +393,35 @@
      *
      * @param crypto object associated with the call or null if none required.
      * @param cancel an object that can be used to cancel authentication
-     * @param callback an object to receive authentication events
      * @param flags optional flags; should be 0
+     * @param callback an object to receive authentication events
+     * @param handler an optional handler to handle callback events
      */
+    @RequiresPermission(USE_FINGERPRINT)
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            @NonNull AuthenticationCallback callback, int flags) {
-        authenticate(crypto, cancel, callback, flags, UserHandle.myUserId());
+            int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
+        authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId());
     }
 
     /**
-     * Request authentication of a crypto object. This call warms up the fingerprint hardware
-     * and starts scanning for a fingerprint. It terminates when
-     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
-     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
-     * which point the object is no longer valid. The operation can be canceled by using the
-     * provided cancel object.
-     *
-     * @param crypto object associated with the call or null if none required.
-     * @param cancel an object that can be used to cancel authentication
-     * @param callback an object to receive authentication events
-     * @param flags optional flags; should be 0
-     * @param userId the userId the fingerprint belongs to
+     * Use the provided handler thread for events.
+     * @param handler
+     */
+    private void useHandler(Handler handler) {
+        if (handler != null) {
+            mHandler = new MyHandler(handler.getLooper());
+        } else if (mHandler.getLooper() != mContext.getMainLooper()){
+            mHandler = new MyHandler(mContext.getMainLooper());
+        }
+    }
+
+    /**
+     * Per-user version
      * @hide
      */
+    @RequiresPermission(USE_FINGERPRINT)
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            @NonNull AuthenticationCallback callback, int flags, int userId) {
+            int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an authentication callback");
         }
@@ -431,6 +436,7 @@
         }
 
         if (mService != null) try {
+            useHandler(handler);
             mAuthenticationCallback = callback;
             mCryptoObject = crypto;
             long sessionId = crypto != null ? crypto.getOpId() : 0;
@@ -458,12 +464,13 @@
      * @param token a unique token provided by a recent creation or verification of device
      * credentials (e.g. pin, pattern or password).
      * @param cancel an object that can be used to cancel enrollment
-     * @param callback an object to receive enrollment events
      * @param flags optional flags
+     * @param callback an object to receive enrollment events
      * @hide
      */
-    public void enroll(byte [] token, CancellationSignal cancel, EnrollmentCallback callback,
-            int flags) {
+    @RequiresPermission(MANAGE_FINGERPRINT)
+    public void enroll(byte [] token, CancellationSignal cancel, int flags,
+            EnrollmentCallback callback) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an enrollment callback");
         }
@@ -496,6 +503,7 @@
      * existing device credentials (e.g. pin/pattern/password).
      * @hide
      */
+    @RequiresPermission(MANAGE_FINGERPRINT)
     public long preEnroll() {
         long result = 0;
         if (mService != null) try {
@@ -514,6 +522,7 @@
      *
      * @hide
      */
+    @RequiresPermission(MANAGE_FINGERPRINT)
     public void remove(Fingerprint fp, RemovalCallback callback) {
         if (mService != null) try {
             mRemovalCallback = callback;
@@ -535,6 +544,7 @@
      *
      * @hide
      */
+    @RequiresPermission(MANAGE_FINGERPRINT)
     public void rename(int fpId, String newName) {
         // Renames the given fpId
         if (mService != null) {
@@ -554,6 +564,7 @@
      *
      * @hide
      */
+    @RequiresPermission(USE_FINGERPRINT)
     public List<Fingerprint> getEnrolledFingerprints(int userId) {
         if (mService != null) try {
             return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
@@ -569,6 +580,7 @@
      *
      * @hide
      */
+    @RequiresPermission(USE_FINGERPRINT)
     public List<Fingerprint> getEnrolledFingerprints() {
         return getEnrolledFingerprints(UserHandle.myUserId());
     }
@@ -578,6 +590,7 @@
      *
      * @return true if at least one fingerprint is enrolled, false otherwise
      */
+    @RequiresPermission(USE_FINGERPRINT)
     public boolean hasEnrolledFingerprints() {
         if (mService != null) try {
             return mService.hasEnrolledFingerprints(UserHandle.myUserId(),
@@ -593,6 +606,7 @@
      *
      * @return true if hardware is present and functional, false otherwise.
      */
+    @RequiresPermission(USE_FINGERPRINT)
     public boolean isHardwareDetected() {
         if (mService != null) {
             try {
@@ -626,13 +640,15 @@
         return 0;
     }
 
-    private Handler mHandler;
-
     private class MyHandler extends Handler {
         private MyHandler(Context context) {
             super(context.getMainLooper());
         }
 
+        private MyHandler(Looper looper) {
+            super(looper);
+        }
+
         public void handleMessage(android.os.Message msg) {
             switch(msg.what) {
                 case MSG_ENROLL_RESULT:
@@ -641,8 +657,11 @@
                 case MSG_ACQUIRED:
                     sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */);
                     break;
-                case MSG_AUTHENTICATED:
-                    sendAuthenticatedResult((Fingerprint) msg.obj);
+                case MSG_AUTHENTICATION_SUCCEEDED:
+                    sendAuthenticatedSucceeded((Fingerprint) msg.obj);
+                    break;
+                case MSG_AUTHENTICATION_FAILED:
+                    sendAuthenticatedFailed();
                     break;
                 case MSG_ERROR:
                     sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */);
@@ -684,15 +703,16 @@
             }
         }
 
-        private void sendAuthenticatedResult(Fingerprint fp) {
+        private void sendAuthenticatedSucceeded(Fingerprint fp) {
             if (mAuthenticationCallback != null) {
-                if (fp.getFingerId() == 0) {
-                    // Fingerprint template valid but doesn't match one in database
-                    mAuthenticationCallback.onAuthenticationFailed();
-                } else {
-                    final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
-                    mAuthenticationCallback.onAuthenticationSucceeded(result);
-                }
+                final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
+                mAuthenticationCallback.onAuthenticationSucceeded(result);
+            }
+        }
+
+        private void sendAuthenticatedFailed() {
+            if (mAuthenticationCallback != null) {
+               mAuthenticationCallback.onAuthenticationFailed();
             }
         }
 
@@ -809,24 +829,33 @@
 
     private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
 
+        @Override // binder call
         public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
             mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
                     new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
         }
 
+        @Override // binder call
         public void onAcquired(long deviceId, int acquireInfo) {
             mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget();
         }
 
-        public void onAuthenticated(long deviceId, int fingerId, int groupId) {
-            mHandler.obtainMessage(MSG_AUTHENTICATED,
-                    new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
+        @Override // binder call
+        public void onAuthenticationSucceeded(long deviceId, Fingerprint fp) {
+            mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, fp).sendToTarget();
         }
 
+        @Override // binder call
+        public void onAuthenticationFailed(long deviceId) {
+            mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();;
+        }
+
+        @Override // binder call
         public void onError(long deviceId, int error) {
             mHandler.obtainMessage(MSG_ERROR, error, 0, deviceId).sendToTarget();
         }
 
+        @Override // binder call
         public void onRemoved(long deviceId, int fingerId, int groupId) {
             mHandler.obtainMessage(MSG_REMOVED, fingerId, groupId, deviceId).sendToTarget();
         }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index a2d74b8d..57a429f 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -15,6 +15,7 @@
  */
 package android.hardware.fingerprint;
 
+import android.hardware.fingerprint.Fingerprint;
 import android.os.Bundle;
 import android.os.UserHandle;
 
@@ -25,7 +26,8 @@
 oneway interface IFingerprintServiceReceiver {
     void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
     void onAcquired(long deviceId, int acquiredInfo);
-    void onAuthenticated(long deviceId, int fingerId, int groupId);
+    void onAuthenticationSucceeded(long deviceId, in Fingerprint fp);
+    void onAuthenticationFailed(long deviceId);
     void onError(long deviceId, int error);
     void onRemoved(long deviceId, int fingerId, int groupId);
 }
diff --git a/docs/html/training/articles/keystore.jd b/docs/html/training/articles/keystore.jd
index 20963f5..fca958e 100644
--- a/docs/html/training/articles/keystore.jd
+++ b/docs/html/training/articles/keystore.jd
@@ -197,7 +197,7 @@
   reset (e.g. by a Device Admin).</li>
 <li>User authentication is required for every use of the key. In this mode, a specific operation
   involving a specific key is authorized by the user. Currently, the only means of such
-  authorization is fingerprint authentication: {@link android.hardware.fingerprint.FingerprintManager#authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, int) FingerprintManager.authenticate}.
+  authorization is fingerprint authentication: {@link android.hardware.fingerprint.FingerprintManager#authenticate(CryptoObject, CancellationSignal, int, AuthenticationCallback, Handler) FingerprintManager.authenticate}.
   Such keys can only be generated or imported if at least one fingerprint is enrolled (see {@link android.hardware.fingerprint.FingerprintManager#hasEnrolledFingerprints() FingerprintManager.hasEnrolledFingerprints}).
   These keys become permanently invalidated once all fingerprints are unenrolled.</li>
 </ul>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3c30d8c..d889d0c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -796,7 +796,7 @@
                 mFingerprintCancelSignal.cancel();
             }
             mFingerprintCancelSignal = new CancellationSignal();
-            mFpm.authenticate(null, mFingerprintCancelSignal, mAuthenticationCallback, 0, userId);
+            mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null, userId);
             setFingerprintRunningDetectionRunning(true);
         }
     }
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index fd36b7e..b0d5765 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -22,6 +22,7 @@
 import android.app.IUserSwitchObserver;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -73,13 +74,6 @@
     private ClientMonitor mRemoveClient = null;
     private final AppOpsManager mAppOps;
 
-    // Message types. Used internally to dispatch messages to the correct callback.
-    // Must agree with the list in fingerprint.h
-    private static final int FINGERPRINT_ERROR = -1;
-    private static final int FINGERPRINT_ACQUIRED = 1;
-    private static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
-    private static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
-    private static final int FINGERPRINT_AUTHENTICATED = 5;
     private static final long MS_PER_SEC = 1000;
     private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
     private static final int MAX_FAILED_ATTEMPTS = 5;
@@ -207,7 +201,6 @@
         if (mEnrollClient != null) {
             if (mEnrollClient.sendEnrollResult(fingerId, groupId, remaining)) {
                 if (remaining == 0) {
-                    ContentResolver res = mContext.getContentResolver();
                     addTemplateForUser(mEnrollClient, fingerId);
                     removeClient(mEnrollClient);
                 }
@@ -262,14 +255,14 @@
     }
 
     void startEnrollment(IBinder token, byte[] cryptoToken, int groupId,
-            IFingerprintServiceReceiver receiver, int flags) {
+            IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
         IFingerprintDaemon daemon = getFingerprintDaemon();
         if (daemon == null) {
             Slog.w(TAG, "enroll: no fingeprintd!");
             return;
         }
         stopPendingOperations();
-        mEnrollClient = new ClientMonitor(token, receiver, groupId);
+        mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted);
         final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
         try {
             final int result = daemon.enroll(cryptoToken, groupId, timeout);
@@ -328,14 +321,14 @@
     }
 
     void startAuthentication(IBinder token, long opId, int groupId,
-            IFingerprintServiceReceiver receiver, int flags) {
+            IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
         IFingerprintDaemon daemon = getFingerprintDaemon();
         if (daemon == null) {
             Slog.w(TAG, "startAuthentication: no fingeprintd!");
             return;
         }
         stopPendingOperations();
-        mAuthClient = new ClientMonitor(token, receiver, groupId);
+        mAuthClient = new ClientMonitor(token, receiver, groupId, restricted);
         if (inLockoutMode()) {
             Slog.v(TAG, "In lockout mode; disallowing authentication");
             if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
@@ -344,7 +337,6 @@
             mAuthClient = null;
             return;
         }
-        final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
         try {
             final int result = daemon.authenticate(opId, groupId);
             if (result != 0) {
@@ -378,13 +370,14 @@
     }
 
     void startRemove(IBinder token, int fingerId, int userId,
-            IFingerprintServiceReceiver receiver) {
+            IFingerprintServiceReceiver receiver, boolean restricted) {
         IFingerprintDaemon daemon = getFingerprintDaemon();
         if (daemon == null) {
             Slog.w(TAG, "startRemove: no fingeprintd!");
             return;
         }
-        mRemoveClient = new ClientMonitor(token, receiver, userId);
+
+        mRemoveClient = new ClientMonitor(token, receiver, userId, restricted);
         // The fingerprint template ids will be removed when we get confirmation from the HAL
         try {
             final int result = daemon.remove(fingerId, userId);
@@ -404,6 +397,11 @@
         return mFingerprintUtils.getFingerprintsForUser(mContext, groupId).size() > 0;
     }
 
+    boolean hasPermission(String permission) {
+        return getContext().checkCallingOrSelfPermission(permission)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     void checkPermission(String permission) {
         getContext().enforceCallingOrSelfPermission(permission,
                 "Must have " + permission + " permission.");
@@ -420,11 +418,14 @@
         IBinder token;
         IFingerprintServiceReceiver receiver;
         int userId;
+        boolean restricted; // True if client does not have MANAGE_FINGERPRINT permission
 
-        public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
+        public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId,
+                boolean restricted) {
             this.token = token;
             this.receiver = receiver;
             this.userId = userId;
+            this.restricted = restricted;
             try {
                 token.linkToDeath(this, 0);
             } catch (RemoteException e) {
@@ -498,7 +499,13 @@
             boolean result = false;
             if (receiver != null) {
                 try {
-                    receiver.onAuthenticated(mHalDeviceId, fpId, groupId);
+                    if (fpId == 0) {
+                        receiver.onAuthenticationFailed(mHalDeviceId);
+                    } else {
+                        Fingerprint fp = !restricted ?
+                                new Fingerprint("" /* TODO */, groupId, fpId, mHalDeviceId) : null;
+                        receiver.onAuthenticationSucceeded(mHalDeviceId, fp);
+                    }
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed to notify Authenticated:", e);
                     result = true; // client failed
@@ -592,14 +599,22 @@
                 final IFingerprintServiceReceiver receiver, final int flags) {
             checkPermission(MANAGE_FINGERPRINT);
             final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length);
+
+            final boolean restricted = isRestricted();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    startEnrollment(token, cryptoClone, groupId, receiver, flags);
+                    startEnrollment(token, cryptoClone, groupId, receiver, flags, restricted);
                 }
             });
         }
 
+        private boolean isRestricted() {
+            // Only give privileged apps (like Settings) access to fingerprint info
+            final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
+            return restricted;
+        }
+
         @Override // Binder call
         public void cancelEnrollment(final IBinder token) {
             checkPermission(MANAGE_FINGERPRINT);
@@ -614,14 +629,15 @@
         @Override // Binder call
         public void authenticate(final IBinder token, final long opId, final int groupId,
                 final IFingerprintServiceReceiver receiver, final int flags, String opPackageName) {
-            checkPermission(USE_FINGERPRINT);
+
             if (!canUseFingerprint(opPackageName)) {
                 return;
             }
+            final boolean restricted = isRestricted();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    startAuthentication(token, opId, groupId, receiver, flags);
+                    startAuthentication(token, opId, groupId, receiver, flags, restricted);
                 }
             });
         }
@@ -643,10 +659,11 @@
         public void remove(final IBinder token, final int fingerId, final int groupId,
                 final IFingerprintServiceReceiver receiver) {
             checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
+            final boolean restricted = isRestricted();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    startRemove(token, fingerId, groupId, receiver);
+                    startRemove(token, fingerId, groupId, receiver, restricted);
                 }
             });
 
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
index 33177b4..902d970 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
@@ -57,7 +57,7 @@
     private final File mFile;
 
     @GuardedBy("this")
-    private final ArrayList<Fingerprint> mFingerprints = new ArrayList<>();
+    private final ArrayList<Fingerprint> mFingerprints = new ArrayList<Fingerprint>();
     private final Context mCtx;
 
     public FingerprintsUserState(Context ctx, int userId) {
@@ -127,7 +127,7 @@
     }
 
     private ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) {
-        ArrayList<Fingerprint> result = new ArrayList<>(array.size());
+        ArrayList<Fingerprint> result = new ArrayList<Fingerprint>(array.size());
         for (int i = 0; i < array.size(); i++) {
             Fingerprint fp = array.get(i);
             result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getFingerId(),