First pass at adding FingerprintManagerService
This adds a new service for monitoring and enrolling fingerprints
to the platform.
Fixed documentation links.
Change-Id: I66013be5e09be9c5f9746c46aacf32d3e26c3b73
diff --git a/Android.mk b/Android.mk
index 8e283d7..c80a9ab 100644
--- a/Android.mk
+++ b/Android.mk
@@ -199,6 +199,8 @@
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 \
diff --git a/api/current.txt b/api/current.txt
index 071db5a..53e5477 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6614,6 +6614,7 @@
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.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 {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fe532bf..c621696 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -106,6 +106,9 @@
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 @@
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());
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index de223a3..7c625bd 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2377,6 +2377,16 @@
/**
* 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.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0eb994d..d5a3bcb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3480,6 +3480,12 @@
"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
index 0000000..0d14c59
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -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
index 0000000..34f1655
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
@@ -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
index 0000000..c7fa7cd
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintService.java
@@ -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
index 0000000..81a2aac
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintUtils.java
@@ -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
index 0000000..e92c20c
--- /dev/null
+++ b/core/java/android/service/fingerprint/IFingerprintService.aidl
@@ -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
index 0000000..4826b59
--- /dev/null
+++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
@@ -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);
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 667bf6c..8bd2e4f 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -141,6 +141,7 @@
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
index 0000000..f8a1fd9
--- /dev/null
+++ b/core/jni/android_server_FingerprintManager.cpp
@@ -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