First pass at FingerprintService integration with HAL.
Move FingerprintService to framework services directory
Fix merge conflicts.

Tested: scanning, enrolling, removing.

Change-Id: I58b2b902cb671dc82cdaa54a195ba5f1a154622c
diff --git a/api/current.txt b/api/current.txt
index 3e5a695..760d68b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26011,29 +26011,61 @@
 package android.service.fingerprint {
 
   public class FingerprintManager {
-    ctor public FingerprintManager(android.content.Context);
+    ctor public FingerprintManager(android.content.Context, android.service.fingerprint.IFingerprintService);
     method public void enroll(long);
+    method public void enrollCancel();
     method public boolean enrolledAndEnabled();
     method public void remove(int);
     method public void startListening(android.service.fingerprint.FingerprintManagerReceiver);
     method public void stopListening();
+    field public static final int FINGERPRINT_ACQUIRED = 1; // 0x1
+    field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
+    field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4; // 0x4
+    field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
+    field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
+    field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16; // 0x10
+    field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8; // 0x8
     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_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
+    field public static final int FINGERPRINT_PROCESSED = 2; // 0x2
+    field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3; // 0x3
     field public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; // 0x4
   }
 
   public class FingerprintManagerReceiver {
     ctor public FingerprintManagerReceiver();
+    method public void onAcquired(int);
     method public void onEnrollResult(int, int);
     method public void onError(int);
+    method public void onProcessed(int);
     method public void onRemoved(int);
-    method public void onScanned(int, int);
+  }
+
+  public class FingerprintUtils {
+    ctor public FingerprintUtils();
+    method public static void addFingerprintIdForUser(int, android.content.ContentResolver, int);
+    method public static int[] getFingerprintIdsForUser(android.content.ContentResolver, int);
+    method public static boolean removeFingerprintIdForUser(int, android.content.ContentResolver, int);
+  }
+
+  public abstract interface IFingerprintService implements android.os.IInterface {
+    method public abstract void enroll(android.os.IBinder, long, int) throws android.os.RemoteException;
+    method public abstract void enrollCancel(android.os.IBinder, int) throws android.os.RemoteException;
+    method public abstract void remove(android.os.IBinder, int, int) throws android.os.RemoteException;
+    method public abstract void startListening(android.os.IBinder, android.service.fingerprint.IFingerprintServiceReceiver, int) throws android.os.RemoteException;
+    method public abstract void stopListening(android.os.IBinder, int) throws android.os.RemoteException;
+  }
+
+  public abstract interface IFingerprintServiceReceiver implements android.os.IInterface {
+    method public abstract void onAcquired(int) throws android.os.RemoteException;
+    method public abstract void onEnrollResult(int, int) throws android.os.RemoteException;
+    method public abstract void onError(int) throws android.os.RemoteException;
+    method public abstract void onProcessed(int) throws android.os.RemoteException;
+    method public abstract void onRemoved(int) throws android.os.RemoteException;
   }
 
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ab3bb49..a42bd3b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -115,9 +115,8 @@
 import android.os.storage.StorageManager;
 import android.print.IPrintManager;
 import android.print.PrintManager;
+import android.service.fingerprint.IFingerprintService;
 import android.service.fingerprint.FingerprintManager;
-import android.service.fingerprint.FingerprintManagerReceiver;
-import android.service.fingerprint.FingerprintService;
 import android.telecomm.TelecommManager;
 import android.telephony.TelephonyManager;
 import android.content.ClipboardManager;
@@ -466,11 +465,6 @@
                     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());
@@ -690,6 +684,7 @@
                 return new MediaSessionManager(ctx);
             }
         });
+
         registerService(TRUST_SERVICE, new ServiceFetcher() {
             public Object createService(ContextImpl ctx) {
                 IBinder b = ServiceManager.getService(TRUST_SERVICE);
@@ -697,6 +692,14 @@
             }
         });
 
+        registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(FINGERPRINT_SERVICE);
+                IFingerprintService service = IFingerprintService.Stub.asInterface(b);
+                return new FingerprintManager(ctx.getOuterContext(), service);
+            }
+        });
+
         registerService(TV_INPUT_SERVICE, new ServiceFetcher() {
             public Object createService(ContextImpl ctx) {
                 IBinder iBinder = ServiceManager.getService(TV_INPUT_SERVICE);
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
index 2fcec52..b6137d1 100644
--- a/core/java/android/service/fingerprint/FingerprintManager.java
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -22,12 +22,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.Slog;
 
 /**
  * A class that coordinates access to the fingerprint hardware.
@@ -36,31 +38,40 @@
 public class FingerprintManager {
     private static final String TAG = "FingerprintManager";
     private 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;
+    private static final int MSG_ACQUIRED = 101;
+    private static final int MSG_PROCESSED = 102;
+    private static final int MSG_ERROR = 103;
+    private static final int MSG_REMOVED = 104;
 
+    // Errors generated by layers above HAL
     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;
+    // Message types.  Must agree with HAL (fingerprint.h)
+    public static final int FINGERPRINT_ERROR = -1;
+    public static final int FINGERPRINT_ACQUIRED = 1;
+    public static final int FINGERPRINT_PROCESSED = 2;
+    public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
     public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
 
-    // Error messages. Must agree with fingerprint HAL definitions.
+    // Error messages. Must agree with HAL (fingerprint.h)
     public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
-    public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2;
+    public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
     public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
     public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
 
+    // FINGERPRINT_ACQUIRED messages.  Must agree with HAL (fingerprint.h)
+    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+    public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+    public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+    public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4;
+    public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8;
+    public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16;
+
     private IFingerprintService mService;
     private FingerprintManagerReceiver mClientReceiver;
     private Context mContext;
+    private IBinder mToken = new Binder();
 
     private Handler mHandler = new Handler() {
         public void handleMessage(android.os.Message msg) {
@@ -69,8 +80,11 @@
                     case MSG_ENROLL_RESULT:
                         mClientReceiver.onEnrollResult(msg.arg1, msg.arg2);
                         break;
-                    case MSG_SCANNED:
-                        mClientReceiver.onScanned(msg.arg1, msg.arg2);
+                    case MSG_ACQUIRED:
+                        mClientReceiver.onAcquired(msg.arg1);
+                        break;
+                    case MSG_PROCESSED:
+                        mClientReceiver.onProcessed(msg.arg1);
                         break;
                     case MSG_ERROR:
                         mClientReceiver.onError(msg.arg1);
@@ -82,45 +96,26 @@
         }
     };
 
-    public FingerprintManager(Context context) {
+    public FingerprintManager(Context context, IFingerprintService service) {
         mContext = 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);
+        mService = service;
+        if (mService == null) {
+            Slog.v(TAG, "FingerprintManagerService was null");
         }
     }
 
-    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 onAcquired(int acquireInfo) {
+            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0).sendToTarget();
+        }
+
+        public void onProcessed(int fingerprintId) {
+            mHandler.obtainMessage(MSG_PROCESSED, fingerprintId, 0).sendToTarget();
         }
 
         public void onError(int error) {
@@ -151,12 +146,14 @@
      */
     public void enroll(long timeout) {
         if (mServiceReceiver == null) {
-            throw new IllegalStateException("enroll: Call registerCallback() first");
+            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
+            return;
         }
         if (mService != null) try {
-            mService.enroll(timeout, getCurrentUserId());
+            mService.enroll(mToken, timeout, getCurrentUserId());
         } catch (RemoteException e) {
             Log.v(TAG, "Remote exception while enrolling: ", e);
+            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
         }
     }
 
@@ -166,10 +163,19 @@
      * @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);
+        if (mServiceReceiver == null) {
+            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
+            return;
+        }
+        if (mService != null) {
+            try {
+                mService.remove(mToken, fingerprintId, getCurrentUserId());
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
+            }
+        } else {
+            Log.w(TAG, "remove(): Service not connected!");
+            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
         }
     }
 
@@ -181,10 +187,13 @@
         mClientReceiver = receiver;
         if (mService != null) {
             try {
-                mService.startListening(mServiceReceiver, getCurrentUserId());
+                mService.startListening(mToken, mServiceReceiver, getCurrentUserId());
             } catch (RemoteException e) {
                 Log.v(TAG, "Remote exception in startListening(): ", e);
             }
+        } else {
+            Log.w(TAG, "startListening(): Service not connected!");
+            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
         }
     }
 
@@ -201,15 +210,38 @@
      * Stops the client from listening to fingerprint events.
      */
     public void stopListening() {
-        mClientReceiver = null;
         if (mService != null) {
             try {
-                mService.stopListening(getCurrentUserId());
+                mService.stopListening(mToken, getCurrentUserId());
+                mClientReceiver = null;
             } catch (RemoteException e) {
                 Log.v(TAG, "Remote exception in stopListening(): ", e);
             }
         } else {
             Log.w(TAG, "stopListening(): Service not connected!");
+            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
         }
     }
+
+    public void enrollCancel() {
+        if (mServiceReceiver == null) {
+            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
+            return;
+        }
+        if (mService != null) {
+            try {
+                mService.enrollCancel(mToken, getCurrentUserId());
+                mClientReceiver = null;
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in enrollCancel(): ", e);
+                sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
+            }
+        } else {
+            Log.w(TAG, "enrollCancel(): Service not connected!");
+        }
+    }
+
+    private void sendError(int msg, int arg1, int arg2) {
+        mHandler.obtainMessage(msg, arg1, arg2);
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
index 34f1655..e5193f5 100644
--- a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
+++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
@@ -30,18 +30,32 @@
     public void onEnrollResult(int fingerprintId,  int remaining) { }
 
     /**
-     * Fingerprint scan detected. Most clients will use this function to detect a fingerprint
+     * Fingerprint touch detected, but not processed yet. Clients will use this message to
+     * determine a good or bad scan before the fingerprint is processed.  This is meant for the
+     * client to provide feedback about the scan or alert the user that recognition is to follow.
      *
-     * @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.
+     * @param acquiredInfo one of:
+     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_GOOD},
+     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_PARTIAL},
+     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_INSUFFICIENT},
+     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_IMAGER_DIRTY},
+     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_SLOW},
+     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_FAST}
      */
-    public void onScanned(int fingerprintId, int confidence) { }
+    public void onAcquired(int acquiredInfo) { }
+
+    /**
+     * Fingerprint has been detected and processed.  A non-zero return indicates a valid
+     * fingerprint was detected.
+     *
+     * @param fingerprintId the finger id, or 0 if not recognized.
+     */
+    public void onProcessed(int fingerprintId) { }
 
     /**
      * 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_UNABLE_TO_PROCESS} or
      * {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT}
      * {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE}
      *
diff --git a/core/java/android/service/fingerprint/FingerprintService.java b/core/java/android/service/fingerprint/FingerprintService.java
deleted file mode 100644
index c7fa7cd..0000000
--- a/core/java/android/service/fingerprint/FingerprintService.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/**
- * 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
index 81a2aac..f4b5526 100644
--- a/core/java/android/service/fingerprint/FingerprintUtils.java
+++ b/core/java/android/service/fingerprint/FingerprintUtils.java
@@ -18,10 +18,12 @@
 
 import android.content.ContentResolver;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.util.Arrays;
 
+public
 class FingerprintUtils {
     private static final boolean DEBUG = true;
     private static final String TAG = "FingerprintUtils";
@@ -30,13 +32,16 @@
         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]);
+        int result[] = {};
+        if (!TextUtils.isEmpty(fingerIdsRaw)) {
+            String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", ");
+            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;
diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl
index e92c20c..43d5e9a 100644
--- a/core/java/android/service/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/service/fingerprint/IFingerprintService.aidl
@@ -22,17 +22,20 @@
  * Communication channel from client to the fingerprint service.
  * @hide
  */
-interface IFingerprintService {
-    // Returns 0 if successfully started, -1 otherwise
-    int enroll(long timeout, int userId);
+oneway interface IFingerprintService {
+    // Any errors resulting from this call will be returned to the listener
+    void enroll(IBinder token, long timeout, int userId);
+    
+    // Any errors resulting from this call will be returned to the listener
+    void enrollCancel(IBinder token, int userId);
 
-    // Returns 0 if fingerprintId's template can be removed, -1 otherwise
-    int remove(int fingerprintId, int userId);
+    // Any errors resulting from this call will be returned to the listener
+    void remove(IBinder token, 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);
+    void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId);
 
     // Stops listening for fingerprints
-    oneway void stopListening(int userId);
+    void stopListening(IBinder token, int userId);
 }
diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
index 4826b59..af4128f 100644
--- a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
@@ -24,7 +24,8 @@
  */
 oneway interface IFingerprintServiceReceiver {
     void onEnrollResult(int fingerprintId,  int remaining);
-    void onScanned(int fingerprintId, int confidence);
+    void onAcquired(int acquiredInfo);
+    void onProcessed(int fingerprintId);
     void onError(int error);
     void onRemoved(int fingerprintId);
 }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f2b9bac..a7a1faad 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -159,6 +159,7 @@
 extern int register_android_text_AndroidCharacter(JNIEnv *env);
 extern int register_android_text_AndroidBidi(JNIEnv *env);
 extern int register_android_opengl_classes(JNIEnv *env);
+extern int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
 extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
 extern int register_android_server_Watchdog(JNIEnv* env);
 extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
@@ -1328,6 +1329,7 @@
     REG_JNI(register_android_media_ToneGenerator),
 
     REG_JNI(register_android_opengl_classes),
+	REG_JNI(register_android_server_fingerprint_FingerprintService),
     REG_JNI(register_android_server_NetworkManagementSocketTagger),
     REG_JNI(register_android_server_Watchdog),
     REG_JNI(register_android_ddm_DdmHandleNativeHeap),
diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp
index f8a1fd9..ad36843c 100644
--- a/core/jni/android_server_FingerprintManager.cpp
+++ b/core/jni/android_server_FingerprintManager.cpp
@@ -20,30 +20,10 @@
 
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.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); \
@@ -61,13 +41,174 @@
         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 {
+
+static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(1, 0);
+
+static const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService";
+static struct {
+    jclass clazz;
+    jmethodID notify;
+    jobject callbackObject;
+} gFingerprintServiceClassInfo;
+
+static struct {
+    fingerprint_module_t const* module;
+    fingerprint_device_t *device;
+} gContext;
+
+// TODO: remove after driver update to use new HAL
+fingerprint_msg_type_t hackTilFpDriverUpdate(fingerprint_msg_type_t t) {
+    switch(static_cast<int>(t)) {
+        case 1: return FINGERPRINT_PROCESSED;
+        case 2: return FINGERPRINT_TEMPLATE_ENROLLING;
+        default: return t;
+    }
+}
+
+// Called by the HAL to notify us of fingerprint events
+static void hal_notify_callback(fingerprint_msg_t msg) {
+    uint32_t arg1 = 0;
+    uint32_t arg2 = 0;
+    uint32_t arg3 = 0; // TODO
+    msg.type = hackTilFpDriverUpdate(msg.type);
+    switch (msg.type) {
+        case FINGERPRINT_ERROR:
+            arg1 = msg.data.error;
+            break;
+        case FINGERPRINT_ACQUIRED:
+            arg1 = msg.data.acquired.acquired_info;
+            break;
+        case FINGERPRINT_PROCESSED:
+            arg1 = msg.data.processed.id;
+            break;
+        case FINGERPRINT_TEMPLATE_ENROLLING:
+            arg1 = msg.data.enroll.id;
+            arg2 = msg.data.enroll.samples_remaining;
+            arg3 = msg.data.enroll.data_collected_bmp;
+            break;
+        case FINGERPRINT_TEMPLATE_REMOVED:
+            arg1 = msg.data.removed.id;
+            break;
+        default:
+            ALOGE("fingerprint: invalid msg: %d", msg.type);
+            return;
+    }
+    //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2);
+
+	// TODO: fix gross hack to attach JNI to calling thread
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    if (env == NULL) {
+        JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
+        JavaVM* vm = AndroidRuntime::getJavaVM();
+        int result = vm->AttachCurrentThread(&env, (void*) &args);
+        if (result != JNI_OK) {
+            ALOGE("Can't call JNI method: attach failed: %#x", result);
+            return;
+        }
+    }
+    env->CallVoidMethod(gFingerprintServiceClassInfo.callbackObject,
+            gFingerprintServiceClassInfo.notify, msg.type, arg1, arg2);
+}
+
+static void nativeInit(JNIEnv *env, jobject clazz, jobject callbackObj) {
+    ALOG(LOG_VERBOSE, LOG_TAG, "nativeInit()\n");
+    FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
+    GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz,
+           "notify", "(III)V");
+    gFingerprintServiceClassInfo.callbackObject = env->NewGlobalRef(callbackObj);
+}
+
+static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
+    ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll()\n");
+    int ret = gContext.device->enroll(gContext.device, timeout);
+    return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeEnrollCancel(JNIEnv* env, jobject clazz) {
+    ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnrollCancel()\n");
+    int ret = gContext.device->enroll_cancel(gContext.device);
+    return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) {
+    ALOG(LOG_VERBOSE, LOG_TAG, "nativeRemove(%d)\n", fingerprintId);
+    int ret = gContext.device->remove(gContext.device, fingerprintId);
+    return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeOpenHal(JNIEnv* env, jobject clazz) {
+    ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
+    int err;
+    const hw_module_t *hw_module = NULL;
+    if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
+        ALOGE("Can't open fingerprint HW Module, error: %d", err);
+        return 0;
+    }
+    if (NULL == hw_module) {
+        ALOGE("No valid fingerprint module");
+        return 0;
+    }
+
+    gContext.module = reinterpret_cast<const fingerprint_module_t*>(hw_module);
+
+    if (gContext.module->common.methods->open == NULL) {
+        ALOGE("No valid open method");
+        return 0;
+    }
+
+    hw_device_t *device = NULL;
+
+    if (0 != (err = gContext.module->common.methods->open(hw_module, NULL, &device))) {
+        ALOGE("Can't open fingerprint methods, error: %d", err);
+        return 0;
+    }
+
+    if (kVersion != device->version) {
+        ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
+        // return 0; // FIXME
+    }
+
+    gContext.device = reinterpret_cast<fingerprint_device_t*>(device);
+    err = gContext.device->set_notify(gContext.device, hal_notify_callback);
+    if (err < 0) {
+        ALOGE("Failed in call to set_notify(), err=%d", err);
+        return 0;
+    }
+
+    // Sanity check - remove
+    if (gContext.device->notify != hal_notify_callback) {
+        ALOGE("NOTIFY not set properly: %p != %p", gContext.device->notify, hal_notify_callback);
+    }
+
+    ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
+    return reinterpret_cast<jlong>(gContext.device);
+}
+
+static jint nativeCloseHal(JNIEnv* env, jobject clazz) {
+    return -ENOSYS; // TODO
+}
+
+// ----------------------------------------------------------------------------
+
+// TODO: clean up void methods
+static const JNINativeMethod g_methods[] = {
+    { "nativeEnroll", "(I)I", (void*)nativeEnroll },
+    { "nativeEnrollCancel", "()I", (void*)nativeEnroll },
+    { "nativeRemove", "(I)I", (void*)nativeRemove },
+    { "nativeOpenHal", "()I", (void*)nativeOpenHal },
+    { "nativeCloseHal", "()I", (void*)nativeCloseHal },
+    { "nativeInit", "(Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit }
+};
+
+int register_android_server_fingerprint_FingerprintService(JNIEnv* env) {
+    FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
+    GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz, "notify",
+            "(III)V");
+    int result = AndroidRuntime::registerNativeMethods(
+        env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods));
+    ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n");
+    return result;
 }
 
 } // namespace android
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
new file mode 100644
index 0000000..2941574
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -0,0 +1,328 @@
+/**
+ * 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 com.android.server.fingerprint;
+
+import android.app.Service;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.service.fingerprint.FingerprintManager;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+import android.service.fingerprint.FingerprintUtils;
+import android.service.fingerprint.IFingerprintService;
+import android.service.fingerprint.IFingerprintServiceReceiver;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * 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 SystemService {
+    private final String TAG = "FingerprintService";
+    private static final boolean DEBUG = true;
+    private ArrayMap<IBinder, ClientData> mClients = new ArrayMap<IBinder, 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 Context mContext;
+
+    private static final int STATE_IDLE = 0;
+    private static final int STATE_LISTENING = 1;
+    private static final int STATE_ENROLLING = 2;
+    private static final int STATE_REMOVING = 3;
+    private static final long MS_PER_SEC = 1000;
+    public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
+    public static final String ENROLL_FINGERPRINT = "android.permission.ENROLL_FINGERPRINT";
+
+    private static final class ClientData {
+        public IFingerprintServiceReceiver receiver;
+        int state;
+        int userId;
+        public TokenWatcher tokenWatcher;
+        IBinder getToken() { return tokenWatcher.getToken(); }
+    }
+
+    private class TokenWatcher implements IBinder.DeathRecipient {
+        WeakReference<IBinder> token;
+
+        TokenWatcher(IBinder token) {
+            this.token = new WeakReference<IBinder>(token);
+        }
+
+        IBinder getToken() { return token.get(); }
+        public void binderDied() {
+            mClients.remove(token);
+            this.token = null;
+        }
+
+        protected void finalize() throws Throwable {
+            try {
+                if (token != null) {
+                    if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
+                    mClients.remove(token);
+                }
+            } finally {
+                super.finalize();
+            }
+        }
+    }
+
+    public FingerprintService(Context context) {
+        super(context);
+        mContext = context;
+        nativeInit(this);
+    }
+
+    // TODO: Move these into separate process
+    // JNI methods to communicate from FingerprintManagerService to HAL
+    native int nativeEnroll(int timeout);
+    native int nativeEnrollCancel();
+    native int nativeRemove(int fingerprintId);
+    native int nativeOpenHal();
+    native int nativeCloseHal();
+    native void nativeInit(FingerprintService service);
+
+    // 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) {
+        Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")");
+        for (int i = 0; i < mClients.size(); i++) {
+            ClientData clientData = mClients.valueAt(i);
+            if (clientData == null || clientData.receiver == null) {
+                if (DEBUG) Slog.v(TAG, "clientData at " + i + " is invalid!!");
+                continue;
+            }
+            switch (msg) {
+                case FingerprintManager.FINGERPRINT_ERROR: {
+                    final int error = arg1;
+                    try {
+                        clientData.receiver.onError(error);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "can't send message to client. Did it die?", e);
+                        mClients.remove(mClients.keyAt(i));
+                    }
+                }
+                break;
+                case FingerprintManager.FINGERPRINT_ACQUIRED: {
+                    final int acquireInfo = arg1;
+                    try {
+                        clientData.receiver.onAcquired(acquireInfo);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "can't send message to client. Did it die?", e);
+                        mClients.remove(mClients.keyAt(i));
+                    }
+                    break;
+                }
+                case FingerprintManager.FINGERPRINT_PROCESSED: {
+                    final int fingerId = arg1;
+                    try {
+                        clientData.receiver.onProcessed(fingerId);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "can't send message to client. Did it die?", e);
+                        mClients.remove(mClients.keyAt(i));
+                    }
+                    break;
+                }
+                case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
+                    final int fingerId = arg1;
+                    final int remaining = arg2;
+                    if (clientData.state == STATE_ENROLLING) {
+                        // Only send enroll updates to clients that are actually enrolling
+                        try {
+                            clientData.receiver.onEnrollResult(fingerId, remaining);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "can't send message to client. Did it die?", e);
+                            mClients.remove(mClients.keyAt(i));
+                        }
+                        // Update the database with new finger id.
+                        // TODO: move to client code (Settings)
+                        if (remaining == 0) {
+                            FingerprintUtils.addFingerprintIdForUser(fingerId,
+                                    mContext.getContentResolver(), clientData.userId);
+                            clientData.state = STATE_IDLE; // Nothing left to do
+                        }
+                    } else {
+                        if (DEBUG) Slog.w(TAG, "Client not enrolling");
+                        break;
+                    }
+                    break;
+                }
+                case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
+                    int fingerId = arg1;
+                    if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
+                    FingerprintUtils.removeFingerprintIdForUser(fingerId,
+                            mContext.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);
+                            mClients.remove(mClients.keyAt(i));
+                        }
+                    }
+                    clientData.state = STATE_LISTENING;
+                }
+                break;
+            }
+        }
+    }
+
+    void startEnroll(IBinder token, long timeout, int userId) {
+        ClientData clientData = mClients.get(token);
+        if (clientData != null) {
+            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+            clientData.state = STATE_ENROLLING;
+            nativeEnroll((int) (timeout / MS_PER_SEC));
+        } else {
+            Slog.w(TAG, "enroll(): No listener registered");
+        }
+    }
+
+    void startEnrollCancel(IBinder token, int userId) {
+        ClientData clientData = mClients.get(token);
+        if (clientData != null) {
+            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+            clientData.state = STATE_LISTENING;
+            nativeEnrollCancel();
+        } else {
+            Slog.w(TAG, "enrollCancel(): No listener registered");
+        }
+    }
+
+    // Remove all fingerprints for the given user.
+    void startRemove(IBinder token, int fingerId, int userId) {
+        ClientData clientData = mClients.get(token);
+        if (clientData != null) {
+            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+            clientData.state = STATE_REMOVING;
+            // The fingerprint id will be removed when we get confirmation from the HAL
+            int result = nativeRemove(fingerId);
+            if (result != 0) {
+                Slog.w(TAG, "Error removing fingerprint with id = " + fingerId);
+            }
+        } else {
+            Slog.w(TAG, "remove(" + token + "): No listener registered");
+        }
+    }
+
+    void addListener(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
+        if (DEBUG) Slog.v(TAG, "startListening(" + receiver + ")");
+        if (mClients.get(token) == null) {
+            ClientData clientData = new ClientData();
+            clientData.state = STATE_LISTENING;
+            clientData.receiver = receiver;
+            clientData.userId = userId;
+            clientData.tokenWatcher = new TokenWatcher(token);
+            try {
+                token.linkToDeath(clientData.tokenWatcher, 0);
+                mClients.put(token, clientData);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+            }
+        } else {
+            if (DEBUG) Slog.v(TAG, "listener already registered for " + token);
+        }
+    }
+
+    void removeListener(IBinder token, int userId) {
+        if (DEBUG) Slog.v(TAG, "stopListening(" + token + ")");
+        ClientData clientData = mClients.get(token);
+        if (clientData != null) {
+            token.unlinkToDeath(clientData.tokenWatcher, 0);
+            mClients.remove(token);
+        } else {
+            if (DEBUG) Slog.v(TAG, "listener not registered: " + token);
+        }
+        mClients.remove(token);
+    }
+
+    void checkPermission(String permisison) {
+        // TODO
+    }
+
+    private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+        @Override // Binder call
+        public void enroll(IBinder token, long timeout, int userId) {
+            checkPermission(ENROLL_FINGERPRINT);
+            startEnroll(token, timeout, userId);
+        }
+
+        @Override // Binder call
+        public void enrollCancel(IBinder token,int userId) {
+            checkPermission(ENROLL_FINGERPRINT);
+            startEnrollCancel(token, userId);
+        }
+
+        @Override // Binder call
+        public void remove(IBinder token, int fingerprintId, int userId) {
+            checkPermission(ENROLL_FINGERPRINT); // TODO: Maybe have another permission
+            startRemove(token, fingerprintId, userId);
+        }
+
+        @Override // Binder call
+        public void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId)
+        {
+            checkPermission(USE_FINGERPRINT);
+            addListener(token, receiver, userId);
+        }
+
+        @Override // Binder call
+        public void stopListening(IBinder token, int userId) {
+            checkPermission(USE_FINGERPRINT);
+            removeListener(token, userId);
+        }
+    }
+
+    @Override
+    public void onStart() {
+       publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
+       nativeOpenHal();
+    }
+
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3102cce..5cccdd6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -44,6 +44,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.service.dreams.DreamService;
+import android.service.fingerprint.FingerprintManager;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -63,6 +64,7 @@
 import com.android.server.devicepolicy.DevicePolicyManagerService;
 import com.android.server.display.DisplayManagerService;
 import com.android.server.dreams.DreamManagerService;
+import com.android.server.fingerprint.FingerprintService;
 import com.android.server.hdmi.HdmiControlService;
 import com.android.server.input.InputManagerService;
 import com.android.server.job.JobSchedulerService;
@@ -989,11 +991,19 @@
                 }
 
                 try {
+                    Slog.i(TAG, "Fingerprint Manager");
+                    mSystemServiceManager.startService(FingerprintService.class);
+                } catch (Throwable e) {
+                    Slog.e(TAG, "Failure starting FingerprintService", e);
+                }
+
+                try {
                     Slog.i(TAG, "BackgroundDexOptService");
                     new BackgroundDexOptService(context);
                 } catch (Throwable e) {
                     reportWtf("starting BackgroundDexOptService", e);
                 }
+
             }
 
             try {