X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=src%2Ftype3_AndroidCloud%2Fanbox-master%2Fandroid%2Ffingerprint%2Ffingerprint.c;fp=src%2Ftype3_AndroidCloud%2Fanbox-master%2Fandroid%2Ffingerprint%2Ffingerprint.c;h=6405e26acf12079f7b3c360740f5f801bd2dcf35;hb=e26c1ec581be598521517829adba8c8dd23a768f;hp=0000000000000000000000000000000000000000;hpb=6699c1aea74eeb0eb400e6299079f0c7576f716f;p=iec.git diff --git a/src/type3_AndroidCloud/anbox-master/android/fingerprint/fingerprint.c b/src/type3_AndroidCloud/anbox-master/android/fingerprint/fingerprint.c new file mode 100644 index 0000000..6405e26 --- /dev/null +++ b/src/type3_AndroidCloud/anbox-master/android/fingerprint/fingerprint.c @@ -0,0 +1,857 @@ +/* + * Copyright (C) 2015 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. + */ + +/** + * This is a very basic implementation of fingerprint to allow testing on the emulator. It + * is *not* meant to be the final implementation on real devices. For example, it does *not* + * implement all of the required features, such as secure template storage and recognition + * inside a Trusted Execution Environment (TEE). However, this file is a reasonable starting + * point as developers add fingerprint support to their platform. See inline comments and + * recommendations for details. + * + * Please see the Android Compatibility Definition Document (CDD) for a full list of requirements + * and suggestions. + */ +#define LOG_TAG "FingerprintHal" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FINGERPRINT_LISTEN_SERVICE_NAME "fingerprintlisten" +#define FINGERPRINT_FILENAME "emufp.bin" +#define AUTHENTICATOR_ID_FILENAME "emuauthid.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 + * the idle state. + * + * Note that this is completely independent of device wake state. If the hardware device was in + * the "scan" state when the device drops into power collapse, it should resume scanning when power + * is restored. This is to facilitate rapid touch-to-unlock from keyguard. + */ +typedef enum worker_state_t { + STATE_IDLE = 0, + STATE_ENROLL, + STATE_SCAN, + STATE_EXIT +} worker_state_t; + +typedef struct worker_thread_t { + pthread_t thread; + worker_state_t state; + uint64_t secureid[MAX_NUM_FINGERS]; + uint64_t fingerid[MAX_NUM_FINGERS]; + char fp_filename[PATH_MAX]; + char authid_filename[PATH_MAX]; +} worker_thread_t; + +typedef struct qemu_fingerprint_device_t { + fingerprint_device_t device; // "inheritance" + worker_thread_t listener; + uint64_t op_id; + uint64_t challenge; + uint64_t user_id; + uint64_t group_id; + uint64_t secure_user_id; + uint64_t authenticator_id; + int qchanfd; + pthread_mutex_t lock; +} qemu_fingerprint_device_t; + +/******************************************************************************/ + +static FILE* openForWrite(const char* filename); + +static void saveFingerprint(worker_thread_t* listener, int idx) { + ALOGD("----------------> %s -----------------> idx %d", __FUNCTION__, idx); + + // Save fingerprints to file + FILE* fp = openForWrite(listener->fp_filename); + if (fp == NULL) { + ALOGE("Could not open fingerprints storage at %s; " + "fingerprints won't be saved", + listener->fp_filename); + perror("Failed to open file"); + return; + } + + ALOGD("Write fingerprint[%d] (0x%" PRIx64 ",0x%" PRIx64 ")", idx, + listener->secureid[idx], listener->fingerid[idx]); + + if (fseek(fp, (idx) * sizeof(uint64_t), SEEK_SET) < 0) { + ALOGE("Failed while seeking for fingerprint[%d] in emulator storage", + idx); + fclose(fp); + return; + } + 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; + } + int nf = fwrite(&listener->fingerid[idx], sizeof(uint64_t), 1, fp); + if (ns != 1 || ns !=1) + ALOGW("Corrupt emulator fingerprints storage; could not save " + "fingerprints"); + + fclose(fp); + + return; +} + +static FILE* openForWrite(const char* filename) { + + if (!filename) return NULL; + + FILE* fp = fopen(filename, "r+"); // write but don't truncate + if (fp == NULL) { + fp = fopen(filename, "w"); + if (fp) { + uint64_t zero = 0; + int i = 0; + for (i = 0; i < 2*MAX_NUM_FINGERS; ++i) { + fwrite(&zero, sizeof(uint64_t), 1, fp); + } + + //the last one is for authenticator id + fwrite(&zero, sizeof(uint64_t), 1, fp); + } + } + return fp; +} + +static void saveAuthenticatorId(const char* filename, uint64_t authenid) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + FILE* fp = openForWrite(filename); + if (!fp) { + ALOGE("Failed to open emulator storage file to save authenticator id"); + return; + } + + rewind(fp); + + int na = fwrite(&authenid, sizeof(authenid), 1, fp); + if (na != 1) { + ALOGE("Failed while writing authenticator id in emulator storage"); + } + + ALOGD("Save authenticator id (0x%" PRIx64 ")", authenid); + + fclose(fp); +} + +static void loadAuthenticatorId(const char* authid_filename, uint64_t* pauthenid) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + FILE* fp = fopen(authid_filename, "r"); + if (fp == NULL) { + ALOGE("Could not load authenticator id from storage at %s; " + "it has not yet been created.", + authid_filename); + perror("Failed to open/create file"); + return; + } + + rewind(fp); + + int na = fread(pauthenid, sizeof(*pauthenid), 1, fp); + if (na != 1) + ALOGW("Corrupt emulator authenticator id storage (read %d)", na); + + ALOGD("Read authenticator id (0x%" PRIx64 ")", *pauthenid); + + fclose(fp); + + return; +} + +static void loadFingerprints(worker_thread_t* listener) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + FILE* fp = fopen(listener->fp_filename, "r"); + if (fp == NULL) { + ALOGE("Could not load fingerprints from storage at %s; " + "it has not yet been created.", + listener->fp_filename); + perror("Failed to open/create file"); + return; + } + + int ns = fread(listener->secureid, MAX_NUM_FINGERS * sizeof(uint64_t), 1, + fp); + int nf = fread(listener->fingerid, MAX_NUM_FINGERS * sizeof(uint64_t), 1, + fp); + if (ns != 1 || nf != 1) + ALOGW("Corrupt emulator fingerprints storage (read %d+%db)", ns, nf); + + int i = 0; + for (i = 0; i < MAX_NUM_FINGERS; i++) + ALOGD("Read fingerprint %d (0x%" PRIx64 ",0x%" PRIx64 ")", i, + listener->secureid[i], listener->fingerid[i]); + + fclose(fp); + + return; +} + +/******************************************************************************/ + +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* 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. + 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); + + ALOGD("----------------> %s auth id %" PRIx64 "----------------->", __FUNCTION__, authenticator_id); + return authenticator_id; +} + +static int fingerprint_set_active_group(struct fingerprint_device *device, uint32_t gid, + const char *path) { + ALOGD("----------------> %s -----------------> path %s", __FUNCTION__, path); + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + pthread_mutex_lock(&qdev->lock); + qdev->group_id = gid; + snprintf(qdev->listener.fp_filename, sizeof(qdev->listener.fp_filename), + "%s/%s", path, FINGERPRINT_FILENAME); + snprintf(qdev->listener.authid_filename, sizeof(qdev->listener.authid_filename), + "%s/%s", path, AUTHENTICATOR_ID_FILENAME); + uint64_t authenticator_id = 0; + loadFingerprints(&qdev->listener); + loadAuthenticatorId(qdev->listener.authid_filename, &authenticator_id); + if (authenticator_id == 0) { + // firs time, create an authenticator id + authenticator_id = get_64bit_rand(); + // save it to disk + saveAuthenticatorId(qdev->listener.authid_filename, authenticator_id); + } + + qdev->authenticator_id = authenticator_id; + pthread_mutex_unlock(&qdev->lock); + + return 0; +} + +/** + * If fingerprints are enrolled, then this function is expected to put the sensor into a + * "scanning" state where it's actively scanning and recognizing fingerprint features. + * 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 *device, + uint64_t operation_id, __unused uint32_t gid) +{ + 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); + + return 0; +} + +/** + * This is expected to put the sensor into an "enroll" state where it's actively scanning and + * working towards a finished fingerprint database entry. Authentication must happen in + * a separate thread since this function is expected to return immediately. + * + * Note: This method should always generate a new random authenticator_id. + * + * Note: As with fingerprint_authenticate(), this would run in TEE on a real device. + */ +static int fingerprint_enroll(struct fingerprint_device *device, + const hw_auth_token_t *hat, + uint32_t __unused gid, + uint32_t __unused timeout_sec) { + ALOGD("fingerprint_enroll"); + 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 auth token", __func__); + } + + if (hat->version != HW_AUTH_TOKEN_VERSION) { + return -EPROTONOSUPPORT; + } + if (hat->challenge != dev->challenge && !(hat->authenticator_type & HW_AUTH_FINGERPRINT)) { + return -EPERM; + } + + dev->user_id = hat->user_id; + + 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; + +} + +/** + * The pre-enrollment step is simply to get an authentication token that can be wrapped and + * verified at a later step. The primary purpose is to return a token that protects against + * spoofing and replay attacks. It is passed to password authentication where it is wrapped and + * propagated to the enroll step. + */ +static uint64_t fingerprint_pre_enroll(struct fingerprint_device *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. + 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 *device) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + + pthread_mutex_lock(&qdev->lock); + qdev->listener.state = STATE_IDLE; + pthread_mutex_unlock(&qdev->lock); + + fingerprint_msg_t msg = {0, {0}}; + msg.type = FINGERPRINT_ERROR; + msg.data.error = FINGERPRINT_ERROR_CANCELED; + qdev->device.notify(&msg); + + return 0; +} + +static int fingerprint_enumerate(struct fingerprint_device *device) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + if (device == NULL) { + ALOGE("Cannot enumerate saved fingerprints with uninitialized params"); + return -1; + } + + qemu_fingerprint_device_t* qdev = (qemu_fingerprint_device_t*)device; + int template_count = 0; + for (int i = 0; i < MAX_NUM_FINGERS; i++) { + if (qdev->listener.secureid[i] != 0 || + qdev->listener.fingerid[i] != 0) { + ALOGD("ENUM: Fingerprint [%d] = 0x%" PRIx64 ",%" PRIx64, i, + qdev->listener.secureid[i], qdev->listener.fingerid[i]); + template_count++; + } + } + fingerprint_msg_t message = {0, {0}}; + message.type = FINGERPRINT_TEMPLATE_ENUMERATING; + message.data.enumerated.finger.gid = qdev->group_id; + for (int i = 0; i < MAX_NUM_FINGERS; i++) { + if (qdev->listener.secureid[i] != 0 || + qdev->listener.fingerid[i] != 0) { + template_count--; + message.data.enumerated.remaining_templates = template_count; + message.data.enumerated.finger.fid = qdev->listener.fingerid[i]; + qdev->device.notify(&message); + } + } + + return 0; +} + +static int fingerprint_remove(struct fingerprint_device *device, + uint32_t __unused gid, uint32_t fid) { + int idx = 0; + fingerprint_msg_t msg = {0, {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.fingerid[idx]; + if (theFid != 0) { + // Delete this entry + qdev->listener.secureid[idx] = 0; + qdev->listener.fingerid[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); + msg.type = FINGERPRINT_TEMPLATE_REMOVED; + msg.data.removed.finger.fid = 0; + device->notify(&msg); + 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.fingerid[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.fingerid[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("----------------> %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 bool is_valid_fid(qemu_fingerprint_device_t* qdev, uint64_t fid) { + int idx = 0; + if (0 == fid) { return false; } + for (idx = 0; idx < MAX_NUM_FINGERS; idx++) { + if (qdev->listener.fingerid[idx] == fid) { + return true; + } + } + return false; +} + +static void send_scan_notice(qemu_fingerprint_device_t* qdev, int fid) { + ALOGD("----------------> %s ----------------->", __FUNCTION__); + + // acquired message + fingerprint_msg_t acqu_msg = {0, {0}}; + acqu_msg.type = FINGERPRINT_ACQUIRED; + acqu_msg.data.acquired.acquired_info = FINGERPRINT_ACQUIRED_GOOD; + + // authenticated message + fingerprint_msg_t auth_msg = {0, {0}}; + auth_msg.type = FINGERPRINT_AUTHENTICATED; + auth_msg.data.authenticated.finger.fid = is_valid_fid(qdev, fid) ? fid : 0; + 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.fingerid[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.fingerid[idx] = fid; + saveFingerprint(&qdev->listener, idx); + + qdev->listener.state = STATE_SCAN; + pthread_mutex_unlock(&qdev->lock); + + // LOCKED notification? + fingerprint_msg_t msg = {0, {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) { + ALOGV("----------------> %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"); + goto done_quiet; + } + + int comm_errors = 0; + struct pollfd pfd = { + .fd = qdev->qchanfd, + .events = POLLIN, + }; + while (1) { + int size = 0; + int fid = 0; + char buffer[MAX_COMM_CHARS] = {0}; + bool disconnected = false; + while (1) { + if (getListenerState(qdev) == STATE_EXIT) { + ALOGD("Received request to exit listener thread"); + goto done; + } + + // Reset revents before poll() (just to be safe) + pfd.revents = 0; + + // Poll qemud channel for 5 seconds + // TODO: Eliminate the timeout so that polling can be interrupted + // instantly. One possible solution is to follow the example of + // android::Looper ($AOSP/system/core/include/utils/Looper.h and + // $AOSP/system/core/libutils/Looper.cpp), which makes use of an + // additional file descriptor ("wake event fd"). + int nfds = poll(&pfd, 1, 5000); + if (nfds < 0) { + ALOGE("Could not poll qemud channel: %s", strerror(errno)); + goto done; + } + + if (!nfds) { + // poll() timed out - try again + continue; + } + + // assert(nfds == 1) + if (pfd.revents & POLLIN) { + // Input data being available doesn't rule out a disconnection + disconnected = pfd.revents & (POLLERR | POLLHUP); + break; // Exit inner while loop + } else { + // Some event(s) other than "input data available" occurred, + // i.e. POLLERR or POLLHUP, indicating a disconnection + ALOGW("Lost connection to qemud channel"); + goto done; + } + } + + // Shouldn't block since we were just notified of a POLLIN event + 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); + } + + if (disconnected) { + ALOGW("Connection to qemud channel has been lost"); + break; + } + } else { + ALOGE("fingerprint listener receive failure"); + if (comm_errors > MAX_COMM_ERRORS) + break; + } + } + +done: + ALOGD("Listener exit with %d receive errors", comm_errors); +done_quiet: + close(qdev->qchanfd); + 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); + // Ask listener thread to exit + 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; + } + + 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; + } + + + qdev->device.common.tag = HARDWARE_DEVICE_TAG; + qdev->device.common.version = HARDWARE_MODULE_API_VERSION(2, 1); + 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; +} + +static struct hw_module_methods_t fingerprint_module_methods = { + .open = fingerprint_open, +}; + +fingerprint_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = FINGERPRINT_MODULE_API_VERSION_2_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = FINGERPRINT_HARDWARE_MODULE_ID, + .name = "Emulator Fingerprint HAL", + .author = "The Android Open Source Project", + .methods = &fingerprint_module_methods, + }, +};