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

Commit aef5cdb5 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 43b21f61
Loading
Loading
Loading
Loading
+23 −4
Original line number Original line 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_UNABLE_TO_PROCESS = 2;
    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
    public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
    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)
    // FINGERPRINT_ACQUIRED messages.  Must agree with HAL (fingerprint.h)
    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
@@ -142,6 +144,24 @@ public class FingerprintManager {
                && FingerprintUtils.getFingerprintIdsForUser(res, getCurrentUserId()).length > 0;
                && 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
     * Start the enrollment process.  Timeout dictates how long to wait for the user to
     * enroll a fingerprint.
     * enroll a fingerprint.
@@ -227,21 +247,20 @@ public class FingerprintManager {
        }
        }
    }
    }


    public void enrollCancel() {
    public void cancel() {
        if (mServiceReceiver == null) {
        if (mServiceReceiver == null) {
            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
            return;
            return;
        }
        }
        if (mService != null) {
        if (mService != null) {
            try {
            try {
                mService.enrollCancel(mToken, getCurrentUserId());
                mService.cancel(mToken, getCurrentUserId());
                mClientReceiver = null;
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Log.v(TAG, "Remote exception in enrollCancel(): ", e);
                Log.v(TAG, "Remote exception in enrollCancel(): ", e);
                sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
                sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
            }
            }
        } else {
        } else {
            Log.w(TAG, "enrollCancel(): Service not connected!");
            Log.w(TAG, "cancel(): Service not connected!");
        }
        }
    }
    }


+5 −3
Original line number Original line Diff line number Diff line
@@ -23,17 +23,19 @@ import android.service.fingerprint.IFingerprintServiceReceiver;
 * @hide
 * @hide
 */
 */
oneway interface IFingerprintService {
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
    // Any errors resulting from this call will be returned to the listener
    void enroll(IBinder token, long timeout, int userId);
    void enroll(IBinder token, long timeout, int userId);
    
    
    // Any errors resulting from this call will be returned to the listener
    // 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
    // Any errors resulting from this call will be returned to the listener
    void remove(IBinder token, int fingerprintId, int userId);
    void remove(IBinder token, int fingerprintId, int userId);


    // Start listening for fingerprint events.  This has the side effect of starting
    // Start listening for fingerprint events.
    // the hardware if not already started.
    void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId);
    void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId);


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


namespace android {
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 const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService";
static struct {
static struct {
@@ -113,15 +113,21 @@ static void nativeInit(JNIEnv *env, jobject clazz, jobject callbackObj) {
    gFingerprintServiceClassInfo.callbackObject = env->NewGlobalRef(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) {
static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll()\n");
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll()\n");
    int ret = gContext.device->enroll(gContext.device, timeout);
    int ret = gContext.device->enroll(gContext.device, timeout);
    return reinterpret_cast<jint>(ret);
    return reinterpret_cast<jint>(ret);
}
}


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


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


    if (kVersion != device->version) {
    if (kVersion != device->version) {
        ALOGE("Wrong fp version. Expected %d, got %d", 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);
    gContext.device = reinterpret_cast<fingerprint_device_t*>(device);
@@ -187,8 +193,9 @@ static jint nativeCloseHal(JNIEnv* env, jobject clazz) {


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


/**
/**
 * A service to manage multiple clients that want to access the fingerprint HAL API.
 * 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 Context mContext;
    private int mState = STATE_IDLE;


    private static final int STATE_IDLE = 0;
    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_ENROLLING = 2;
    private static final int STATE_REMOVING = 3;
    private static final long MS_PER_SEC = 1000;
    private static final long MS_PER_SEC = 1000;
    public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
    public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
    public static final String ENROLL_FINGERPRINT = "android.permission.ENROLL_FINGERPRINT";
    public static final String ENROLL_FINGERPRINT = "android.permission.ENROLL_FINGERPRINT";


    private long mHal;

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


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


    void handleNotify(int msg, int arg1, int arg2) {
    void handleNotify(int msg, int arg1, int arg2) {
        Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")");
        Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")");
        for (int i = 0; i < mClients.size(); i++) {
        int newState = mState;
            ClientData clientData = mClients.valueAt(i);
        for (Iterator<Entry<IBinder, ClientData>> it = mClients.entrySet().iterator();
            if (clientData == null || clientData.receiver == null) {
                it.hasNext(); ) {
                if (DEBUG) Slog.v(TAG, "clientData at " + i + " is invalid!!");
            ClientData clientData = it.next().getValue();
                continue;
            }
            switch (msg) {
            switch (msg) {
                case FingerprintManager.FINGERPRINT_ERROR: {
                case FingerprintManager.FINGERPRINT_ERROR: {
                    final int error = arg1;
                    final int error = arg1;
                    try {
                    try {
                        newState = STATE_IDLE;
                        if (clientData != null && clientData.receiver != null) {
                            clientData.receiver.onError(error);
                            clientData.receiver.onError(error);
                        }
                    } catch (RemoteException e) {
                    } catch (RemoteException e) {
                        Slog.e(TAG, "can't send message to client. Did it die?", e);
                        Slog.e(TAG, "can't send message to client. Did it die?", e);
                        mClients.remove(mClients.keyAt(i));
                        it.remove();
                    }
                    }
                }
                }
                break;
                break;
                case FingerprintManager.FINGERPRINT_ACQUIRED: {
                case FingerprintManager.FINGERPRINT_ACQUIRED: {
                    final int acquireInfo = arg1;
                    final int acquireInfo = arg1;
                    if (mState == STATE_AUTHENTICATING) {
                        try {
                        try {
                            if (clientData != null && clientData.receiver != null) {
                                clientData.receiver.onAcquired(acquireInfo);
                                clientData.receiver.onAcquired(acquireInfo);
                            }
                        } catch (RemoteException e) {
                        } catch (RemoteException e) {
                            Slog.e(TAG, "can't send message to client. Did it die?", 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;
                    break;
                }
                }
                case FingerprintManager.FINGERPRINT_PROCESSED: {
                case FingerprintManager.FINGERPRINT_PROCESSED: {
                    final int fingerId = arg1;
                    final int fingerId = arg1;
                    if (mState == STATE_AUTHENTICATING) {
                        try {
                        try {
                            newState = STATE_IDLE;
                            if (clientData != null && clientData.receiver != null) {
                                clientData.receiver.onProcessed(fingerId);
                                clientData.receiver.onProcessed(fingerId);
                            }
                        } catch (RemoteException e) {
                        } catch (RemoteException e) {
                            Slog.e(TAG, "can't send message to client. Did it die?", 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;
                    break;
                }
                }
                case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
                case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
                    final int fingerId = arg1;
                    final int fingerId = arg1;
                    final int remaining = arg2;
                    final int remaining = arg2;
                    if (clientData.state == STATE_ENROLLING) {
                    if (mState == STATE_ENROLLING) {
                        // Only send enroll updates to clients that are actually enrolling
                        try {
                        try {
                            if (clientData != null && clientData.receiver != null) {
                                clientData.receiver.onEnrollResult(fingerId, remaining);
                                clientData.receiver.onEnrollResult(fingerId, remaining);
                            }
                        } catch (RemoteException e) {
                        } catch (RemoteException e) {
                            Slog.e(TAG, "can't send message to client. Did it die?", 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.
                        // Update the database with new finger id.
                        // TODO: move to client code (Settings)
                        // TODO: move to client code (Settings)
                        if (remaining == 0) {
                        if (remaining == 0) {
                            FingerprintUtils.addFingerprintIdForUser(fingerId,
                            FingerprintUtils.addFingerprintIdForUser(fingerId,
                                    mContext.getContentResolver(), clientData.userId);
                                    mContext.getContentResolver(), clientData.userId);
                            clientData.state = STATE_IDLE; // Nothing left to do
                            newState = STATE_IDLE;
                        }
                        }
                    } else {
                    } else {
                        if (DEBUG) Slog.w(TAG, "Client not enrolling");
                        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");
                    if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
                    FingerprintUtils.removeFingerprintIdForUser(fingerId,
                    FingerprintUtils.removeFingerprintIdForUser(fingerId,
                            mContext.getContentResolver(), clientData.userId);
                            mContext.getContentResolver(), clientData.userId);
                    if (clientData.receiver != null) {
                    try {
                    try {
                        if (clientData != null && clientData.receiver != null) {
                            clientData.receiver.onRemoved(fingerId);
                            clientData.receiver.onRemoved(fingerId);
                        }
                    } catch (RemoteException e) {
                    } catch (RemoteException e) {
                        Slog.e(TAG, "can't send message to client. Did it die?", 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;
                break;
            }
            }
        }
        }
        mState = newState;
    }
    }


    void startEnroll(IBinder token, long timeout, int userId) {
    void startEnroll(IBinder token, long timeout, int userId) {
        ClientData clientData = mClients.get(token);
        ClientData clientData = mClients.get(token);
        if (clientData != null) {
        if (clientData != null) {
            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
            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));
            nativeEnroll((int) (timeout / MS_PER_SEC));
            mState = STATE_ENROLLING;
        } else {
        } else {
            Slog.w(TAG, "enroll(): No listener registered");
            Slog.w(TAG, "enroll(): No listener registered");
        }
        }
    }
    }


    void startEnrollCancel(IBinder token, int userId) {
    void startAuthentication(IBinder token, int userId) {
        ClientData clientData = mClients.get(token);
        ClientData clientData = mClients.get(token);
        if (clientData != null) {
        if (clientData != null) {
            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
            clientData.state = STATE_LISTENING;
            if (mState != STATE_IDLE) {
            nativeEnrollCancel();
                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 {
        } else {
            Slog.w(TAG, "enrollCancel(): No listener registered");
            Slog.w(TAG, "enrollCancel(): No listener registered");
        }
        }
@@ -240,7 +279,6 @@ public class FingerprintService extends SystemService {
        ClientData clientData = mClients.get(token);
        ClientData clientData = mClients.get(token);
        if (clientData != null) {
        if (clientData != null) {
            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
            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
            // The fingerprint id will be removed when we get confirmation from the HAL
            int result = nativeRemove(fingerId);
            int result = nativeRemove(fingerId);
            if (result != 0) {
            if (result != 0) {
@@ -255,7 +293,6 @@ public class FingerprintService extends SystemService {
        if (DEBUG) Slog.v(TAG, "startListening(" + receiver + ")");
        if (DEBUG) Slog.v(TAG, "startListening(" + receiver + ")");
        if (mClients.get(token) == null) {
        if (mClients.get(token) == null) {
            ClientData clientData = new ClientData();
            ClientData clientData = new ClientData();
            clientData.state = STATE_LISTENING;
            clientData.receiver = receiver;
            clientData.receiver = receiver;
            clientData.userId = userId;
            clientData.userId = userId;
            clientData.tokenWatcher = new TokenWatcher(token);
            clientData.tokenWatcher = new TokenWatcher(token);
@@ -287,6 +324,12 @@ public class FingerprintService extends SystemService {
    }
    }


    private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
    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
        @Override // Binder call
        public void enroll(IBinder token, long timeout, int userId) {
        public void enroll(IBinder token, long timeout, int userId) {
            checkPermission(ENROLL_FINGERPRINT);
            checkPermission(ENROLL_FINGERPRINT);
@@ -294,9 +337,9 @@ public class FingerprintService extends SystemService {
        }
        }


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


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


}
}