Add fingerprint settings support to the framework

- cleanup thread issue and simplify native FingerprintService methods
- add new permissions and enforce them
- add fingerprint hardware detection API

Change-Id: I87c2243ea2412061f1e85b044138480d0161bcdf
diff --git a/api/current.txt b/api/current.txt
index 285d75d..5386b04 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -141,6 +141,7 @@
     field public static final java.lang.String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
     field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
     field public static final java.lang.String USE_CREDENTIALS = "android.permission.USE_CREDENTIALS";
+    field public static final java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
     field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
     field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
     field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK";
diff --git a/api/system-current.txt b/api/system-current.txt
index 0393896..ab53f0b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -210,6 +210,7 @@
     field public static final java.lang.String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
     field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
     field public static final java.lang.String USE_CREDENTIALS = "android.permission.USE_CREDENTIALS";
+    field public static final java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
     field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
     field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
     field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK";
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
index 178cc8b..494c238 100644
--- a/core/java/android/service/fingerprint/FingerprintManager.java
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -31,6 +32,9 @@
 import android.util.Log;
 import android.util.Slog;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A class that coordinates access to the fingerprint hardware.
  * @hide
@@ -97,6 +101,15 @@
         }
     };
 
+    public static final class FingerprintItem {
+        CharSequence name;
+        int id;
+        FingerprintItem(CharSequence name, int id) {
+            this.name = name;
+            this.id = id;
+        }
+    }
+
     /**
      * @hide
      */
@@ -248,4 +261,38 @@
     private void sendError(int msg, int arg1, int arg2) {
         mHandler.obtainMessage(msg, arg1, arg2);
     }
+
+    /**
+     * @return list of current fingerprint items
+     * @hide
+     */
+    public List<FingerprintItem> getEnrolledFingerprints() {
+        int[] ids = FingerprintUtils.getFingerprintIdsForUser(mContext.getContentResolver(),
+                getCurrentUserId());
+        List<FingerprintItem> result = new ArrayList<FingerprintItem>();
+        for (int i = 0; i < ids.length; i++) {
+            // TODO: persist names in Settings
+            FingerprintItem item = new FingerprintItem("Finger" + ids[i], ids[i]);
+            result.add(item);
+        }
+        return result;
+    }
+
+    /**
+     * Determine if fingerprint hardware is present and functional.
+     * @return true if hardware is present and functional, false otherwise.
+     * @hide
+     */
+    public boolean isHardwareDetected() {
+        if (mService != null) {
+            try {
+                return mService.isHardwareDetected();
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in isFingerprintHardwareDetected(): ", e);
+            }
+        } else {
+            Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
+        }
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java
index a4caf8e..cc17b99 100644
--- a/core/java/android/service/fingerprint/FingerprintUtils.java
+++ b/core/java/android/service/fingerprint/FingerprintUtils.java
@@ -21,7 +21,11 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * Utility class for dealing with fingerprints and fingerprint settings.
@@ -32,34 +36,50 @@
     private static final boolean DEBUG = true;
     private static final String TAG = "FingerprintUtils";
 
+    private static int[] toIntArray(List<Integer> list) {
+        if (list == null) {
+            return null;
+        }
+        int[] arr = new int[list.size()];
+        int i = 0;
+        for (int elem : list) {
+            arr[i] = elem;
+            i++;
+        }
+        return arr;
+    }
+
     public static int[] getFingerprintIdsForUser(ContentResolver res, int userId) {
         String fingerIdsRaw = Settings.Secure.getStringForUser(res,
                 Settings.Secure.USER_FINGERPRINT_IDS, userId);
-
-        int result[] = {};
+        ArrayList<Integer> tmp = new ArrayList<Integer>();
         if (!TextUtils.isEmpty(fingerIdsRaw)) {
             String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", ");
-            result = new int[fingerStringIds.length];
-            for (int i = 0; i < result.length; i++) {
+            int length = fingerStringIds.length;
+            for (int i = 0; i < length; i++) {
                 try {
-                    result[i] = Integer.decode(fingerStringIds[i]);
+                    tmp.add(Integer.decode(fingerStringIds[i]));
                 } catch (NumberFormatException e) {
-                    if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]);
+                    if (DEBUG) Log.w(TAG, "Error parsing finger id: '" + fingerStringIds[i] + "'");
                 }
             }
         }
-        return result;
+        return toIntArray(tmp);
     }
 
     public static void addFingerprintIdForUser(int fingerId, ContentResolver res, int userId) {
+        // FingerId 0 has special meaning.
+        if (fingerId == 0) {
+            Log.w(TAG, "Tried to add fingerId 0");
+            return;
+        }
+
         int[] fingerIds = getFingerprintIdsForUser(res, userId);
 
-        // FingerId 0 has special meaning.
-        if (fingerId == 0) return;
-
         // Don't allow dups
-        for (int i = 0; i < fingerIds.length; i++) {
-            if (fingerIds[i] == fingerId) return;
+        if (ArrayUtils.contains(fingerIds, fingerId)) {
+            Log.w(TAG, "finger already added " + fingerId);
+            return;
         }
         int[] newList = Arrays.copyOf(fingerIds, fingerIds.length + 1);
         newList[fingerIds.length] = fingerId;
@@ -72,19 +92,13 @@
         // FingerId 0 has special meaning. The HAL layer is supposed to remove each finger one
         // at a time and invoke notify() for each fingerId.  If we get called with 0 here, it means
         // something bad has happened.
-        if (fingerId == 0) throw new IllegalStateException("Bad fingerId");
+        if (fingerId == 0) throw new IllegalArgumentException("fingerId can't be 0");
 
-        int[] fingerIds = getFingerprintIdsForUser(res, userId);
-        int[] resultIds = Arrays.copyOf(fingerIds, fingerIds.length);
-        int resultCount = 0;
-        for (int i = 0; i < fingerIds.length; i++) {
-            if (fingerId != fingerIds[i]) {
-                resultIds[resultCount++] = fingerIds[i];
-            }
-        }
-        if (resultCount > 0) {
+        final int[] fingerIds = getFingerprintIdsForUser(res, userId);
+        if (ArrayUtils.contains(fingerIds, fingerId)) {
+            final int[] result = ArrayUtils.removeInt(fingerIds, fingerId);
             Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS,
-                    Arrays.toString(Arrays.copyOf(resultIds, resultCount)), userId);
+                    Arrays.toString(result), userId);
             return true;
         }
         return false;
diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl
index 43d5e9a..a7d4090 100644
--- a/core/java/android/service/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/service/fingerprint/IFingerprintService.aidl
@@ -22,10 +22,10 @@
  * Communication channel from client to the fingerprint service.
  * @hide
  */
-oneway interface IFingerprintService {
+interface IFingerprintService {
     // Any errors resulting from this call will be returned to the listener
     void enroll(IBinder token, long timeout, int userId);
-    
+
     // Any errors resulting from this call will be returned to the listener
     void enrollCancel(IBinder token, int userId);
 
@@ -38,4 +38,7 @@
 
     // Stops listening for fingerprints
     void stopListening(IBinder token, int userId);
+
+    // Determine if HAL is loaded and ready
+    boolean isHardwareDetected();
 }
diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp
index 24f8f67..853425c 100644
--- a/core/jni/android_server_FingerprintManager.cpp
+++ b/core/jni/android_server_FingerprintManager.cpp
@@ -20,10 +20,11 @@
 
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
+#include <android_os_MessageQueue.h>
 #include <hardware/hardware.h>
 #include <hardware/fingerprint.h>
 #include <utils/Log.h>
-
+#include <utils/Looper.h>
 #include "core_jni_helpers.h"
 
 namespace android {
@@ -34,7 +35,6 @@
 static struct {
     jclass clazz;
     jmethodID notify;
-    jobject callbackObject;
 } gFingerprintServiceClassInfo;
 
 static struct {
@@ -42,11 +42,26 @@
     fingerprint_device_t *device;
 } gContext;
 
+static sp<Looper> gLooper;
+static jobject gCallback;
+
+class CallbackHandler : public MessageHandler {
+    int type;
+    int arg1, arg2;
+public:
+    CallbackHandler(int type, int arg1, int arg2) : type(type), arg1(arg1), arg2(arg2) { }
+
+    virtual void handleMessage(const Message& message) {
+        //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2);
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        env->CallVoidMethod(gCallback, gFingerprintServiceClassInfo.notify, type, arg1, arg2);
+    }
+};
+
 // Called by the HAL to notify us of fingerprint events
 static void hal_notify_callback(fingerprint_msg_t msg) {
     uint32_t arg1 = 0;
     uint32_t arg2 = 0;
-    uint32_t arg3 = 0; // TODO
     switch (msg.type) {
         case FINGERPRINT_ERROR:
             arg1 = msg.data.error;
@@ -60,7 +75,6 @@
         case FINGERPRINT_TEMPLATE_ENROLLING:
             arg1 = msg.data.enroll.finger.fid;
             arg2 = msg.data.enroll.samples_remaining;
-            arg3 = 0;
             break;
         case FINGERPRINT_TEMPLATE_REMOVED:
             arg1 = msg.data.removed.finger.fid;
@@ -69,32 +83,16 @@
             ALOGE("fingerprint: invalid msg: %d", msg.type);
             return;
     }
-    (void)arg3;
-    //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2);
-
-	// TODO: fix gross hack to attach JNI to calling thread
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    if (env == NULL) {
-        JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
-        JavaVM* vm = AndroidRuntime::getJavaVM();
-        int result = vm->AttachCurrentThread(&env, (void*) &args);
-        if (result != JNI_OK) {
-            ALOGE("Can't call JNI method: attach failed: %#x", result);
-            return;
-        }
-    }
-    env->CallVoidMethod(gFingerprintServiceClassInfo.callbackObject,
-            gFingerprintServiceClassInfo.notify, msg.type, arg1, arg2);
+    // This call potentially comes in on a thread not owned by us. Hand it off to our
+    // looper so it runs on our thread when calling back to FingerprintService.
+    // CallbackHandler object is reference-counted, so no cleanup necessary.
+    gLooper->sendMessage(new CallbackHandler(msg.type, arg1, arg2), Message());
 }
 
-static void nativeInit(JNIEnv *env, jobject clazz, jobject callbackObj) {
+static void nativeInit(JNIEnv *env, jobject clazz, jobject mQueue, jobject callbackObj) {
     ALOG(LOG_VERBOSE, LOG_TAG, "nativeInit()\n");
-    gFingerprintServiceClassInfo.clazz = FindClassOrDie(env, FINGERPRINT_SERVICE);
-    gFingerprintServiceClassInfo.clazz = MakeGlobalRefOrDie(env,
-                                                            gFingerprintServiceClassInfo.clazz);
-    gFingerprintServiceClassInfo.notify = GetMethodIDOrDie(env, gFingerprintServiceClassInfo.clazz,
-           "notify", "(III)V");
-    gFingerprintServiceClassInfo.callbackObject = MakeGlobalRefOrDie(env, callbackObj);
+    gCallback = MakeGlobalRefOrDie(env, callbackObj);
+    gLooper = android_os_MessageQueue_getMessageQueue(env, mQueue)->getLooper();
 }
 
 static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
@@ -179,14 +177,15 @@
     { "nativeRemove", "(I)I", (void*)nativeRemove },
     { "nativeOpenHal", "()I", (void*)nativeOpenHal },
     { "nativeCloseHal", "()I", (void*)nativeCloseHal },
-    { "nativeInit", "(Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit }
+    { "nativeInit","(Landroid/os/MessageQueue;"
+            "Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit }
 };
 
 int register_android_server_fingerprint_FingerprintService(JNIEnv* env) {
     jclass clazz = FindClassOrDie(env, FINGERPRINT_SERVICE);
     gFingerprintServiceClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
-    gFingerprintServiceClassInfo.notify = GetMethodIDOrDie(env, gFingerprintServiceClassInfo.clazz,
-                                                           "notify", "(III)V");
+    gFingerprintServiceClassInfo.notify =
+            GetMethodIDOrDie(env, gFingerprintServiceClassInfo.clazz,"notify", "(III)V");
     int result = RegisterMethodsOrDie(env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods));
     ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n");
     return result;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4b97138..0d15ae54 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2835,6 +2835,18 @@
         android:label="@string/permlab_access_keyguard_secure_storage"
         android:description="@string/permdesc_access_keyguard_secure_storage" />
 
+    <!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
+    <permission android:name="android.permission.MANAGE_FINGERPRINT"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_manageFingerprint"
+        android:description="@string/permdesc_manageFingerprint" />
+
+    <!-- Allows an app to use fingerprint hardware. -->
+    <permission android:name="android.permission.USE_FINGERPRINT"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_useFingerprint"
+        android:description="@string/permdesc_useFingerprint" />
+
     <!-- Allows an application to control keyguard.  Only allowed for system processes.
         @hide -->
     <permission android:name="android.permission.CONTROL_KEYGUARD"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 199b783..b1f51ef 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2209,6 +2209,15 @@
       re-enables the keylock when the call is finished.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_manageFingerprint">manage fingerprint hardware</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_manageFingerprint">Allows the app to invoke methods to add and delete fingerprint templates for use.</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_useFingerprint">use fingerprint hardware</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_useFingerprint">Allows the app to use fingerprint hardware for authentication</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readSyncSettings">read sync settings</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readSyncSettings">Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account.</string>
diff --git a/packages/Keyguard/AndroidManifest.xml b/packages/Keyguard/AndroidManifest.xml
index 352317d..e19246c 100644
--- a/packages/Keyguard/AndroidManifest.xml
+++ b/packages/Keyguard/AndroidManifest.xml
@@ -40,6 +40,7 @@
     <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
     <uses-permission android:name="android.permission.TRUST_LISTENER" />
+    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
 
     <application android:label="@string/app_name"
         android:process="com.android.systemui"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 035bb0e..e8260bb 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -103,6 +103,7 @@
     <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
     <uses-permission android:name="android.permission.TRUST_LISTENER" />
+    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
 
     <!-- Recents -->
     <uses-permission android:name="android.permission.BIND_APPWIDGET" />
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 2941574..c44e39d 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -20,8 +20,11 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.MessageQueue;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.provider.Settings;
@@ -34,6 +37,8 @@
 import android.service.fingerprint.FingerprintUtils;
 import android.service.fingerprint.IFingerprintService;
 import android.service.fingerprint.IFingerprintServiceReceiver;
+import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.USE_FINGERPRINT;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -68,14 +73,13 @@
         }
     };
     private Context mContext;
+    private int mHalDeviceId;
 
     private static final int STATE_IDLE = 0;
     private static final int STATE_LISTENING = 1;
     private static final int STATE_ENROLLING = 2;
     private static final int STATE_REMOVING = 3;
     private static final long MS_PER_SEC = 1000;
-    public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
-    public static final String ENROLL_FINGERPRINT = "android.permission.ENROLL_FINGERPRINT";
 
     private static final class ClientData {
         public IFingerprintServiceReceiver receiver;
@@ -113,17 +117,17 @@
     public FingerprintService(Context context) {
         super(context);
         mContext = context;
-        nativeInit(this);
+        nativeInit(Looper.getMainLooper().getQueue(), this);
     }
 
     // TODO: Move these into separate process
     // JNI methods to communicate from FingerprintManagerService to HAL
-    native int nativeEnroll(int timeout);
-    native int nativeEnrollCancel();
-    native int nativeRemove(int fingerprintId);
-    native int nativeOpenHal();
-    native int nativeCloseHal();
-    native void nativeInit(FingerprintService service);
+    static native int nativeEnroll(int timeout);
+    static native int nativeEnrollCancel();
+    static native int nativeRemove(int fingerprintId);
+    static native int nativeOpenHal();
+    static native int nativeCloseHal();
+    static native void nativeInit(MessageQueue queue, FingerprintService service);
 
     // JNI methods for communicating from HAL to clients
     void notify(int msg, int arg1, int arg2) {
@@ -131,11 +135,13 @@
     }
 
     void handleNotify(int msg, int arg1, int arg2) {
-        Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")");
+        Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")"
+                + ", " + mClients.size() + " clients");
         for (int i = 0; i < mClients.size(); i++) {
+            if (DEBUG) Slog.v(TAG, "Client[" + i + "] binder token: " + mClients.keyAt(i));
             ClientData clientData = mClients.valueAt(i);
             if (clientData == null || clientData.receiver == null) {
-                if (DEBUG) Slog.v(TAG, "clientData at " + i + " is invalid!!");
+                if (DEBUG) Slog.v(TAG, "clientData is invalid!!");
                 continue;
             }
             switch (msg) {
@@ -282,26 +288,27 @@
         mClients.remove(token);
     }
 
-    void checkPermission(String permisison) {
-        // TODO
+    void checkPermission(String permission) {
+        getContext().enforceCallingOrSelfPermission(permission, "Must have "
+                + permission + " permission.");
     }
 
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
         @Override // Binder call
         public void enroll(IBinder token, long timeout, int userId) {
-            checkPermission(ENROLL_FINGERPRINT);
+            checkPermission(MANAGE_FINGERPRINT);
             startEnroll(token, timeout, userId);
         }
 
         @Override // Binder call
         public void enrollCancel(IBinder token,int userId) {
-            checkPermission(ENROLL_FINGERPRINT);
+            checkPermission(MANAGE_FINGERPRINT);
             startEnrollCancel(token, userId);
         }
 
         @Override // Binder call
         public void remove(IBinder token, int fingerprintId, int userId) {
-            checkPermission(ENROLL_FINGERPRINT); // TODO: Maybe have another permission
+            checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
             startRemove(token, fingerprintId, userId);
         }
 
@@ -317,12 +324,19 @@
             checkPermission(USE_FINGERPRINT);
             removeListener(token, userId);
         }
+
+        @Override // Binder call
+        public boolean isHardwareDetected() {
+            checkPermission(USE_FINGERPRINT);
+            return mHalDeviceId != 0;
+        }
     }
 
     @Override
     public void onStart() {
        publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
-       nativeOpenHal();
+       mHalDeviceId = nativeOpenHal();
+       if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
     }
 
 }