1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "Fingerprint-JNI"
18
19#include "JNIHelp.h"
20#include <inttypes.h>
21
22#include <android_runtime/AndroidRuntime.h>
23#include <android_runtime/Log.h>
24#include <android_os_MessageQueue.h>
25#include <binder/IServiceManager.h>
26#include <utils/String16.h>
27#include <utils/Looper.h>
28#include <keystore/IKeystoreService.h>
29#include <keystore/keystore.h> // for error code
30
31#include <hardware/hardware.h>
32#include <hardware/fingerprint.h>
33#include <hardware/hw_auth_token.h>
34
35#include <utils/Log.h>
36#include "core_jni_helpers.h"
37
38
39namespace android {
40
41static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0);
42
43static const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService";
44static struct {
45    jclass clazz;
46    jmethodID notify;
47} gFingerprintServiceClassInfo;
48
49static struct {
50    fingerprint_module_t const* module;
51    fingerprint_device_t *device;
52} gContext;
53
54static sp<Looper> gLooper;
55static jobject gCallback;
56
57class CallbackHandler : public MessageHandler {
58    int type;
59    int arg1, arg2, arg3;
60public:
61    CallbackHandler(int type, int arg1, int arg2, int arg3)
62        : type(type), arg1(arg1), arg2(arg2), arg3(arg3) { }
63
64    virtual void handleMessage(const Message& message) {
65        //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2);
66        JNIEnv* env = AndroidRuntime::getJNIEnv();
67        env->CallVoidMethod(gCallback, gFingerprintServiceClassInfo.notify, type, arg1, arg2, arg3);
68    }
69};
70
71static void notifyKeystore(uint8_t *auth_token, size_t auth_token_length) {
72    if (auth_token != NULL && auth_token_length > 0) {
73        // TODO: cache service?
74        sp<IServiceManager> sm = defaultServiceManager();
75        sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
76        sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
77        if (service != NULL) {
78            status_t ret = service->addAuthToken(auth_token, auth_token_length);
79            if (ret != ResponseCode::NO_ERROR) {
80                ALOGE("Falure sending auth token to KeyStore: %d", ret);
81            }
82        } else {
83            ALOGE("Unable to communicate with KeyStore");
84        }
85    }
86}
87
88// Called by the HAL to notify us of fingerprint events
89static void hal_notify_callback(fingerprint_msg_t msg) {
90    uint32_t arg1 = 0;
91    uint32_t arg2 = 0;
92    uint32_t arg3 = 0;
93    switch (msg.type) {
94        case FINGERPRINT_ERROR:
95            arg1 = msg.data.error;
96            break;
97        case FINGERPRINT_ACQUIRED:
98            arg1 = msg.data.acquired.acquired_info;
99            break;
100        case FINGERPRINT_AUTHENTICATED:
101            arg1 = msg.data.authenticated.finger.fid;
102            arg2 = msg.data.authenticated.finger.gid;
103            if (arg1 != 0) {
104                notifyKeystore(reinterpret_cast<uint8_t *>(&msg.data.authenticated.hat),
105                        sizeof(msg.data.authenticated.hat));
106            }
107            break;
108        case FINGERPRINT_TEMPLATE_ENROLLING:
109            arg1 = msg.data.enroll.finger.fid;
110            arg2 = msg.data.enroll.finger.gid;
111            arg3 = msg.data.enroll.samples_remaining;
112            break;
113        case FINGERPRINT_TEMPLATE_REMOVED:
114            arg1 = msg.data.removed.finger.fid;
115            arg2 = msg.data.removed.finger.gid;
116            break;
117        default:
118            ALOGE("fingerprint: invalid msg: %d", msg.type);
119            return;
120    }
121    // This call potentially comes in on a thread not owned by us. Hand it off to our
122    // looper so it runs on our thread when calling back to FingerprintService.
123    // CallbackHandler object is reference-counted, so no cleanup necessary.
124    gLooper->sendMessage(new CallbackHandler(msg.type, arg1, arg2, arg3), Message());
125}
126
127static void nativeInit(JNIEnv *env, jobject clazz, jobject mQueue, jobject callbackObj) {
128    ALOG(LOG_VERBOSE, LOG_TAG, "nativeInit()\n");
129    gCallback = MakeGlobalRefOrDie(env, callbackObj);
130    gLooper = android_os_MessageQueue_getMessageQueue(env, mQueue)->getLooper();
131}
132
133static jint nativeEnroll(JNIEnv* env, jobject clazz, jbyteArray token, jint groupId, jint timeout) {
134    ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll(gid=%d, timeout=%d)\n", groupId, timeout);
135    const int tokenSize = env->GetArrayLength(token);
136    jbyte* tokenData = env->GetByteArrayElements(token, 0);
137    if (tokenSize != sizeof(hw_auth_token_t)) {
138        ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll() : invalid token size %d\n", tokenSize);
139        return -1;
140    }
141    int ret = gContext.device->enroll(gContext.device,
142            reinterpret_cast<const hw_auth_token_t*>(tokenData), groupId, timeout);
143    env->ReleaseByteArrayElements(token, tokenData, 0);
144    return reinterpret_cast<jint>(ret);
145}
146
147static jlong nativePreEnroll(JNIEnv* env, jobject clazz) {
148    uint64_t ret = gContext.device->pre_enroll(gContext.device);
149    // ALOG(LOG_VERBOSE, LOG_TAG, "nativePreEnroll(), result = %llx", ret);
150    return reinterpret_cast<jlong>((int64_t)ret);
151}
152
153static jint nativeStopEnrollment(JNIEnv* env, jobject clazz) {
154    ALOG(LOG_VERBOSE, LOG_TAG, "nativeStopEnrollment()\n");
155    int ret = gContext.device->cancel(gContext.device);
156    return reinterpret_cast<jint>(ret);
157}
158
159static jint nativeAuthenticate(JNIEnv* env, jobject clazz, jlong sessionId, jint groupId) {
160    ALOG(LOG_VERBOSE, LOG_TAG, "nativeAuthenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId);
161    int ret = gContext.device->authenticate(gContext.device, sessionId, groupId);
162    return reinterpret_cast<jint>(ret);
163}
164
165static jint nativeStopAuthentication(JNIEnv* env, jobject clazz) {
166    ALOG(LOG_VERBOSE, LOG_TAG, "nativeStopAuthentication()\n");
167    int ret = gContext.device->cancel(gContext.device);
168    return reinterpret_cast<jint>(ret);
169}
170
171static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerId, jint groupId) {
172    ALOG(LOG_VERBOSE, LOG_TAG, "nativeRemove(fid=%d, gid=%d)\n", fingerId, groupId);
173    fingerprint_finger_id_t finger;
174    finger.fid = fingerId;
175    finger.gid = groupId;
176    int ret = gContext.device->remove(gContext.device, finger);
177    return reinterpret_cast<jint>(ret);
178}
179
180static jlong nativeGetAuthenticatorId(JNIEnv *, jobject clazz) {
181    return gContext.device->get_authenticator_id(gContext.device);
182}
183
184static jint nativeSetActiveGroup(JNIEnv *env, jobject clazz, jint gid, jbyteArray path) {
185    const int pathSize = env->GetArrayLength(path);
186    jbyte* pathData = env->GetByteArrayElements(path, 0);
187    if (pathSize >= PATH_MAX) {
188	ALOGE("Path name is too long\n");
189        return -1;
190    }
191    char path_name[PATH_MAX] = {0};
192    memcpy(path_name, pathData, pathSize);
193    ALOG(LOG_VERBOSE, LOG_TAG, "nativeSetActiveGroup() path: %s, gid: %d\n", path_name, gid);
194    int result = gContext.device->set_active_group(gContext.device, gid, path_name);
195    env->ReleaseByteArrayElements(path, pathData, 0);
196    return result;
197}
198
199static jint nativeOpenHal(JNIEnv* env, jobject clazz) {
200    ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
201    int err;
202    const hw_module_t *hw_module = NULL;
203    if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
204        ALOGE("Can't open fingerprint HW Module, error: %d", err);
205        return 0;
206    }
207    if (NULL == hw_module) {
208        ALOGE("No valid fingerprint module");
209        return 0;
210    }
211
212    gContext.module = reinterpret_cast<const fingerprint_module_t*>(hw_module);
213
214    if (gContext.module->common.methods->open == NULL) {
215        ALOGE("No valid open method");
216        return 0;
217    }
218
219    hw_device_t *device = NULL;
220
221    if (0 != (err = gContext.module->common.methods->open(hw_module, NULL, &device))) {
222        ALOGE("Can't open fingerprint methods, error: %d", err);
223        return 0;
224    }
225
226    if (kVersion != device->version) {
227        ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
228        // return 0; // FIXME
229    }
230
231    gContext.device = reinterpret_cast<fingerprint_device_t*>(device);
232    err = gContext.device->set_notify(gContext.device, hal_notify_callback);
233    if (err < 0) {
234        ALOGE("Failed in call to set_notify(), err=%d", err);
235        return 0;
236    }
237
238    // Sanity check - remove
239    if (gContext.device->notify != hal_notify_callback) {
240        ALOGE("NOTIFY not set properly: %p != %p", gContext.device->notify, hal_notify_callback);
241    }
242
243    ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
244    return reinterpret_cast<jlong>(gContext.device);
245}
246
247static jint nativeCloseHal(JNIEnv* env, jobject clazz) {
248    return -ENOSYS; // TODO
249}
250
251
252// ----------------------------------------------------------------------------
253
254
255// TODO: clean up void methods
256static const JNINativeMethod g_methods[] = {
257    { "nativeAuthenticate", "(JI)I", (void*)nativeAuthenticate },
258    { "nativeStopAuthentication", "()I", (void*)nativeStopAuthentication },
259    { "nativeEnroll", "([BII)I", (void*)nativeEnroll },
260    { "nativeSetActiveGroup", "(I[B)I", (void*)nativeSetActiveGroup },
261    { "nativePreEnroll", "()J", (void*)nativePreEnroll },
262    { "nativeStopEnrollment", "()I", (void*)nativeStopEnrollment },
263    { "nativeRemove", "(II)I", (void*)nativeRemove },
264    { "nativeGetAuthenticatorId", "()J", (void*)nativeGetAuthenticatorId },
265    { "nativeOpenHal", "()I", (void*)nativeOpenHal },
266    { "nativeCloseHal", "()I", (void*)nativeCloseHal },
267    { "nativeInit","(Landroid/os/MessageQueue;"
268            "Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit }
269};
270
271int register_android_server_fingerprint_FingerprintService(JNIEnv* env) {
272    jclass clazz = FindClassOrDie(env, FINGERPRINT_SERVICE);
273    gFingerprintServiceClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
274    gFingerprintServiceClassInfo.notify =
275            GetMethodIDOrDie(env, gFingerprintServiceClassInfo.clazz,"notify", "(IIII)V");
276    int result = RegisterMethodsOrDie(env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods));
277    ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n");
278    return result;
279}
280
281} // namespace android
282