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 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;
    }

    fpc1020_device_t *dev = new fpc1020_device_t;
    if (!dev) {
        return -ENOMEM;
    }

    dev->impl = new Fpc1020Sensor(fingerprint_cb_acquired,
            fingerprint_cb_enrollment_progress,
            fingerprint_cb_authenticate,
            fingerprint_cb_error, dev);
    if (!dev->impl) {
        delete dev;
        return -ENOMEM;
    }

    dev->device.common.tag = HARDWARE_DEVICE_TAG;
    dev->device.common.version = HARDWARE_MODULE_API_VERSION(1, 2);
    dev->device.common.module = (struct hw_module_t*) module;
    dev->device.common.close = fingerprint_close;

    dev->device.authenticate = fingerprint_authenticate;
    dev->device.cancel = fingerprint_cancel;
    dev->device.enroll = fingerprint_enroll;
    dev->device.remove = fingerprint_remove;
    dev->device.set_notify = set_notify_callback;
    dev->device.notify = NULL;
    dev->device.get_enrollment_info = fingerprint_get_enrollment_info;
    dev->device.release_enrollment_info = fingerprint_release_enrollment_info;
    dev->device.get_num_enrollment_steps = fingerprint_get_num_enrollment_steps;
    dev->device.set_parameters = fingerprint_set_parameters;

    *device = (hw_device_t *) &dev->device;

    return 0;
}
namespace android {

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

static const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService";
static struct {
    jclass clazz;
    jmethodID notify;
    jobject callbackObject;
} gFingerprintServiceClassInfo;

static struct {
    fingerprint_module_t const* module;
    fingerprint_device_t *device;
} gContext;

// Called by the HAL to notify us of fingerprint events
static void hal_notify_callback(fingerprint_msg_t msg) {
    uint32_t arg1 = 0;
    uint32_t arg2 = 0;
    uint32_t arg3 = 0; // TODO
    switch (msg.type) {
        case FINGERPRINT_ERROR:
            arg1 = msg.data.error;
            break;
        case FINGERPRINT_ACQUIRED:
            arg1 = msg.data.acquired.acquired_info;
            break;
        case FINGERPRINT_PROCESSED:
            arg1 = msg.data.processed.id;
            break;
        case FINGERPRINT_TEMPLATE_ENROLLING:
            arg1 = msg.data.enroll.id;
            arg2 = msg.data.enroll.samples_remaining;
            arg3 = msg.data.enroll.data_collected_bmp;
            break;
        case FINGERPRINT_TEMPLATE_REMOVED:
            arg1 = msg.data.removed.id;
            break;
        default:
            ALOGE("fingerprint: invalid msg: %d", msg.type);
            return;
    }
    (void)arg3;
    //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2);

	// TODO: fix gross hack to attach JNI to calling thread
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    if (env == NULL) {
        JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
        JavaVM* vm = AndroidRuntime::getJavaVM();
        int result = vm->AttachCurrentThread(&env, (void*) &args);
        if (result != JNI_OK) {
            ALOGE("Can't call JNI method: attach failed: %#x", result);
            return;
        }
    }
    env->CallVoidMethod(gFingerprintServiceClassInfo.callbackObject,
            gFingerprintServiceClassInfo.notify, msg.type, arg1, arg2);
}

static void nativeInit(JNIEnv *env, jobject clazz, jobject callbackObj) {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeInit()\n");
    FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
    GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz,
           "notify", "(III)V");
    gFingerprintServiceClassInfo.callbackObject = env->NewGlobalRef(callbackObj);
}

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

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

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

static jint nativeOpenHal(JNIEnv* env, jobject clazz) {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
    int err;
    const hw_module_t *hw_module = NULL;
    if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
        ALOGE("Can't open fingerprint HW Module, error: %d", err);
        return 0;
    }
    if (NULL == hw_module) {
        ALOGE("No valid fingerprint module");
        return 0;
    }

    gContext.module = reinterpret_cast<const fingerprint_module_t*>(hw_module);

    if (gContext.module->common.methods->open == NULL) {
        ALOGE("No valid open method");
        return 0;
    }

    hw_device_t *device = NULL;

    if (0 != (err = gContext.module->common.methods->open(hw_module, NULL, &device))) {
        ALOGE("Can't open fingerprint methods, error: %d", err);
        return 0;
    }

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

    gContext.device = reinterpret_cast<fingerprint_device_t*>(device);
    err = gContext.device->set_notify(gContext.device, hal_notify_callback);
    if (err < 0) {
        ALOGE("Failed in call to set_notify(), err=%d", err);
        return 0;
    }

    // Sanity check - remove
    if (gContext.device->notify != hal_notify_callback) {
        ALOGE("NOTIFY not set properly: %p != %p", gContext.device->notify, hal_notify_callback);
    }

    ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
    return reinterpret_cast<jlong>(gContext.device);
}

static jint nativeCloseHal(JNIEnv* env, jobject clazz) {
    return -ENOSYS; // TODO
}

// ----------------------------------------------------------------------------

// TODO: clean up void methods
static const JNINativeMethod g_methods[] = {
    { "nativeEnroll", "(I)I", (void*)nativeEnroll },
    { "nativeEnrollCancel", "()I", (void*)nativeEnrollCancel },
    { "nativeRemove", "(I)I", (void*)nativeRemove },
    { "nativeOpenHal", "()I", (void*)nativeOpenHal },
    { "nativeCloseHal", "()I", (void*)nativeCloseHal },
    { "nativeInit", "(Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit }
};

int register_android_server_fingerprint_FingerprintService(JNIEnv* env) {
    FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
    GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz, "notify",
            "(III)V");
    int result = AndroidRuntime::registerNativeMethods(
        env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods));
    ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n");
    return result;
}

} // namespace android
namespace android {

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

static const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService";
static struct {
    jclass clazz;
    jmethodID notify;
    jobject callbackObject;
} gFingerprintServiceClassInfo;

static struct {
    fingerprint_module_t const* module;
    fingerprint_device_t *device;
} gContext;

// Called by the HAL to notify us of fingerprint events
static void hal_notify_callback(fingerprint_msg_t msg) {
    uint32_t arg1 = 0;
    uint32_t arg2 = 0;
    uint32_t arg3 = 0; // TODO
    switch (msg.type) {
        case FINGERPRINT_ERROR:
            arg1 = msg.data.error;
            break;
        case FINGERPRINT_ACQUIRED:
            arg1 = msg.data.acquired.acquired_info;
            break;
        case FINGERPRINT_PROCESSED:
            arg1 = msg.data.processed.id;
            break;
        case FINGERPRINT_TEMPLATE_ENROLLING:
            arg1 = msg.data.enroll.id;
            arg2 = msg.data.enroll.samples_remaining;
            arg3 = msg.data.enroll.data_collected_bmp;
            break;
        case FINGERPRINT_TEMPLATE_REMOVED:
            arg1 = msg.data.removed.id;
            break;
        default:
            ALOGE("fingerprint: invalid msg: %d", msg.type);
            return;
    }
    //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2);

	// TODO: fix gross hack to attach JNI to calling thread
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    JavaVM* vm = NULL;
    if (env == NULL) {
        JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
        vm = AndroidRuntime::getJavaVM();
        int result = vm->AttachCurrentThread(&env, (void*) &args);
        if (result != JNI_OK) {
            ALOGE("Can't call JNI method: attach failed: %#x", result);
            return;
        }
    }
    env->CallVoidMethod(gFingerprintServiceClassInfo.callbackObject,
            gFingerprintServiceClassInfo.notify, msg.type, arg1, arg2);
    if (vm != NULL) {
        vm->DetachCurrentThread();
    }
}

static void nativeInit(JNIEnv *env, jobject clazz, jobject callbackObj) {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeInit()\n");
    FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
    GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz,
           "notify", "(III)V");
    gFingerprintServiceClassInfo.callbackObject = env->NewGlobalRef(callbackObj);
}

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

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

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

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

static jobject nativeGetEnrollments(JNIEnv* env) {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeGetEnrollMents()\n");

    // Get Enrollment info from the HAL
    enrollment_info_t *enrollmentInfo;
    int success = gContext.device->get_enrollment_info(gContext.device, &enrollmentInfo);
    if (success != 0) {
       ALOG(LOG_VERBOSE, LOG_TAG, "nativeGetEnrollments(): failed to get info\n");
       return NULL;
    }

    // Create return object
    jclass clazz = env->FindClass("android/hardware/fingerprint/Fingerprint");
    jobjectArray jFingerprintObjArray = env->NewObjectArray(enrollmentInfo->num_fingers, clazz, NULL);

    // Populate the array with enrolled Fingerprint objects
    for(int i = 0; i < enrollmentInfo->num_fingers; i++) {
        fingerprint_t* fp = &(enrollmentInfo->fpinfo[i]);

        // Create the fingerprint object
        jmethodID constructorMethod = env->GetMethodID(clazz, "<init>", "()V");
        jobject jFingerprintObj = env->NewObject(clazz, constructorMethod);

        // Set the fields
        jfieldID indexField = env->GetFieldID(clazz, "mFingerId", "I");
        jint jindex = fp->index;
        env->SetIntField(jFingerprintObj, indexField, jindex);

        env->SetObjectArrayElement(jFingerprintObjArray, i, jFingerprintObj);
    }

    // Only release on success
    if (success == 0) {
        gContext.device->release_enrollment_info(gContext.device, enrollmentInfo);
    }

    return jFingerprintObjArray;
}

static jint nativeGetNumEnrollmentSteps(JNIEnv* env) {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeGetNumEnrollMents()\n");
    return reinterpret_cast<jint>(gContext.device->get_num_enrollment_steps(gContext.device));
}

static jint nativeOpenHal(JNIEnv* env, jobject clazz) {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
    int err;
    const hw_module_t *hw_module = NULL;
    if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
        ALOGE("Can't open fingerprint HW Module, error: %d", err);
        return 0;
    }
    if (NULL == hw_module) {
        ALOGE("No valid fingerprint module");
        return 0;
    }

    gContext.module = reinterpret_cast<const fingerprint_module_t*>(hw_module);

    if (gContext.module->common.methods->open == NULL) {
        ALOGE("No valid open method");
        return 0;
    }

    hw_device_t *device = NULL;

    if (0 != (err = gContext.module->common.methods->open(hw_module, NULL, &device))) {
        ALOGE("Can't open fingerprint methods, error: %d", err);
        return 0;
    }

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

    gContext.device = reinterpret_cast<fingerprint_device_t*>(device);
    err = gContext.device->set_notify(gContext.device, hal_notify_callback);
    if (err < 0) {
        ALOGE("Failed in call to set_notify(), err=%d", err);
        return 0;
    }

    // Sanity check - remove
    if (gContext.device->notify != hal_notify_callback) {
        ALOGE("NOTIFY not set properly: %p != %p", gContext.device->notify, hal_notify_callback);
    }

    ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
    return reinterpret_cast<jlong>(gContext.device);
}

static jint nativeCloseHal(JNIEnv* env, jobject clazz) {
    return -ENOSYS; // TODO
}

// ----------------------------------------------------------------------------

// TODO: clean up void methods
static const JNINativeMethod g_methods[] = {
    { "nativeAuthenticate", "()I", (void*)nativeAuthenticate },
    { "nativeEnroll", "(I)I", (void*)nativeEnroll },
    { "nativeCancel", "()I", (void*)nativeCancel },
    { "nativeRemove", "(I)I", (void*)nativeRemove },
    { "nativeOpenHal", "()I", (void*)nativeOpenHal },
    { "nativeCloseHal", "()I", (void*)nativeCloseHal },
    { "nativeInit", "(Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit },
    { "nativeGetEnrollments", "()[Landroid/hardware/fingerprint/Fingerprint;", (void*)nativeGetEnrollments },
    { "nativeGetNumEnrollmentSteps", "()I", (void*)nativeGetNumEnrollmentSteps }
};

int register_android_server_fingerprint_FingerprintService(JNIEnv* env) {
    FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE);
    GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz, "notify",
            "(III)V");
    int result = AndroidRuntime::registerNativeMethods(
        env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods));
    ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n");
    return result;
}

} // namespace android
namespace android {

FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = NULL;

// Supported fingerprint HAL version
static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0);

FingerprintDaemonProxy::FingerprintDaemonProxy() : mModule(NULL), mDevice(NULL), mCallback(NULL) {

}

FingerprintDaemonProxy::~FingerprintDaemonProxy() {
    closeHal();
}

void FingerprintDaemonProxy::hal_notify_callback(const fingerprint_msg_t *msg) {
    FingerprintDaemonProxy* instance = FingerprintDaemonProxy::getInstance();
    const sp<IFingerprintDaemonCallback> callback = instance->mCallback;
    if (callback == NULL) {
        ALOGE("Invalid callback object");
        return;
    }
    const int64_t device = (int64_t) instance->mDevice;
    switch (msg->type) {
        case FINGERPRINT_ERROR:
            ALOGD("onError(%d)", msg->data.error);
            callback->onError(device, msg->data.error);
            break;
        case FINGERPRINT_ACQUIRED:
            ALOGD("onAcquired(%d)", msg->data.acquired.acquired_info);
            callback->onAcquired(device, msg->data.acquired.acquired_info);
            break;
        case FINGERPRINT_AUTHENTICATED:
            ALOGD("onAuthenticated(fid=%d, gid=%d)",
                    msg->data.authenticated.finger.fid,
                    msg->data.authenticated.finger.gid);
            if (msg->data.authenticated.finger.fid != 0) {
                const uint8_t* hat = reinterpret_cast<const uint8_t *>(&msg->data.authenticated.hat);
                instance->notifyKeystore(hat, sizeof(msg->data.authenticated.hat));
            }
            callback->onAuthenticated(device,
                    msg->data.authenticated.finger.fid,
                    msg->data.authenticated.finger.gid);
            break;
        case FINGERPRINT_TEMPLATE_ENROLLING:
            ALOGD("onEnrollResult(fid=%d, gid=%d, rem=%d)",
                    msg->data.enroll.finger.fid,
                    msg->data.enroll.finger.gid,
                    msg->data.enroll.samples_remaining);
            callback->onEnrollResult(device,
                    msg->data.enroll.finger.fid,
                    msg->data.enroll.finger.gid,
                    msg->data.enroll.samples_remaining);
            break;
        case FINGERPRINT_TEMPLATE_REMOVED:
            ALOGD("onRemove(fid=%d, gid=%d)",
                    msg->data.removed.finger.fid,
                    msg->data.removed.finger.gid);
            callback->onRemoved(device,
                    msg->data.removed.finger.fid,
                    msg->data.removed.finger.gid);
            break;
        default:
            ALOGE("invalid msg type: %d", msg->type);
            return;
    }
}

void FingerprintDaemonProxy::notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length) {
    if (auth_token != NULL && auth_token_length > 0) {
        // TODO: cache service?
        sp < IServiceManager > sm = defaultServiceManager();
        sp < IBinder > binder = sm->getService(String16("android.security.keystore"));
        sp < IKeystoreService > service = interface_cast < IKeystoreService > (binder);
        if (service != NULL) {
            status_t ret = service->addAuthToken(auth_token, auth_token_length);
            if (ret != ResponseCode::NO_ERROR) {
                ALOGE("Falure sending auth token to KeyStore: %d", ret);
            }
        } else {
            ALOGE("Unable to communicate with KeyStore");
        }
    }
}

void FingerprintDaemonProxy::init(const sp<IFingerprintDaemonCallback>& callback) {
    if (mCallback != NULL && IInterface::asBinder(callback) != IInterface::asBinder(mCallback)) {
        IInterface::asBinder(mCallback)->unlinkToDeath(this);
    }
    IInterface::asBinder(callback)->linkToDeath(this);
    mCallback = callback;
}

int32_t FingerprintDaemonProxy::enroll(const uint8_t* token, ssize_t tokenSize, int32_t groupId,
        int32_t timeout) {
    ALOG(LOG_VERBOSE, LOG_TAG, "enroll(gid=%d, timeout=%d)\n", groupId, timeout);
    if (tokenSize != sizeof(hw_auth_token_t) ) {
        ALOG(LOG_VERBOSE, LOG_TAG, "enroll() : invalid token size %zu\n", tokenSize);
        return -1;
    }
    const hw_auth_token_t* authToken = reinterpret_cast<const hw_auth_token_t*>(token);
    return mDevice->enroll(mDevice, authToken, groupId, timeout);
}

uint64_t FingerprintDaemonProxy::preEnroll() {
    return mDevice->pre_enroll(mDevice);
}

int32_t FingerprintDaemonProxy::postEnroll() {
    return mDevice->post_enroll(mDevice);
}

int32_t FingerprintDaemonProxy::stopEnrollment() {
    ALOG(LOG_VERBOSE, LOG_TAG, "stopEnrollment()\n");
    return mDevice->cancel(mDevice);
}

int32_t FingerprintDaemonProxy::authenticate(uint64_t sessionId, uint32_t groupId) {
    ALOG(LOG_VERBOSE, LOG_TAG, "authenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId);
    return mDevice->authenticate(mDevice, sessionId, groupId);
}

int32_t FingerprintDaemonProxy::stopAuthentication() {
    ALOG(LOG_VERBOSE, LOG_TAG, "stopAuthentication()\n");
    return mDevice->cancel(mDevice);
}

int32_t FingerprintDaemonProxy::remove(int32_t fingerId, int32_t groupId) {
    ALOG(LOG_VERBOSE, LOG_TAG, "remove(fid=%d, gid=%d)\n", fingerId, groupId);
    return mDevice->remove(mDevice, groupId, fingerId);
}

uint64_t FingerprintDaemonProxy::getAuthenticatorId() {
    return mDevice->get_authenticator_id(mDevice);
}

int32_t FingerprintDaemonProxy::setActiveGroup(int32_t groupId, const uint8_t* path,
        ssize_t pathlen) {
    if (pathlen >= PATH_MAX || pathlen <= 0) {
        ALOGE("Bad path length: %zd", pathlen);
        return -1;
    }
    // Convert to null-terminated string
    char path_name[PATH_MAX];
    memcpy(path_name, path, pathlen);
    path_name[pathlen] = '\0';
    ALOG(LOG_VERBOSE, LOG_TAG, "setActiveGroup(%d, %s, %zu)", groupId, path_name, pathlen);
    return mDevice->set_active_group(mDevice, groupId, path_name);
}

int64_t FingerprintDaemonProxy::openHal() {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
    int err;
    const hw_module_t *hw_module = NULL;
    if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
        ALOGE("Can't open fingerprint HW Module, error: %d", err);
        return 0;
    }
    if (NULL == hw_module) {
        ALOGE("No valid fingerprint module");
        return 0;
    }

    mModule = reinterpret_cast<const fingerprint_module_t*>(hw_module);

    if (mModule->common.methods->open == NULL) {
        ALOGE("No valid open method");
        return 0;
    }

    hw_device_t *device = NULL;

    if (0 != (err = mModule->common.methods->open(hw_module, NULL, &device))) {
        ALOGE("Can't open fingerprint methods, error: %d", err);
        return 0;
    }

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

    mDevice = reinterpret_cast<fingerprint_device_t*>(device);
    err = mDevice->set_notify(mDevice, hal_notify_callback);
    if (err < 0) {
        ALOGE("Failed in call to set_notify(), err=%d", err);
        return 0;
    }

    // Sanity check - remove
    if (mDevice->notify != hal_notify_callback) {
        ALOGE("NOTIFY not set properly: %p != %p", mDevice->notify, hal_notify_callback);
    }

    ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
    return reinterpret_cast<int64_t>(mDevice); // This is just a handle
}

int32_t FingerprintDaemonProxy::closeHal() {
    ALOG(LOG_VERBOSE, LOG_TAG, "nativeCloseHal()\n");
    if (mDevice == NULL) {
        ALOGE("No valid device");
        return -ENOSYS;
    }
    int err;
    if (0 != (err = mDevice->common.close(reinterpret_cast<hw_device_t*>(mDevice)))) {
        ALOGE("Can't close fingerprint module, error: %d", err);
        return err;
    }
    mDevice = NULL;
    return 0;
}

void FingerprintDaemonProxy::binderDied(const wp<IBinder>& who) {
    ALOGD("binder died");
    int err;
    if (0 != (err = closeHal())) {
        ALOGE("Can't close fingerprint device, error: %d", err);
    }
    if (IInterface::asBinder(mCallback) == who) {
        mCallback = NULL;
    }
}

}