Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7a39c8c7 authored by Scott Mertz's avatar Scott Mertz Committed by Andy Mast
Browse files

Fingerprint: Make the existing FingerprintService semi-useful.

- Add explicit actions for authentication so HW has
  a chance to power down when not in use.  The other
  option would be to start scanning when startListening
  is called, but that would force apps trying to enroll
  to start trying to authenticate immediately before they
  start enrollment.  That didn't seem like a good idea.
- Update to CM Fingerprint HAL 1-1.
- Extreme care needs to be taken to use this
  API.  It's not exactly robust against multiple
  clients stepping on each other.
- All of this goes away in M.

Change-Id: I6e34935cdec5e8eeabeac6f8cf9fb58e689cb889
parent 29e7b6cb
Loading
Loading
Loading
Loading
+23 −4
Original line number Diff line number Diff line
@@ -60,6 +60,8 @@ public class FingerprintManager {
    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;
    public static final int FINGERPRINT_ERROR_CANCELED = 5;
    public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;

    // FINGERPRINT_ACQUIRED messages.  Must agree with HAL (fingerprint.h)
    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
@@ -142,6 +144,24 @@ public class FingerprintManager {
                && FingerprintUtils.getFingerprintIdsForUser(res, getCurrentUserId()).length > 0;
    }

    /**
     * Start the authentication process.
     *
     * @param timeout
     */
    public void authenticate() {
        if (mServiceReceiver == null) {
            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
            return;
        }
        if (mService != null) try {
            mService.authenticate(mToken, getCurrentUserId());
        } catch (RemoteException e) {
            Log.v(TAG, "Remote exception while enrolling: ", e);
            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
        }
    }

    /**
     * Start the enrollment process.  Timeout dictates how long to wait for the user to
     * enroll a fingerprint.
@@ -227,21 +247,20 @@ public class FingerprintManager {
        }
    }

    public void enrollCancel() {
    public void cancel() {
        if (mServiceReceiver == null) {
            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
            return;
        }
        if (mService != null) {
            try {
                mService.enrollCancel(mToken, getCurrentUserId());
                mClientReceiver = null;
                mService.cancel(mToken, getCurrentUserId());
            } 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!");
            Log.w(TAG, "cancel(): Service not connected!");
        }
    }

+5 −3
Original line number Diff line number Diff line
@@ -23,17 +23,19 @@ import android.service.fingerprint.IFingerprintServiceReceiver;
 * @hide
 */
oneway interface IFingerprintService {
    // Any errors resulting from this call will be returned to the listener
    void authenticate(IBinder token, int userId);

    // 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);
    void cancel(IBinder token, 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.
    // Start listening for fingerprint events.
    void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId);

    // Stops listening for fingerprints
+13 −6
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@

namespace android {

static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(1, 0);
static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(1, 1);

static const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService";
static struct {
@@ -113,15 +113,21 @@ static void nativeInit(JNIEnv *env, jobject clazz, jobject callbackObj) {
    gFingerprintServiceClassInfo.callbackObject = env->NewGlobalRef(callbackObj);
}

static jint nativeAuthenticate(JNIEnv* env, jobject clazz) {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeAuthenticate()\n");
    int ret = gContext.device->authenticate(gContext.device);
    return reinterpret_cast<jint>(ret);
}

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);
static jint nativeCancel(JNIEnv* env, jobject clazz) {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeCancel()\n");
    int ret = gContext.device->cancel(gContext.device);
    return reinterpret_cast<jint>(ret);
}

@@ -160,7 +166,7 @@ static jint nativeOpenHal(JNIEnv* env, jobject clazz) {

    if (kVersion != device->version) {
        ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
        // return 0; // FIXME
        return 0;
    }

    gContext.device = reinterpret_cast<fingerprint_device_t*>(device);
@@ -187,8 +193,9 @@ static jint nativeCloseHal(JNIEnv* env, jobject clazz) {

// TODO: clean up void methods
static const JNINativeMethod g_methods[] = {
    { "nativeAuthenticate", "()I", (void*)nativeAuthenticate },
    { "nativeEnroll", "(I)I", (void*)nativeEnroll },
    { "nativeEnrollCancel", "()I", (void*)nativeEnrollCancel },
    { "nativeCancel", "()I", (void*)nativeCancel },
    { "nativeRemove", "(I)I", (void*)nativeRemove },
    { "nativeOpenHal", "()I", (void*)nativeOpenHal },
    { "nativeCloseHal", "()I", (void*)nativeCloseHal },
+86 −43
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Iterator;

/**
 * A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -68,18 +69,19 @@ public class FingerprintService extends SystemService {
        }
    };
    private Context mContext;
    private int mState = STATE_IDLE;

    private static final int STATE_IDLE = 0;
    private static final int STATE_LISTENING = 1;
    private static final int STATE_AUTHENTICATING = 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 long mHal;

    private static final class ClientData {
        public IFingerprintServiceReceiver receiver;
        int state;
        int userId;
        public TokenWatcher tokenWatcher;
        IBinder getToken() { return tokenWatcher.getToken(); }
@@ -118,8 +120,9 @@ public class FingerprintService extends SystemService {

    // TODO: Move these into separate process
    // JNI methods to communicate from FingerprintManagerService to HAL
    native int nativeAuthenticate();
    native int nativeEnroll(int timeout);
    native int nativeEnrollCancel();
    native int nativeCancel();
    native int nativeRemove(int fingerprintId);
    native int nativeOpenHal();
    native int nativeCloseHal();
@@ -132,60 +135,77 @@ public class FingerprintService extends SystemService {

    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;
            }
        int newState = mState;
        for (Iterator<Entry<IBinder, ClientData>> it = mClients.entrySet().iterator();
                it.hasNext(); ) {
            ClientData clientData = it.next().getValue();
            switch (msg) {
                case FingerprintManager.FINGERPRINT_ERROR: {
                    final int error = arg1;
                    try {
                        newState = STATE_IDLE;
                        if (clientData != null && clientData.receiver != null) {
                            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));
                        it.remove();
                    }
                }
                break;
                case FingerprintManager.FINGERPRINT_ACQUIRED: {
                    final int acquireInfo = arg1;
                    if (mState == STATE_AUTHENTICATING) {
                        try {
                            if (clientData != null && clientData.receiver != null) {
                                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));
                            it.remove();
                        }
                    } else {
                        if (DEBUG) Slog.w(TAG, "Client not authenticating");
                        break;
                    }
                    break;
                }
                case FingerprintManager.FINGERPRINT_PROCESSED: {
                    final int fingerId = arg1;
                    if (mState == STATE_AUTHENTICATING) {
                        try {
                            newState = STATE_IDLE;
                            if (clientData != null && clientData.receiver != null) {
                                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));
                            it.remove();
                        }
                    } else {
                        if (DEBUG) Slog.w(TAG, "Client not authenticating");
                        break;
                    }
                    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
                    if (mState == STATE_ENROLLING) {
                        try {
                            if (clientData != null && clientData.receiver != null) {
                                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));
                            it.remove();
                        }
                        // 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
                            newState = STATE_IDLE;
                        }
                    } else {
                        if (DEBUG) Slog.w(TAG, "Client not enrolling");
@@ -198,38 +218,57 @@ public class FingerprintService extends SystemService {
                    if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
                    FingerprintUtils.removeFingerprintIdForUser(fingerId,
                            mContext.getContentResolver(), clientData.userId);
                    if (clientData.receiver != null) {
                    try {
                        if (clientData != null && clientData.receiver != null) {
                            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));
                        }
                        it.remove();
                    }
                    clientData.state = STATE_LISTENING;
                }
                break;
            }
        }
        mState = newState;
    }

    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;
            if (mState != STATE_IDLE) {
                Slog.i(TAG, "fingerprint is in use");
                return;
            }
            nativeEnroll((int) (timeout / MS_PER_SEC));
            mState = STATE_ENROLLING;
        } else {
            Slog.w(TAG, "enroll(): No listener registered");
        }
    }

    void startEnrollCancel(IBinder token, int userId) {
    void startAuthentication(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();
            if (mState != STATE_IDLE) {
                Slog.i(TAG, "fingerprint is in use");
                return;
            }
            nativeAuthenticate();
            mState = STATE_AUTHENTICATING;
        } else {
            Slog.w(TAG, "authenticate(): No listener registered");
        }
    }

    void startCancel(IBinder token, int userId) {
        ClientData clientData = mClients.get(token);
        if (clientData != null) {
            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
            if (mState == STATE_IDLE) return;
            nativeCancel();
        } else {
            Slog.w(TAG, "enrollCancel(): No listener registered");
        }
@@ -240,7 +279,6 @@ public class FingerprintService extends SystemService {
        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) {
@@ -255,7 +293,6 @@ public class FingerprintService extends SystemService {
        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);
@@ -287,6 +324,12 @@ public class FingerprintService extends SystemService {
    }

    private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
        @Override // Binder call
        public void authenticate(IBinder token, int userId) {
            checkPermission(USE_FINGERPRINT);
            startAuthentication(token, userId);
        }

        @Override // Binder call
        public void enroll(IBinder token, long timeout, int userId) {
            checkPermission(ENROLL_FINGERPRINT);
@@ -294,9 +337,9 @@ public class FingerprintService extends SystemService {
        }

        @Override // Binder call
        public void enrollCancel(IBinder token,int userId) {
            checkPermission(ENROLL_FINGERPRINT);
            startEnrollCancel(token, userId);
        public void cancel(IBinder token,int userId) {
            checkPermission(USE_FINGERPRINT);
            startCancel(token, userId);
        }

        @Override // Binder call
@@ -322,7 +365,7 @@ public class FingerprintService extends SystemService {
    @Override
    public void onStart() {
       publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
       nativeOpenHal();
       mHal = nativeOpenHal();
    }

}