blob: c3dc7877b0cd98ea2b8a64f25d66efc6a29752ae [file] [log] [blame]
/*
* 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.
*/
#define FINGERPRINT_LISTEN_SERVICE_NAME "fingerprintlisten"
#define FINGERPRINT_TXT_FILENAME "/data/fingerprint.txt"
#define LOG_TAG "FingerprintHal"
#define MAX_NUM_FINGERS 32
#include <errno.h>
#include <endian.h>
#include <inttypes.h>
#include <malloc.h>
#include <string.h>
#include <cutils/log.h>
#include <hardware/hardware.h>
#include <hardware/fingerprint.h>
#include <hardware/qemud.h>
typedef enum worker_state_t {
STATE_ENROLL = 1,
STATE_SCAN = 2,
STATE_IDLE = 3,
STATE_EXIT = 4
} 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;;
} worker_thread_t;
typedef struct emu_fingerprint_hal_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 authenticator_id;
pthread_mutex_t lock;
} emu_fingerprint_hal_device_t;
static uint64_t get_64bit_rand() {
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);
}
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;
}
}
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);
}
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)) {
dev->listener.all_fingerids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = dev->listener.fingerid;
dev->listener.all_secureids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = dev->secure_user_id;
dev->listener.all_authenids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = 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);
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 (is_valid_finger || is_authentication) {
dev->device.notify(&message);
}
pthread_mutex_unlock(&dev->lock);
}
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;
}
const char* cmd = "listen";
if (qemud_channel_send(fd, cmd, strlen(cmd)) < 0) {
ALOGE("cannot write fingerprint 'listen' to host");
return NULL;
}
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) {
dev->listener.all_fingerids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = fingerid;
dev->listener.all_secureids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = secureid;
dev->listener.all_authenids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = authenid;
++ dev->listener.num_fingers_enrolled;
} else {
break;
}
}
fclose(fp_stored);
}
dev->listener.fp_write = fopen(FINGERPRINT_TXT_FILENAME, "a");
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
}
ALOGD("listener exit");
return NULL;
}
static void createListenerThread(emu_fingerprint_hal_device_t* dev)
{
pthread_mutex_init(&dev->listener.mutex, NULL);
pthread_create(&dev->listener.thread, NULL, listenerFunction, dev);
}
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 fingerprint_get_auth_id(struct fingerprint_device __unused *device) {
emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device;
return dev->authenticator_id;
}
static int fingerprint_set_active_group(struct fingerprint_device __unused *device, uint32_t gid,
const char *path) {
// TODO: implements me
return 0;
}
static int fingerprint_authenticate(struct fingerprint_device __unused *device,
uint64_t __unused operation_id, __unused uint32_t gid)
{
ALOGD("fingerprint_authenticate");
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;
}
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");
emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device;
if (hat && hat->challenge == dev->challenge) {
dev->secure_user_id = hat->user_id;
} else {
ALOGW("%s: invalid or null 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;
// TODO: store enrolled fingerprints, authenticator id, and secure_user_id
setListenerState(dev, STATE_ENROLL);
return 0;
}
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;
dev->challenge = get_64bit_rand();
return dev->challenge;
}
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);
return 0;
}
static int fingerprint_enumerate(struct fingerprint_device *device,
fingerprint_finger_id_t *results, uint32_t *max_size) {
// TODO: implement me
return 0;
}
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 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);
device->notify = notify;
pthread_mutex_unlock(&dev->lock);
return 0;
}
static int fingerprint_open(const hw_module_t* module, const char __unused *id,
hw_device_t** device)
{
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;
dev->authenticator_id = 0xdeadbeef;
pthread_mutex_init(&dev->lock, NULL);
createListenerThread(dev);
*device = (hw_device_t*) dev;
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_0,
.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,
},
};