/* * Copyright (C) 2014 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 LOG_TAG "Fingerprint-JNI" #include "JNIHelp.h" #include #include #include #include #include #include #include #include #include // for error code #include #include #include #include #include "core_jni_helpers.h" namespace android { static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0); static const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService"; static struct { jclass clazz; jmethodID notify; } gFingerprintServiceClassInfo; static struct { fingerprint_module_t const* module; fingerprint_device_t *device; } gContext; static sp gLooper; static jobject gCallback; class CallbackHandler : public MessageHandler { int type; int arg1, arg2, arg3; public: CallbackHandler(int type, int arg1, int arg2, int arg3) : type(type), arg1(arg1), arg2(arg2), arg3(arg3) { } virtual void handleMessage(const Message& message) { //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2); JNIEnv* env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod(gCallback, gFingerprintServiceClassInfo.notify, type, arg1, arg2, arg3); } }; static void notifyKeystore(uint8_t *auth_token, size_t auth_token_length) { if (auth_token != NULL && auth_token_length > 0) { // TODO: cache service? sp sm = defaultServiceManager(); sp binder = sm->getService(String16("android.security.keystore")); sp service = interface_cast(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"); } } } // 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; switch (msg.type) { case FINGERPRINT_ERROR: arg1 = msg.data.error; break; case FINGERPRINT_ACQUIRED: arg1 = msg.data.acquired.acquired_info; break; case FINGERPRINT_AUTHENTICATED: arg1 = msg.data.authenticated.finger.fid; arg2 = msg.data.authenticated.finger.gid; if (arg1 != 0) { notifyKeystore(reinterpret_cast(&msg.data.authenticated.hat), sizeof(msg.data.authenticated.hat)); } break; case FINGERPRINT_TEMPLATE_ENROLLING: arg1 = msg.data.enroll.finger.fid; arg2 = msg.data.enroll.finger.gid; arg3 = msg.data.enroll.samples_remaining; break; case FINGERPRINT_TEMPLATE_REMOVED: arg1 = msg.data.removed.finger.fid; arg2 = msg.data.removed.finger.gid; break; default: ALOGE("fingerprint: invalid msg: %d", msg.type); return; } // This call potentially comes in on a thread not owned by us. Hand it off to our // looper so it runs on our thread when calling back to FingerprintService. // CallbackHandler object is reference-counted, so no cleanup necessary. gLooper->sendMessage(new CallbackHandler(msg.type, arg1, arg2, arg3), Message()); } static void nativeInit(JNIEnv *env, jobject clazz, jobject mQueue, jobject callbackObj) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeInit()\n"); gCallback = MakeGlobalRefOrDie(env, callbackObj); gLooper = android_os_MessageQueue_getMessageQueue(env, mQueue)->getLooper(); } static jint nativeEnroll(JNIEnv* env, jobject clazz, jbyteArray token, jint groupId, jint timeout) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll(gid=%d, timeout=%d)\n", groupId, timeout); const int tokenSize = env->GetArrayLength(token); jbyte* tokenData = env->GetByteArrayElements(token, 0); if (tokenSize != sizeof(hw_auth_token_t)) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll() : invalid token size %d\n", tokenSize); return -1; } int ret = gContext.device->enroll(gContext.device, reinterpret_cast(tokenData), groupId, timeout); env->ReleaseByteArrayElements(token, tokenData, 0); return reinterpret_cast(ret); } static jlong nativePreEnroll(JNIEnv* env, jobject clazz) { uint64_t ret = gContext.device->pre_enroll(gContext.device); // ALOG(LOG_VERBOSE, LOG_TAG, "nativePreEnroll(), result = %llx", ret); return reinterpret_cast((int64_t)ret); } static jint nativeStopEnrollment(JNIEnv* env, jobject clazz) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeStopEnrollment()\n"); int ret = gContext.device->cancel(gContext.device); return reinterpret_cast(ret); } static jint nativeAuthenticate(JNIEnv* env, jobject clazz, jlong sessionId, jint groupId) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeAuthenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId); int ret = gContext.device->authenticate(gContext.device, sessionId, groupId); return reinterpret_cast(ret); } static jint nativeStopAuthentication(JNIEnv* env, jobject clazz) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeStopAuthentication()\n"); int ret = gContext.device->cancel(gContext.device); return reinterpret_cast(ret); } static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerId, jint groupId) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeRemove(fid=%d, gid=%d)\n", fingerId, groupId); fingerprint_finger_id_t finger; finger.fid = fingerId; finger.gid = groupId; int ret = gContext.device->remove(gContext.device, finger); return reinterpret_cast(ret); } static jlong nativeGetAuthenticatorId(JNIEnv *, jobject clazz) { return gContext.device->get_authenticator_id(gContext.device); } static jint nativeSetActiveGroup(JNIEnv *env, jobject clazz, jint gid, jbyteArray path) { const int pathSize = env->GetArrayLength(path); jbyte* pathData = env->GetByteArrayElements(path, 0); if (pathSize >= PATH_MAX) { ALOGE("Path name is too long\n"); return -1; } char path_name[PATH_MAX] = {0}; memcpy(path_name, pathData, pathSize); ALOG(LOG_VERBOSE, LOG_TAG, "nativeSetActiveGroup() path: %s, gid: %d\n", path_name, gid); int result = gContext.device->set_active_group(gContext.device, gid, path_name); env->ReleaseByteArrayElements(path, pathData, 0); return result; } 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(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(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(gContext.device); } static jint nativeCloseHal(JNIEnv* env, jobject clazz) { return -ENOSYS; // TODO } // ---------------------------------------------------------------------------- // TODO: clean up void methods static const JNINativeMethod g_methods[] = { { "nativeAuthenticate", "(JI)I", (void*)nativeAuthenticate }, { "nativeStopAuthentication", "()I", (void*)nativeStopAuthentication }, { "nativeEnroll", "([BII)I", (void*)nativeEnroll }, { "nativeSetActiveGroup", "(I[B)I", (void*)nativeSetActiveGroup }, { "nativePreEnroll", "()J", (void*)nativePreEnroll }, { "nativeStopEnrollment", "()I", (void*)nativeStopEnrollment }, { "nativeRemove", "(II)I", (void*)nativeRemove }, { "nativeGetAuthenticatorId", "()J", (void*)nativeGetAuthenticatorId }, { "nativeOpenHal", "()I", (void*)nativeOpenHal }, { "nativeCloseHal", "()I", (void*)nativeCloseHal }, { "nativeInit","(Landroid/os/MessageQueue;" "Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit } }; int register_android_server_fingerprint_FingerprintService(JNIEnv* env) { jclass clazz = FindClassOrDie(env, FINGERPRINT_SERVICE); gFingerprintServiceClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); gFingerprintServiceClassInfo.notify = GetMethodIDOrDie(env, gFingerprintServiceClassInfo.clazz,"notify", "(IIII)V"); int result = RegisterMethodsOrDie(env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods)); ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n"); return result; } } // namespace android