From 08fa40c5cb5229b7969b2a5146855a337870f45a Mon Sep 17 00:00:00 2001 From: Jim Miller Date: Tue, 29 Apr 2014 18:18:47 -0700 Subject: [PATCH] First pass at adding FingerprintManagerService This adds a new service for monitoring and enrolling fingerprints to the platform. Fixed documentation links. Change-Id: I66013be5e09be9c5f9746c46aacf32d3e26c3b73 --- Android.mk | 2 + api/current.txt | 31 +++ core/java/android/app/ContextImpl.java | 8 + core/java/android/content/Context.java | 10 + core/java/android/provider/Settings.java | 6 + .../fingerprint/FingerprintManager.java | 200 ++++++++++++++++ .../FingerprintManagerReceiver.java | 59 +++++ .../fingerprint/FingerprintService.java | 219 ++++++++++++++++++ .../service/fingerprint/FingerprintUtils.java | 85 +++++++ .../fingerprint/IFingerprintService.aidl | 38 +++ .../IFingerprintServiceReceiver.aidl | 30 +++ core/jni/Android.mk | 1 + .../jni/android_server_FingerprintManager.cpp | 73 ++++++ 13 files changed, 762 insertions(+) create mode 100644 core/java/android/service/fingerprint/FingerprintManager.java create mode 100644 core/java/android/service/fingerprint/FingerprintManagerReceiver.java create mode 100644 core/java/android/service/fingerprint/FingerprintService.java create mode 100644 core/java/android/service/fingerprint/FingerprintUtils.java create mode 100644 core/java/android/service/fingerprint/IFingerprintService.aidl create mode 100644 core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl create mode 100644 core/jni/android_server_FingerprintManager.cpp diff --git a/Android.mk b/Android.mk index 8e283d74aa518..c80a9ab077342 100644 --- a/Android.mk +++ b/Android.mk @@ -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 \ diff --git a/api/current.txt b/api/current.txt index 071db5afa4529..53e5477b504e8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index fe532bf1c6aca..c62169665f0b5 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -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()); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index de223a3a1c86e..7c625bd725dee 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2375,6 +2375,16 @@ public abstract class Context { */ public static final String AUDIO_SERVICE = "audio"; + /** + * 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 diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0eb994d884c78..d5a3bcb77893d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3479,6 +3479,12 @@ public final class Settings { public static final String LOCK_SCREEN_APPWIDGET_IDS = "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 0000000000000..0d14c5996aa60 --- /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 0000000000000..34f16558acca7 --- /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 0000000000000..c7fa7cdadd08d --- /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 mClients = + new HashMap(); + + 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 0000000000000..81a2aac5867b3 --- /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 0000000000000..e92c20cd03397 --- /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 0000000000000..4826b59b650cc --- /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 667bf6cbfac44..8bd2e4f9e9a3e 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -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 index 0000000000000..f8a1fd9a4893e --- /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 +#include +#include + +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