diff options
author | Jim Kaye <jameskaye@google.com> | 2015-09-10 16:53:21 -0700 |
---|---|---|
committer | Jim Kaye <jameskaye@google.com> | 2015-09-10 16:53:21 -0700 |
commit | 5ff9e3298ed844aaac2a54608b36ed56f7f09319 (patch) | |
tree | 9c7d0cf318512ef75cb6db85c2808faa602c7e14 | |
parent | 82f9ec5920daa3430fad54a17400acaa47329f71 (diff) | |
parent | b82a006cf7db1d3790272279eeaa066827d5bf81 (diff) | |
download | android_device_generic_goldfish-5ff9e3298ed844aaac2a54608b36ed56f7f09319.tar.gz android_device_generic_goldfish-5ff9e3298ed844aaac2a54608b36ed56f7f09319.tar.bz2 android_device_generic_goldfish-5ff9e3298ed844aaac2a54608b36ed56f7f09319.zip |
Merge commit 'b82a006cf7db1d3790272279eeaa066827d5bf81' into gf-merge
Primarily pick up the additional comments/descriptions that were
in parallel.
Change-Id: I164986a39d9c9c4cccaec165f011e3c1fe930128
-rw-r--r-- | fingerprint/fingerprint.c | 773 |
1 files changed, 495 insertions, 278 deletions
diff --git a/fingerprint/fingerprint.c b/fingerprint/fingerprint.c index 1b26b8f..0b4ca7f 100644 --- a/fingerprint/fingerprint.c +++ b/fingerprint/fingerprint.c @@ -25,15 +25,8 @@ * Please see the Android Compatibility Definition Document (CDD) for a full list of requirements * and suggestions. */ -#define FINGERPRINT_LISTEN_SERVICE_NAME "fingerprintlisten" -#define FINGERPRINT_TXT_FILENAME "/data/fingerprint.txt" - #define LOG_TAG "FingerprintHal" -// Typical devices will allow up to 5 fingerprints per user to maintain performance of -// t < 500ms for recognition. This is the total number of fingerprints we'll store. -#define MAX_NUM_FINGERS 32 - #include <errno.h> #include <endian.h> #include <inttypes.h> @@ -44,6 +37,16 @@ #include <hardware/fingerprint.h> #include <hardware/qemud.h> +#define FINGERPRINT_LISTEN_SERVICE_NAME "fingerprintlisten" +#define FINGERPRINT_FILENAME \ + "/data/system/users/0/fpdata/emulator_fingerprint_storage.bin" +#define MAX_COMM_CHARS 128 +#define MAX_COMM_ERRORS 8 +// Typical devices will allow up to 5 fingerprints per user to maintain performance of +// t < 500ms for recognition. This is the total number of fingerprints we'll store. +#define MAX_NUM_FINGERS 20 +#define MAX_FID_VALUE 0x7FFFFFFF // Arbitrary limit + /** * Most devices will have an internal state machine resembling this. There are 3 basic states, as * shown below. When device is not authenticating or enrolling, it is expected to be in @@ -54,254 +57,124 @@ * is restored. This is to facilitate rapid touch-to-unlock from keyguard. */ typedef enum worker_state_t { - STATE_ENROLL = 1, - STATE_SCAN = 2, - STATE_IDLE = 3, + STATE_IDLE = 0, + STATE_ENROLL, + STATE_SCAN, + STATE_EXIT } worker_state_t; typedef struct worker_thread_t { pthread_t thread; - pthread_mutex_t mutex; - int request; worker_state_t state; - int fingerid; - int finger_is_on; - int all_fingerids[MAX_NUM_FINGERS]; - uint64_t all_secureids[MAX_NUM_FINGERS]; - uint64_t all_authenids[MAX_NUM_FINGERS]; - int num_fingers_enrolled; - FILE *fp_write;; + uint64_t secureid[MAX_NUM_FINGERS]; + uint64_t authenid[MAX_NUM_FINGERS]; } worker_thread_t; -typedef struct emu_fingerprint_hal_device_t { - fingerprint_device_t device; //inheritance +typedef struct qemu_fingerprint_device_t { + fingerprint_device_t device; // "inheritance" worker_thread_t listener; uint64_t op_id; uint64_t challenge; - uint64_t secure_user_id; uint64_t user_id; + uint64_t group_id; + uint64_t secure_user_id; uint64_t authenticator_id; + int qchanfd; pthread_mutex_t lock; -} emu_fingerprint_hal_device_t; +} qemu_fingerprint_device_t; -static uint64_t get_64bit_rand() { - // This should use a cryptographically-secure random number generator like arc4random(). - // It should be generated inside of the TEE where possible. Here we just use something - // very simple. - return (((uint64_t) rand()) << 32) | ((uint64_t) rand()); -} +/******************************************************************************/ -static void destroyListenerThread(emu_fingerprint_hal_device_t* dev) -{ - pthread_join(dev->listener.thread, NULL); - pthread_mutex_destroy(&dev->listener.mutex); -} +static void saveFingerprint(worker_thread_t* listener, int idx) { + ALOGD("----------------> %s -----------------> idx %d", __FUNCTION__, idx); -bool finger_already_enrolled(emu_fingerprint_hal_device_t* dev) { - int i; - for (i = 0; i < dev->listener.num_fingers_enrolled; ++ i) { - if (dev->listener.fingerid == dev->listener.all_fingerids[i % MAX_NUM_FINGERS]) { - dev->secure_user_id = dev->listener.all_secureids[i % MAX_NUM_FINGERS]; - dev->authenticator_id = dev->listener.all_authenids[i % MAX_NUM_FINGERS]; - return true; - } + // Save fingerprints to file + FILE* fp = fopen(FINGERPRINT_FILENAME, "r+"); // write but don't truncate + if (fp == NULL) { + ALOGE("Could not open fingerprints storage at %s; " + "fingerprints won't be saved", + FINGERPRINT_FILENAME); + perror("Failed to open file"); + return; } - return false; -} - -static void save_fingerid(FILE* fp, int fingerid, uint64_t secureid, uint64_t authenid) { - if (!fp) return; - fprintf(fp, " %d %" PRIu64 " %" PRIu64, fingerid, secureid, authenid); - fflush(fp); -} -/** - * This is the communication channel from the HAL layer to fingerprintd. - */ -static void listener_send_notice(emu_fingerprint_hal_device_t* dev) -{ - fingerprint_msg_t message = {0}; - bool is_authentication = false; - bool is_valid_finger = false; - pthread_mutex_lock(&dev->listener.mutex); - if (dev->listener.state == STATE_ENROLL) { - message.type = FINGERPRINT_TEMPLATE_ENROLLING; - message.data.enroll.finger.fid = dev->listener.fingerid; - message.data.enroll.samples_remaining = 0; - dev->authenticator_id = get_64bit_rand(); - dev->listener.state = STATE_SCAN; - if (!finger_already_enrolled(dev)) { - const int n = dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS; - dev->listener.all_fingerids[n] = dev->listener.fingerid; - dev->listener.all_secureids[n] = dev->secure_user_id; - dev->listener.all_authenids[n] = dev->authenticator_id; - ++ dev->listener.num_fingers_enrolled; - save_fingerid(dev->listener.fp_write, dev->listener.fingerid, dev->secure_user_id, - dev->authenticator_id); - is_valid_finger = true; - } - } else { - is_authentication = true; - is_valid_finger = finger_already_enrolled(dev); - message.type = FINGERPRINT_AUTHENTICATED; - message.data.authenticated.finger.gid = 0; - message.data.authenticated.finger.fid = is_valid_finger ? dev->listener.fingerid : 0; - message.data.authenticated.hat.version = HW_AUTH_TOKEN_VERSION; - message.data.authenticated.hat.authenticator_type = htobe32(HW_AUTH_FINGERPRINT); - message.data.authenticated.hat.challenge = dev->op_id; - message.data.authenticated.hat.authenticator_id = dev->authenticator_id; - message.data.authenticated.hat.user_id = dev->secure_user_id; - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - message.data.authenticated.hat.timestamp = - htobe64((uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000); - } - pthread_mutex_unlock(&dev->listener.mutex); + ALOGD("Write fingerprint[%d] (0x%" PRIx64 ",0x%" PRIx64 ")", idx, + listener->secureid[idx], listener->authenid[idx]); - pthread_mutex_lock(&dev->lock); - if (is_authentication) { - fingerprint_msg_t acquired_message = {0}; - acquired_message.type = FINGERPRINT_ACQUIRED; - message.data.acquired.acquired_info = FINGERPRINT_ACQUIRED_GOOD; - dev->device.notify(&acquired_message); + if (fseek(fp, (idx) * sizeof(uint64_t), SEEK_SET) < 0) { + ALOGE("Failed while seeking for fingerprint[%d] in emulator storage", + idx); + fclose(fp); + return; } - if (is_valid_finger || is_authentication) { - dev->device.notify(&message); + int ns = fwrite(&listener->secureid[idx], sizeof(uint64_t), 1, fp); + if (fseek(fp, (MAX_NUM_FINGERS + idx) * sizeof(uint64_t), SEEK_SET) < 0) { + ALOGE("Failed while seeking for fingerprint[%d] in emulator storage", + idx); + fclose(fp); + return; } - pthread_mutex_unlock(&dev->lock); -} + int na = fwrite(&listener->authenid[idx], sizeof(uint64_t), 1, fp); + if (ns != 1 || na != 1) + ALOGW("Corrupt emulator fingerprints storage; could not save " + "fingerprints"); -/** - * This a very simple event loop for the fingerprint sensor. For a given state (enroll, scan), - * this would receive events from the sensor and forward them to fingerprintd using the - * notify() method. - * - * In this simple example, we open a qemu channel (a pipe) where the developer can inject events to - * exercise the API and test application code. - * - * The scanner should remain in the scanning state until either an error occurs or the operation - * completes. - * - * Recoverable errors such as EINTR should be handled locally; they should not - * be propagated unless there's something the user can do about it (e.g. "clean sensor"). Such - * messages should go through the onAcquired() interface. - * - * If an unrecoverable error occurs, an acquired message (e.g. ACQUIRED_PARTIAL) should be sent, - * followed by an error message (e.g. FINGERPRINT_ERROR_UNABLE_TO_PROCESS). - * - * Note that this event loop would typically run in TEE since it must interact with the sensor - * hardware and handle raw fingerprint data and encrypted templates. It is expected that - * this code monitors the TEE for resulting events, such as enrollment and authentication status. - * Here we just have a very simple event loop that monitors a qemu channel for pseudo events. - */ -static void* listenerFunction(void* data) -{ - emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) data; - - int fd = qemud_channel_open(FINGERPRINT_LISTEN_SERVICE_NAME); - if (fd < 0) { - ALOGE("listener cannot open fingerprint listener service exit"); - return NULL; - } + fclose(fp); - const char* cmd = "listen"; - if (qemud_channel_send(fd, cmd, strlen(cmd)) < 0) { - ALOGE("cannot write fingerprint 'listen' to host"); - return NULL; - } + return; +} - int i; - for (i = 0; i < MAX_NUM_FINGERS; ++ i) { - dev->listener.all_fingerids[i] = 0; - } - //read registered fingerprint ids from /data/local/fingerprint.txt - //TODO: store it in a better location - dev->listener.num_fingers_enrolled = 0; - FILE* fp_stored = fopen(FINGERPRINT_TXT_FILENAME, "r"); - if (fp_stored) { - while (1) { - int fingerid = 0; - uint64_t secureid = 0; - uint64_t authenid = 0; - if(fscanf(fp_stored, "%d %" SCNu64 " %" SCNu64, &fingerid, &secureid, &authenid) == 3) { - const int n = dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS; - dev->listener.all_fingerids[n] = fingerid; - dev->listener.all_secureids[n] = secureid; - dev->listener.all_authenids[n] = authenid; - ++ dev->listener.num_fingers_enrolled; - } else { - break; - } - } - fclose(fp_stored); +static void loadFingerprints(worker_thread_t* listener) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + FILE* fp = fopen(FINGERPRINT_FILENAME, "a+"); // so we can create if empty + if (fp == NULL) { + ALOGE("Could not load fingerprints from storage at %s; " + "it has not yet been created.", + FINGERPRINT_FILENAME); + perror("Failed to open/create file"); + return; } - dev->listener.fp_write = fopen(FINGERPRINT_TXT_FILENAME, "a"); + int ns = fread(listener->secureid, MAX_NUM_FINGERS * sizeof(uint64_t), 1, + fp); + int na = fread(listener->authenid, MAX_NUM_FINGERS * sizeof(uint64_t), 1, + fp); + if (ns != 1 || na != 1) + ALOGW("Corrupt emulator fingerprints storage (read %d+%db)", ns, na); - char buffer[128]; - int fingerid=-1; - int size; - while (1) { - //simply listen in blocking mode - if ((size = qemud_channel_recv(fd, buffer, sizeof buffer - 1)) >0) { - buffer[size] = '\0'; - if (sscanf(buffer, "on:%d", &fingerid) == 1) { - if (fingerid > 0 ) { - dev->listener.fingerid = fingerid; - dev->listener.finger_is_on = 1; - ALOGD("got finger %d", fingerid); - listener_send_notice(dev); - ALOGD("send notice finger %d", fingerid); - } - else { - ALOGE("finger id should be positive"); - } - } else if (strncmp("off", buffer, 3) == 0) { - dev->listener.finger_is_on = 0; - ALOGD("finger off %d", fingerid); - } else { - ALOGE("error: '%s'", buffer); - } - } else { - ALOGE("receive failure"); - // return NULL; - } - //TODO: check for request to exit thread - } + int i = 0; + for (i = 0; i < MAX_NUM_FINGERS; i++) + ALOGD("Read fingerprint %d (0x%" PRIx64 ",0x%" PRIx64 ")", i, + listener->secureid[i], listener->authenid[i]); - ALOGD("listener exit"); - return NULL; -} + fclose(fp); -static void createListenerThread(emu_fingerprint_hal_device_t* dev) -{ - pthread_mutex_init(&dev->listener.mutex, NULL); - pthread_create(&dev->listener.thread, NULL, listenerFunction, dev); + return; } -static int fingerprint_close(hw_device_t *dev) -{ - if (dev) { - destroyListenerThread((emu_fingerprint_hal_device_t*) dev); - free(dev); - return 0; - } else { - return -1; - } -} +/******************************************************************************/ -static void setListenerState(emu_fingerprint_hal_device_t* dev, worker_state_t state) { - pthread_mutex_lock(&dev->listener.mutex); - dev->listener.state = state; - pthread_mutex_unlock(&dev->listener.mutex); +static uint64_t get_64bit_rand() { + // This should use a cryptographically-secure random number generator like arc4random(). + // It should be generated inside of the TEE where possible. Here we just use something + // very simple. + ALOGD("----------------> %s ----------------->", __FUNCTION__); + uint64_t r = (((uint64_t)rand()) << 32) | ((uint64_t)rand()); + return r != 0 ? r : 1; } -static uint64_t fingerprint_get_auth_id(struct fingerprint_device __unused *device) { +static uint64_t fingerprint_get_auth_id(struct fingerprint_device* device) { // This should return the authentication_id generated when the fingerprint template database // was created. Though this isn't expected to be secret, it is reasonable to expect it to be // cryptographically generated to avoid replay attacks. - emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device; - return dev->authenticator_id; + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + ALOGD("----------------> %s ----------------->", __FUNCTION__); + uint64_t authenticator_id = 0; + pthread_mutex_lock(&qdev->lock); + authenticator_id = qdev->authenticator_id; + pthread_mutex_unlock(&qdev->lock); + + return authenticator_id; } static int fingerprint_set_active_group(struct fingerprint_device __unused *device, uint32_t gid, @@ -310,6 +183,7 @@ static int fingerprint_set_active_group(struct fingerprint_device __unused *devi // as the primary group id for the user. This code should create a tuple (groupId, fingerId) // that represents a single fingerprint entity in the database. For now we just generate // globally unique ids. + ALOGW("Setting active finger group not implemented"); return 0; } @@ -319,16 +193,16 @@ static int fingerprint_set_active_group(struct fingerprint_device __unused *devi * Actual authentication must happen in TEE and should be monitored in a separate thread * since this function is expected to return immediately. */ -static int fingerprint_authenticate(struct fingerprint_device __unused *device, - uint64_t __unused operation_id, __unused uint32_t gid) +static int fingerprint_authenticate(struct fingerprint_device *device, + uint64_t operation_id, __unused uint32_t gid) { - ALOGD("fingerprint_authenticate"); + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + + pthread_mutex_lock(&qdev->lock); + qdev->op_id = operation_id; + qdev->listener.state = STATE_SCAN; + pthread_mutex_unlock(&qdev->lock); - emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device; - pthread_mutex_lock(&dev->lock); - dev->op_id = operation_id; - pthread_mutex_unlock(&dev->lock); - setListenerState(dev, STATE_SCAN); return 0; } @@ -346,14 +220,18 @@ static int fingerprint_enroll(struct fingerprint_device *device, uint32_t __unused gid, uint32_t __unused timeout_sec) { ALOGD("fingerprint_enroll"); - emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device; - if (hat && hat->challenge == dev->challenge) { + qemu_fingerprint_device_t* dev = (qemu_fingerprint_device_t*)device; + if (!hat) { + ALOGW("%s: null auth token", __func__); + return -EPROTONOSUPPORT; + } + if (hat->challenge == dev->challenge) { // The secure_user_id retrieved from the auth token should be stored // with the enrolled fingerprint template and returned in the auth result // for a successful authentication with that finger. dev->secure_user_id = hat->user_id; } else { - ALOGW("%s: invalid or null auth token", __func__); + ALOGW("%s: invalid auth token", __func__); } if (hat->version != HW_AUTH_TOKEN_VERSION) { @@ -365,8 +243,13 @@ static int fingerprint_enroll(struct fingerprint_device *device, dev->user_id = hat->user_id; - // TODO: store enrolled fingerprints, authenticator id, and secure_user_id - setListenerState(dev, STATE_ENROLL); + pthread_mutex_lock(&dev->lock); + dev->listener.state = STATE_ENROLL; + pthread_mutex_unlock(&dev->lock); + + // fingerprint id, authenticator id, and secure_user_id + // will be stored by worked thread + return 0; } @@ -378,85 +261,419 @@ static int fingerprint_enroll(struct fingerprint_device *device, * propagated to the enroll step. */ static uint64_t fingerprint_pre_enroll(struct fingerprint_device *device) { - ALOGD("fingerprint_pre_enroll"); - emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device; + ALOGD("----------------> %s ----------------->", __FUNCTION__); + uint64_t challenge = 0; + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + // The challenge will typically be a cryptographically-secure key // coming from the TEE so it can be verified at a later step. For now we just generate a // random value. - dev->challenge = get_64bit_rand(); - return dev->challenge; + challenge = get_64bit_rand(); + + pthread_mutex_lock(&qdev->lock); + qdev->challenge = challenge; + pthread_mutex_unlock(&qdev->lock); + + return challenge; +} + +static int fingerprint_post_enroll(struct fingerprint_device* device) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + + pthread_mutex_lock(&qdev->lock); + qdev->challenge = 0; + pthread_mutex_unlock(&qdev->lock); + + return 0; } /** * Cancel is called by the framework to cancel an outstanding event. This should *not* be called * by the driver since it will cause the framework to stop listening for fingerprints. */ -static int fingerprint_cancel(struct fingerprint_device __unused *device) { - ALOGD("fingerprint_cancel"); - emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device; - setListenerState(dev, STATE_IDLE); +static int fingerprint_cancel(struct fingerprint_device *device) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + + fingerprint_msg_t msg = {0}; + msg.type = FINGERPRINT_ERROR; + msg.data.error = FINGERPRINT_ERROR_CANCELED; + + pthread_mutex_lock(&qdev->lock); + qdev->listener.state = STATE_IDLE; + pthread_mutex_unlock(&qdev->lock); + + device->notify(&msg); + return 0; } static int fingerprint_enumerate(struct fingerprint_device *device, fingerprint_finger_id_t *results, uint32_t *max_size) { - // TODO: implement me - return 0; + ALOGD("----------------> %s ----------------->", __FUNCTION__); + if (device == NULL || results == NULL || max_size == NULL) { + ALOGE("Cannot enumerate saved fingerprints with uninitialized params"); + return -1; + } + + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + unsigned int i = 0; + int num = 0; + for (i = 0; i < MAX_NUM_FINGERS; i++) { + if (qdev->listener.secureid[i] != 0 || + qdev->listener.authenid[i] != 0) { + ALOGD("ENUM: Fingerprint [%d] = 0x%" PRIx64 ",%" PRIx64, i, + qdev->listener.secureid[i], qdev->listener.authenid[i]); + num++; + } + } + + return num; } -static int fingerprint_remove(struct fingerprint_device __unused *dev, - uint32_t __unused gid, uint32_t __unused fid) { - // TODO: implement enroll and remove, and set dev->authenticator_id = 0 when no FPs enrolled - return FINGERPRINT_ERROR; +static int fingerprint_remove(struct fingerprint_device *device, + uint32_t __unused gid, uint32_t fid) { + int idx = 0; + fingerprint_msg_t msg = {0}; + ALOGD("----------------> %s -----------------> fid %d", __FUNCTION__, fid); + if (device == NULL) { + ALOGE("Can't remove fingerprint (gid=%d, fid=%d); " + "device not initialized properly", + gid, fid); + return -1; + } + + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + + if (fid == 0) { + // Delete all fingerprints + // I'll do this one at a time, so I am not + // holding the mutext during the notification + bool listIsEmpty; + do { + pthread_mutex_lock(&qdev->lock); + listIsEmpty = true; // Haven't seen a valid entry yet + for (idx = 0; idx < MAX_NUM_FINGERS; idx++) { + uint32_t theFid = qdev->listener.authenid[idx]; + if (theFid != 0) { + // Delete this entry + qdev->listener.secureid[idx] = 0; + qdev->listener.authenid[idx] = 0; + saveFingerprint(&qdev->listener, idx); + + // Send a notification that we deleted this one + pthread_mutex_unlock(&qdev->lock); + msg.type = FINGERPRINT_TEMPLATE_REMOVED; + msg.data.removed.finger.fid = theFid; + device->notify(&msg); + + // Because we released the mutex, the list + // may have changed. Restart the 'for' loop + // after reacquiring the mutex. + listIsEmpty = false; + break; + } + } // end for (idx < MAX_NUM_FINGERS) + } while (!listIsEmpty); + qdev->listener.state = STATE_IDLE; + pthread_mutex_unlock(&qdev->lock); + } else { + // Delete one fingerprint + // Look for this finger ID in our table. + pthread_mutex_lock(&qdev->lock); + for (idx = 0; idx < MAX_NUM_FINGERS; idx++) { + if (qdev->listener.authenid[idx] == fid && + qdev->listener.secureid[idx] != 0) { + // Found it! + break; + } + } + if (idx >= MAX_NUM_FINGERS) { + qdev->listener.state = STATE_IDLE; + pthread_mutex_unlock(&qdev->lock); + ALOGE("Fingerprint ID %d not found", fid); + return FINGERPRINT_ERROR; + } + + qdev->listener.secureid[idx] = 0; + qdev->listener.authenid[idx] = 0; + saveFingerprint(&qdev->listener, idx); + + qdev->listener.state = STATE_IDLE; + pthread_mutex_unlock(&qdev->lock); + + msg.type = FINGERPRINT_TEMPLATE_REMOVED; + msg.data.removed.finger.fid = fid; + device->notify(&msg); + } + + return 0; } static int set_notify_callback(struct fingerprint_device *device, - fingerprint_notify_t notify) { - ALOGD("set_notify"); - emu_fingerprint_hal_device_t* dev =(emu_fingerprint_hal_device_t*) device; - pthread_mutex_lock(&dev->lock); + fingerprint_notify_t notify) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + if (device == NULL || notify == NULL) { + ALOGE("Failed to set notify callback @ %p for fingerprint device %p", + device, notify); + return -1; + } + + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + pthread_mutex_lock(&qdev->lock); + qdev->listener.state = STATE_IDLE; device->notify = notify; + pthread_mutex_unlock(&qdev->lock); + ALOGD("fingerprint callback notification set"); + + return 0; +} + +static void send_scan_notice(qemu_fingerprint_device_t* qdev, int fid) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + + // acquired message + fingerprint_msg_t acqu_msg = {0}; + acqu_msg.type = FINGERPRINT_ACQUIRED; + acqu_msg.data.acquired.acquired_info = FINGERPRINT_ACQUIRED_GOOD; + + // authenticated message + fingerprint_msg_t auth_msg = {0}; + auth_msg.type = FINGERPRINT_AUTHENTICATED; + auth_msg.data.authenticated.finger.fid = fid; + auth_msg.data.authenticated.finger.gid = 0; // unused + auth_msg.data.authenticated.hat.version = HW_AUTH_TOKEN_VERSION; + auth_msg.data.authenticated.hat.authenticator_type = + htobe32(HW_AUTH_FINGERPRINT); + auth_msg.data.authenticated.hat.challenge = qdev->op_id; + auth_msg.data.authenticated.hat.authenticator_id = qdev->authenticator_id; + auth_msg.data.authenticated.hat.user_id = qdev->secure_user_id; + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + auth_msg.data.authenticated.hat.timestamp = + htobe64((uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000); + + // pthread_mutex_lock(&qdev->lock); + qdev->device.notify(&acqu_msg); + qdev->device.notify(&auth_msg); + // pthread_mutex_unlock(&qdev->lock); + + return; +} + +static void send_enroll_notice(qemu_fingerprint_device_t* qdev, int fid) { + ALOGD("----------------> %s -----------------> fid %d", __FUNCTION__, fid); + + if (fid == 0) { + ALOGD("Fingerprint ID is zero (invalid)"); + return; + } + if (qdev->secure_user_id == 0) { + ALOGD("Secure user ID is zero (invalid)"); + return; + } + + // Find an available entry in the table + pthread_mutex_lock(&qdev->lock); + int idx = 0; + for (idx = 0; idx < MAX_NUM_FINGERS; idx++) { + if (qdev->listener.secureid[idx] == 0 || + qdev->listener.authenid[idx] == 0) { + // This entry is available + break; + } + } + if (idx >= MAX_NUM_FINGERS) { + qdev->listener.state = STATE_SCAN; + pthread_mutex_unlock(&qdev->lock); + ALOGD("Fingerprint ID table is full"); + return; + } + + qdev->listener.secureid[idx] = qdev->secure_user_id; + qdev->listener.authenid[idx] = fid; + saveFingerprint(&qdev->listener, idx); + + qdev->listener.state = STATE_SCAN; + pthread_mutex_unlock(&qdev->lock); + + // LOCKED notification? + fingerprint_msg_t msg = {0}; + msg.type = FINGERPRINT_TEMPLATE_ENROLLING; + msg.data.enroll.finger.fid = fid; + msg.data.enroll.samples_remaining = 0; + qdev->device.notify(&msg); + + return; +} + +static worker_state_t getListenerState(qemu_fingerprint_device_t* dev) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + worker_state_t state = STATE_IDLE; + + pthread_mutex_lock(&dev->lock); + state = dev->listener.state; pthread_mutex_unlock(&dev->lock); + + return state; +} + +/** + * This a very simple event loop for the fingerprint sensor. For a given state (enroll, scan), + * this would receive events from the sensor and forward them to fingerprintd using the + * notify() method. + * + * In this simple example, we open a qemu channel (a pipe) where the developer can inject events to + * exercise the API and test application code. + * + * The scanner should remain in the scanning state until either an error occurs or the operation + * completes. + * + * Recoverable errors such as EINTR should be handled locally; they should not + * be propagated unless there's something the user can do about it (e.g. "clean sensor"). Such + * messages should go through the onAcquired() interface. + * + * If an unrecoverable error occurs, an acquired message (e.g. ACQUIRED_PARTIAL) should be sent, + * followed by an error message (e.g. FINGERPRINT_ERROR_UNABLE_TO_PROCESS). + * + * Note that this event loop would typically run in TEE since it must interact with the sensor + * hardware and handle raw fingerprint data and encrypted templates. It is expected that + * this code monitors the TEE for resulting events, such as enrollment and authentication status. + * Here we just have a very simple event loop that monitors a qemu channel for pseudo events. + */ +static void* listenerFunction(void* data) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)data; + + pthread_mutex_lock(&qdev->lock); + qdev->qchanfd = qemud_channel_open(FINGERPRINT_LISTEN_SERVICE_NAME); + if (qdev->qchanfd < 0) { + ALOGE("listener cannot open fingerprint listener service exit"); + pthread_mutex_unlock(&qdev->lock); + return NULL; + } + qdev->listener.state = STATE_IDLE; + pthread_mutex_unlock(&qdev->lock); + + const char* cmd = "listen"; + if (qemud_channel_send(qdev->qchanfd, cmd, strlen(cmd)) < 0) { + ALOGE("cannot write fingerprint 'listen' to host"); + return NULL; + } + int comm_errors = 0; + while (getListenerState(qdev) != STATE_EXIT) { + int size = 0; + int fid = 0; + char buffer[MAX_COMM_CHARS] = {0}; + // will block until a new event happens + if ((size = qemud_channel_recv(qdev->qchanfd, buffer, + sizeof(buffer) - 1)) > 0) { + buffer[size] = '\0'; + if (sscanf(buffer, "on:%d", &fid) == 1) { + if (fid > 0 && fid <= MAX_FID_VALUE) { + switch (qdev->listener.state) { + case STATE_ENROLL: + send_enroll_notice(qdev, fid); + break; + case STATE_SCAN: + send_scan_notice(qdev, fid); + break; + default: + ALOGE("fingerprint event listener at unexpected " + "state 0%x", + qdev->listener.state); + } + } else { + ALOGE("fingerprintid %d not in valid range [%d, %d] and " + "will be " + "ignored", + fid, 1, MAX_FID_VALUE); + continue; + } + } else if (strncmp("off", buffer, 3) == 0) { + // TODO: Nothing to do here ? Looks valid + ALOGD("fingerprint ID %d off", fid); + } else { + ALOGE("Invalid command '%s' to fingerprint listener", buffer); + } + } else { + ALOGE("fingerprint listener receive failure"); + if (comm_errors > MAX_COMM_ERRORS) + break; + } + } + + ALOGD("Listener exit with %d receive errors", comm_errors); + return NULL; +} + +static int fingerprint_close(hw_device_t* device) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + if (device == NULL) { + ALOGE("fingerprint hw device is NULL"); + return -1; + } + + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + pthread_mutex_lock(&qdev->lock); + if (qdev->qchanfd != 0) + close(qdev->qchanfd); // unblock listener + qdev->listener.state = STATE_EXIT; + pthread_mutex_unlock(&qdev->lock); + + pthread_join(qdev->listener.thread, NULL); + pthread_mutex_destroy(&qdev->lock); + free(qdev); + return 0; } static int fingerprint_open(const hw_module_t* module, const char __unused *id, hw_device_t** device) { + + ALOGD("----------------> %s ----------------->", __FUNCTION__); if (device == NULL) { ALOGE("NULL device on open"); return -EINVAL; - } else { - ALOGD("fingerprint open\n"); } - emu_fingerprint_hal_device_t *dev = malloc(sizeof(emu_fingerprint_hal_device_t)); - memset(dev, 0, sizeof(emu_fingerprint_hal_device_t)); - - dev->device.common.tag = HARDWARE_DEVICE_TAG; - dev->device.common.version = HARDWARE_MODULE_API_VERSION(2, 0); - dev->device.common.module = (struct hw_module_t*) module; - dev->device.common.close = fingerprint_close; - dev->device.pre_enroll = fingerprint_pre_enroll; - dev->device.enroll = fingerprint_enroll; - dev->device.get_authenticator_id = fingerprint_get_auth_id; - dev->device.set_active_group = fingerprint_set_active_group; - dev->device.authenticate = fingerprint_authenticate; - dev->device.cancel = fingerprint_cancel; - dev->device.enumerate = fingerprint_enumerate; - dev->device.remove = fingerprint_remove; - dev->device.set_notify = set_notify_callback; - dev->device.notify = NULL; - - // This is typically a cryptographically-secure token generated when the private fingerprint - // template database is created. For simplicity of this driver, we store a recognizable value. - // - // Real devices should *not* use this token! - dev->authenticator_id = 0xdeadbeef; - - pthread_mutex_init(&dev->lock, NULL); - createListenerThread(dev); - *device = (hw_device_t*) dev; + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)calloc( + 1, sizeof(qemu_fingerprint_device_t)); + if (qdev == NULL) { + ALOGE("Insufficient memory for virtual fingerprint device"); + return -ENOMEM; + } + + loadFingerprints(&qdev->listener); + + qdev->device.common.tag = HARDWARE_DEVICE_TAG; + qdev->device.common.version = HARDWARE_MODULE_API_VERSION(2, 0); + qdev->device.common.module = (struct hw_module_t*)module; + qdev->device.common.close = fingerprint_close; + + qdev->device.pre_enroll = fingerprint_pre_enroll; + qdev->device.enroll = fingerprint_enroll; + qdev->device.post_enroll = fingerprint_post_enroll; + qdev->device.get_authenticator_id = fingerprint_get_auth_id; + qdev->device.set_active_group = fingerprint_set_active_group; + qdev->device.authenticate = fingerprint_authenticate; + qdev->device.cancel = fingerprint_cancel; + qdev->device.enumerate = fingerprint_enumerate; + qdev->device.remove = fingerprint_remove; + qdev->device.set_notify = set_notify_callback; + qdev->device.notify = NULL; + + // init and create listener thread + pthread_mutex_init(&qdev->lock, NULL); + if (pthread_create(&qdev->listener.thread, NULL, listenerFunction, qdev) != + 0) + return -1; + + // "Inheritance" / casting + *device = &qdev->device.common; + return 0; } |