OSDN Git Service

First pass at adding FingerprintManagerService
authorJim Miller <jaggies@google.com>
Wed, 30 Apr 2014 01:18:47 +0000 (18:18 -0700)
committerJim Miller <jaggies@google.com>
Wed, 7 May 2014 02:27:21 +0000 (02:27 +0000)
This adds a new service for monitoring and enrolling fingerprints
to the platform.

Fixed documentation links.

Change-Id: I66013be5e09be9c5f9746c46aacf32d3e26c3b73

13 files changed:
Android.mk
api/current.txt
core/java/android/app/ContextImpl.java
core/java/android/content/Context.java
core/java/android/provider/Settings.java
core/java/android/service/fingerprint/FingerprintManager.java [new file with mode: 0644]
core/java/android/service/fingerprint/FingerprintManagerReceiver.java [new file with mode: 0644]
core/java/android/service/fingerprint/FingerprintService.java [new file with mode: 0644]
core/java/android/service/fingerprint/FingerprintUtils.java [new file with mode: 0644]
core/java/android/service/fingerprint/IFingerprintService.aidl [new file with mode: 0644]
core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl [new file with mode: 0644]
core/jni/Android.mk
core/jni/android_server_FingerprintManager.cpp [new file with mode: 0644]

index 8e283d7..c80a9ab 100644 (file)
@@ -199,6 +199,8 @@ LOCAL_SRC_FILES += \
        core/java/android/service/dreams/IDozeHardware.aidl \
        core/java/android/service/dreams/IDreamManager.aidl \
        core/java/android/service/dreams/IDreamService.aidl \
+       core/java/android/service/fingerprint/IFingerprintService.aidl \
+       core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl \
        core/java/android/service/trust/ITrustAgentService.aidl \
        core/java/android/service/trust/ITrustAgentServiceCallback.aidl \
        core/java/android/service/voice/IVoiceInteractionService.aidl \
index 071db5a..53e5477 100644 (file)
@@ -6614,6 +6614,7 @@ package android.content {
     field public static final java.lang.String DISPLAY_SERVICE = "display";
     field public static final java.lang.String DOWNLOAD_SERVICE = "download";
     field public static final java.lang.String DROPBOX_SERVICE = "dropbox";
+    field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint";
     field public static final java.lang.String HDMI_CEC_SERVICE = "hdmi_cec";
     field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
     field public static final java.lang.String INPUT_SERVICE = "input";
@@ -25059,6 +25060,36 @@ package android.service.dreams {
 
 }
 
+package android.service.fingerprint {
+
+  public class FingerprintManager {
+    ctor public FingerprintManager(android.content.Context);
+    method public void enroll(long);
+    method public void remove(int);
+    method public void startListening(android.service.fingerprint.FingerprintManagerReceiver);
+    method public void stopListening();
+    field protected static final boolean DEBUG = true;
+    field public static final int FINGERPRINT_ERROR = -1; // 0xffffffff
+    field public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; // 0x2
+    field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
+    field public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; // 0xfffffff6
+    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_SCANNED = 1; // 0x1
+    field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; // 0x2
+    field public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; // 0x4
+  }
+
+  public class FingerprintManagerReceiver {
+    ctor public FingerprintManagerReceiver();
+    method public void onEnrollResult(int, int);
+    method public void onError(int);
+    method public void onRemoved(int);
+    method public void onScanned(int, int);
+  }
+
+}
+
 package android.service.notification {
 
   public abstract class NotificationListenerService extends android.app.Service {
index fe532bf..c621696 100644 (file)
@@ -106,6 +106,9 @@ import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
 import android.print.IPrintManager;
 import android.print.PrintManager;
+import android.service.fingerprint.FingerprintManager;
+import android.service.fingerprint.FingerprintManagerReceiver;
+import android.service.fingerprint.FingerprintService;
 import android.telephony.TelephonyManager;
 import android.tv.ITvInputManager;
 import android.tv.TvInputManager;
@@ -451,6 +454,11 @@ class ContextImpl extends Context {
                     return new KeyguardManager();
                 }});
 
+        registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                return new FingerprintManager(ctx);
+            }});
+
         registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
                     return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
index de223a3..7c625bd 100644 (file)
@@ -2377,6 +2377,16 @@ public abstract class Context {
 
     /**
      * Use with {@link #getSystemService} to retrieve a
+     * {@link android.service.fingerprint.FingerprintManager} for handling management
+     * of fingerprints.
+     *
+     * @see #getSystemService
+     * @see android.app.FingerprintManager
+     */
+    public static final String FINGERPRINT_SERVICE = "fingerprint";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.media.MediaRouter} for controlling and managing
      * routing of media.
      *
index 0eb994d..d5a3bcb 100644 (file)
@@ -3480,6 +3480,12 @@ public final class Settings {
             "lock_screen_appwidget_ids";
 
         /**
+         * List of enrolled fingerprint identifiers (comma-delimited).
+         * @hide
+         */
+        public static final String USER_FINGERPRINT_IDS = "user_fingerprint_ids";
+
+        /**
          * Id of the appwidget shown on the lock screen when appwidgets are disabled.
          * @hide
          */
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
new file mode 100644 (file)
index 0000000..0d14c59
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+ * Copyright (C) 2014 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.service.fingerprint;
+
+import android.app.ActivityManagerNative;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * A class that coordinates access to the fingerprint hardware.
+ */
+
+public class FingerprintManager {
+    private static final String TAG = "FingerprintManager";
+    protected static final boolean DEBUG = true;
+    private static final String FINGERPRINT_SERVICE_PACKAGE = "com.android.service.fingerprint";
+    private static final String FINGERPRINT_SERVICE_CLASS =
+            "com.android.service.fingerprint.FingerprintService";
+    private static final int MSG_ENROLL_RESULT = 100;
+    private static final int MSG_SCANNED = 101;
+    private static final int MSG_ERROR = 102;
+    private static final int MSG_REMOVED = 103;
+
+    public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10;
+    public static final int FINGERPRINT_ERROR = -1; // One of the error messages below.
+
+    // Progress messages.
+    public static final int FINGERPRINT_SCANNED = 1;
+    public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2;
+    public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
+
+    // Error messages. Must agree with fingerprint HAL definitions.
+    public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+    public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2;
+    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+    public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+
+    private IFingerprintService mService;
+    private FingerprintManagerReceiver mClientReceiver;
+
+    private Handler mHandler = new Handler() {
+        public void handleMessage(android.os.Message msg) {
+            if (mClientReceiver != null) {
+                switch(msg.what) {
+                    case MSG_ENROLL_RESULT:
+                        mClientReceiver.onEnrollResult(msg.arg1, msg.arg2);
+                        break;
+                    case MSG_SCANNED:
+                        mClientReceiver.onScanned(msg.arg1, msg.arg2);
+                        break;
+                    case MSG_ERROR:
+                        mClientReceiver.onError(msg.arg1);
+                        break;
+                    case MSG_REMOVED:
+                        mClientReceiver.onRemoved(msg.arg1);
+                }
+            }
+        }
+    };
+
+    public FingerprintManager(Context context) {
+        // Connect to service...
+        Intent intent = new Intent();
+        intent.setClassName(FINGERPRINT_SERVICE_PACKAGE, FINGERPRINT_SERVICE_CLASS);
+        if (!context.bindServiceAsUser(intent, mFingerprintConnection,
+                Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF)) {
+            if (DEBUG) Log.v(TAG, "Can't bind to " + FINGERPRINT_SERVICE_CLASS);
+        }
+    }
+
+    private final ServiceConnection mFingerprintConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG) Log.v(TAG, "Connected to FingerprintService");
+            mService = IFingerprintService.Stub.asInterface(service);
+            try {
+                mService.startListening(mServiceReceiver, getCurrentUserId());
+            } catch (RemoteException e) {
+                if (DEBUG) Log.v(TAG, "Failed to set callback", e);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) Log.v(TAG, "Disconnected from FingerprintService");
+            mService = null;
+        }
+    };
+
+    private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
+
+        public void onEnrollResult(int fingerprintId,  int remaining) {
+            mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget();
+        }
+
+        public void onScanned(int fingerprintId, int confidence) {
+            mHandler.obtainMessage(MSG_SCANNED, fingerprintId, confidence)
+                    .sendToTarget();;
+        }
+
+        public void onError(int error) {
+            mHandler.obtainMessage(MSG_ERROR, error, 0).sendToTarget();
+        }
+
+        public void onRemoved(int fingerprintId) {
+            mHandler.obtainMessage(MSG_REMOVED, fingerprintId, 0).sendToTarget();
+        }
+    };
+
+    /**
+     * Start the enrollment process.  Timeout dictates how long to wait for the user to
+     * enroll a fingerprint.
+     *
+     * @param timeout
+     */
+    public void enroll(long timeout) {
+        if (mServiceReceiver == null) {
+            throw new IllegalStateException("enroll: Call registerCallback() first");
+        }
+        if (mService != null) try {
+            mService.enroll(timeout, getCurrentUserId());
+        } catch (RemoteException e) {
+            Log.v(TAG, "Remote exception while enrolling: ", e);
+        }
+    }
+
+    /**
+     * Remove the given fingerprintId from the system.  FingerprintId of 0 has special meaning
+     * which is to delete all fingerprint data for the current user. Use with caution.
+     * @param fingerprintId
+     */
+    public void remove(int fingerprintId) {
+        if (mService != null) try {
+            mService.remove(fingerprintId, getCurrentUserId());
+        } catch (RemoteException e) {
+            Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
+        }
+    }
+
+    /**
+     * Starts listening for fingerprint events.  When a finger is scanned or recognized, the
+     * client will be notified via the callback.
+     */
+    public void startListening(FingerprintManagerReceiver receiver) {
+        mClientReceiver = receiver;
+        if (mService != null) {
+            try {
+                mService.startListening(mServiceReceiver, getCurrentUserId());
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in startListening(): ", e);
+            }
+        }
+    }
+
+    private int getCurrentUserId() {
+        try {
+            return ActivityManagerNative.getDefault().getCurrentUser().id;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to get current user id\n");
+            return UserHandle.USER_NULL;
+        }
+    }
+
+    /**
+     * Stops the client from listening to fingerprint events.
+     */
+    public void stopListening() {
+        mClientReceiver = null;
+        if (mService != null) {
+            try {
+                mService.stopListening(getCurrentUserId());
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in stopListening(): ", e);
+            }
+        } else {
+            Log.w(TAG, "stopListening(): Service not connected!");
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
new file mode 100644 (file)
index 0000000..34f1655
--- /dev/null
@@ -0,0 +1,59 @@
+package android.service.fingerprint;
+/**
+ * Copyright (C) 2014 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.
+ */
+
+public class FingerprintManagerReceiver {
+    /**
+     * Fingerprint enrollment progress update. Enrollment is considered complete if
+     * remaining hits 0 without {@link #onError(int)} being called.
+     *
+     * @param fingerprintId the fingerprint we're currently enrolling
+     * @param remaining the number of samples required to complete enrollment. It's up to
+     * the hardware to define what each step in enrollment means. Some hardware
+     * requires multiple samples of the same part of the finger.  Others require sampling of
+     * different parts of the finger.  The enrollment flow can use remaining to
+     * mean "step x" of the process or "just need another sample."
+     */
+    public void onEnrollResult(int fingerprintId,  int remaining) { }
+
+    /**
+     * Fingerprint scan detected. Most clients will use this function to detect a fingerprint
+     *
+     * @param fingerprintId is the finger the hardware has detected.
+     * @param confidence from 0 (no confidence) to 65535 (high confidence). Fingerprint 0 has
+     * special meaning - the finger wasn't recognized.
+     */
+    public void onScanned(int fingerprintId, int confidence) { }
+
+    /**
+     * An error was detected during scan or enrollment.  One of
+     * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE},
+     * {@link FingerprintManager#FINGERPRINT_ERROR_BAD_CAPTURE} or
+     * {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT}
+     * {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE}
+     *
+     * @param error one of the above error codes
+     */
+    public void onError(int error) { }
+
+    /**
+     * The given fingerprint template was successfully removed by the driver.
+     * See {@link FingerprintManager#remove(int)}
+     *
+     * @param fingerprintId id of template to remove.
+     */
+    public void onRemoved(int fingerprintId) { }
+}
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintService.java b/core/java/android/service/fingerprint/FingerprintService.java
new file mode 100644 (file)
index 0000000..c7fa7cd
--- /dev/null
@@ -0,0 +1,219 @@
+/**
+ * Copyright (C) 2014 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.service.fingerprint;
+
+import android.app.Service;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+/**
+ * A service to manage multiple clients that want to access the fingerprint HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * fingerprint -related events.
+ *
+ * @hide
+ */
+public class FingerprintService extends Service {
+    private final String TAG = FingerprintService.class.getSimpleName() +
+            "[" + getClass().getSimpleName() + "]";
+    private static final boolean DEBUG = true;
+    HashMap<IFingerprintServiceReceiver, ClientData> mClients =
+            new HashMap<IFingerprintServiceReceiver, ClientData>();
+
+    private static final int MSG_NOTIFY = 10;
+
+    Handler mHandler = new Handler() {
+        public void handleMessage(android.os.Message msg) {
+            switch (msg.what) {
+                case MSG_NOTIFY:
+                    handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj);
+                    break;
+
+                default:
+                    Slog.w(TAG, "Unknown message:" + msg.what);
+            }
+        }
+    };
+
+    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_DELETING = 3;
+    private static final long MS_PER_SEC = 1000;
+
+    private static final class ClientData {
+        public IFingerprintServiceReceiver receiver;
+        int state;
+        int userId;
+    }
+
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
+        return new FingerprintServiceWrapper();
+    }
+
+    // JNI methods to communicate from FingerprintManagerService to HAL
+    native int nativeEnroll(int timeout);
+    native int nativeRemove(int fingerprintId);
+
+    // JNI methods for communicating from HAL to clients
+    void notify(int msg, int arg1, int arg2) {
+        mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget();
+    }
+
+    void handleNotify(int msg, int arg1, int arg2) {
+        for (int i = 0; i < mClients.size(); i++) {
+            ClientData clientData = mClients.get(i);
+            switch (msg) {
+                case FingerprintManager.FINGERPRINT_ERROR: {
+                    if (clientData.state != STATE_IDLE) {
+                        // FINGERPRINT_ERROR_HW_UNAVAILABLE
+                        // FINGERPRINT_ERROR_BAD_CAPTURE
+                        // FINGERPRINT_ERROR_TIMEOUT
+                        // FINGERPRINT_ERROR_NO_SPACE
+                        final int error = arg1;
+                        clientData.state = STATE_IDLE;
+                        if (clientData.receiver != null) {
+                            try {
+                                clientData.receiver.onError(error);
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "can't send message to client. Did it die?", e);
+                            }
+                        }
+                    }
+                }
+                break;
+                case FingerprintManager.FINGERPRINT_SCANNED: {
+                    final int fingerId = arg1;
+                    final int confidence = arg2;
+                    if (clientData.state == STATE_LISTENING && clientData.receiver != null) {
+                        try {
+                            clientData.receiver.onScanned(fingerId, confidence);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "can't send message to client. Did it die?", e);
+                        }
+                    }
+                    break;
+                }
+                case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
+                    if (clientData.state == STATE_ENROLLING) {
+                        final int fingerId = arg1;
+                        final int remaining = arg2;
+                        if (remaining == 0) {
+                            FingerprintUtils.addFingerprintIdForUser(fingerId,
+                                    getContentResolver(), clientData.userId);
+                            clientData.state = STATE_IDLE; // Nothing left to do
+                        }
+                        if (clientData.receiver != null) {
+                            try {
+                                clientData.receiver.onEnrollResult(fingerId, remaining);
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "can't send message to client. Did it die?", e);
+                            }
+                        }
+                    }
+                    break;
+                }
+                case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
+                    int fingerId = arg1;
+                    if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
+                    if (clientData.state == STATE_DELETING) {
+                        FingerprintUtils.removeFingerprintIdForUser(fingerId, getContentResolver(),
+                                clientData.userId);
+                        if (clientData.receiver != null) {
+                            try {
+                                clientData.receiver.onRemoved(fingerId);
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "can't send message to client. Did it die?", e);
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    int enroll(IFingerprintServiceReceiver receiver, long timeout, int userId) {
+        ClientData clientData = mClients.get(receiver);
+        if (clientData != null) {
+            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+            clientData.state = STATE_ENROLLING;
+            return nativeEnroll((int) (timeout / MS_PER_SEC));
+        }
+        return -1;
+    }
+
+    int remove(IFingerprintServiceReceiver receiver, int fingerId, int userId) {
+        ClientData clientData = mClients.get(receiver);
+        if (clientData != null) {
+            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+            clientData.state = STATE_DELETING;
+            // The fingerprint id will be removed when we get confirmation from the HAL
+            return nativeRemove(fingerId);
+        }
+        return -1;
+    }
+
+    void startListening(IFingerprintServiceReceiver receiver, int userId) {
+        ClientData clientData = new ClientData();
+        clientData.state = STATE_LISTENING;
+        clientData.receiver = receiver;
+        clientData.userId = userId;
+        mClients.put(receiver, clientData);
+    }
+
+    void stopListening(IFingerprintServiceReceiver receiver, int userId) {
+        ClientData clientData = mClients.get(receiver);
+        if (clientData != null) {
+            clientData.state = STATE_IDLE;
+            clientData.userId = -1;
+            clientData.receiver = null;
+        }
+        mClients.remove(receiver);
+    }
+
+    private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+        IFingerprintServiceReceiver mReceiver;
+        public int enroll(long timeout, int userId) {
+            return mReceiver != null ? FingerprintService.this.enroll(mReceiver, timeout, userId)
+                    : FingerprintManager.FINGERPRINT_ERROR_NO_RECEIVER;
+        }
+
+        public int remove(int fingerprintId, int userId) {
+            return FingerprintService.this.remove(mReceiver, fingerprintId, userId);
+        }
+
+        public void startListening(IFingerprintServiceReceiver receiver, int userId) {
+            mReceiver = receiver;
+            FingerprintService.this.startListening(receiver, userId);
+        }
+
+        public void stopListening(int userId) {
+            FingerprintService.this.stopListening(mReceiver, userId);
+        }
+    }
+}
diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java
new file mode 100644 (file)
index 0000000..81a2aac
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2014 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.service.fingerprint;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.Arrays;
+
+class FingerprintUtils {
+    private static final boolean DEBUG = true;
+    private static final String TAG = "FingerprintUtils";
+
+    public static int[] getFingerprintIdsForUser(ContentResolver res, int userId) {
+        String fingerIdsRaw = Settings.Secure.getStringForUser(res,
+                Settings.Secure.USER_FINGERPRINT_IDS, userId);
+
+        String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", ");
+        int result[] = new int[fingerStringIds.length];
+        for (int i = 0; i < result.length; i++) {
+            try {
+                result[i] = Integer.decode(fingerStringIds[i]);
+            } catch (NumberFormatException e) {
+                if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]);
+            }
+        }
+        return result;
+    }
+
+    public static void addFingerprintIdForUser(int fingerId, ContentResolver res, int userId) {
+        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;
+        }
+        int[] newList = Arrays.copyOf(fingerIds, fingerIds.length + 1);
+        newList[fingerIds.length] = fingerId;
+        Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS,
+                Arrays.toString(newList), userId);
+    }
+
+    public static boolean removeFingerprintIdForUser(int fingerId, ContentResolver res, int userId)
+    {
+        // 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");
+
+        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) {
+            Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS,
+                    Arrays.toString(Arrays.copyOf(resultIds, resultCount)), userId);
+            return true;
+        }
+        return false;
+    }
+
+};
+
diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl
new file mode 100644 (file)
index 0000000..e92c20c
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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.service.fingerprint;
+
+import android.os.Bundle;
+import android.service.fingerprint.IFingerprintServiceReceiver;
+
+/**
+ * Communication channel from client to the fingerprint service.
+ * @hide
+ */
+interface IFingerprintService {
+    // Returns 0 if successfully started, -1 otherwise
+    int enroll(long timeout, int userId);
+
+    // Returns 0 if fingerprintId's template can be removed, -1 otherwise
+    int remove(int fingerprintId, int userId);
+
+    // Start listening for fingerprint events.  This has the side effect of starting
+    // the hardware if not already started.
+    oneway void startListening(IFingerprintServiceReceiver receiver, int userId);
+
+    // Stops listening for fingerprints
+    oneway void stopListening(int userId);
+}
diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
new file mode 100644 (file)
index 0000000..4826b59
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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.service.fingerprint;
+
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Communication channel from the FingerprintService back to FingerprintManager.
+ * @hide
+ */
+oneway interface IFingerprintServiceReceiver {
+    void onEnrollResult(int fingerprintId,  int remaining);
+    void onScanned(int fingerprintId, int confidence);
+    void onError(int error);
+    void onRemoved(int fingerprintId);
+}
index 667bf6c..8bd2e4f 100644 (file)
@@ -141,6 +141,7 @@ LOCAL_SRC_FILES:= \
        android_util_FileObserver.cpp \
        android/opengl/poly_clip.cpp.arm \
        android/opengl/util.cpp.arm \
+       android_server_FingerprintManager.cpp \
        android_server_NetworkManagementSocketTagger.cpp \
        android_server_Watchdog.cpp \
        android_ddm_DdmHandleNativeHeap.cpp \
diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp
new file mode 100644 (file)
index 0000000..f8a1fd9
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define LOG_TAG "Fingerprint-JNI"
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <utils/Log.h>
+
+namespace android {
+
+static struct {
+    jclass clazz;
+    jmethodID notify;
+} gFingerprintManagerClassInfo;
+
+static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
+    return -1; // TODO
+}
+
+static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) {
+    return -1; // TODO
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod g_methods[] = {
+    { "nativeEnroll", "(I)I", (void*)nativeEnroll },
+    { "nativeRemove", "(I)I", (void*)nativeRemove },
+};
+
+#define FIND_CLASS(var, className) \
+        var = env->FindClass(className); \
+        LOG_FATAL_IF(! var, "Unable to find class " className); \
+        var = jclass(env->NewGlobalRef(var));
+
+#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+        var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find static method" methodName);
+
+#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find method" methodName);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_server_FingerprintManager(JNIEnv* env) {
+    FIND_CLASS(gFingerprintManagerClassInfo.clazz,
+            "android/service/fingerprint/FingerprintManager");
+    GET_METHOD_ID(gFingerprintManagerClassInfo.notify, gFingerprintManagerClassInfo.clazz,
+            "notify", "(III)V");
+    return AndroidRuntime::registerNativeMethods(
+        env, "com/android/service/fingerprint/FingerprintManager", g_methods, NELEM(g_methods));
+}
+
+} // namespace android