From 961fb8612c341bec457c4d3a46b1c345050915d6 Mon Sep 17 00:00:00 2001 From: Minggang Wang Date: Thu, 18 Feb 2016 14:24:29 +0800 Subject: [PATCH] [Fingerprint]Implement the fingerprint authentication on Android This patch implements the fingerprint, as a local authentication method. Developer can call the javascript interface: xwalk.experimental.fingerprint.authenticate(); to start the sensor to authenticate. Also, this patch adds fingerprint as a deploy target in .travis.yml. BUG=XWALK-6222 --- .travis.yml | 4 +- all.gyp | 1 + fingerprint/fingerprint.gyp | 26 ++++ fingerprint/fingerprint.js | 45 ++++++ fingerprint/fingerprint.json | 5 + .../extensions/FingerprintExtension.java | 137 ++++++++++++++++++ 6 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 fingerprint/fingerprint.gyp create mode 100644 fingerprint/fingerprint.js create mode 100644 fingerprint/fingerprint.json create mode 100644 fingerprint/src/org/xwalk/extensions/FingerprintExtension.java diff --git a/.travis.yml b/.travis.yml index bb89d31..c970521 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ language: android android: components: - build-tools-22.0.1 - - android-22 + - android-23 env: matrix: @@ -40,6 +40,7 @@ install: before_deploy: - zip -j iap.zip out/Default/gen/iap/iap.jar out/Default/gen/iap/iap.js out/Default/gen/iap/iap.json +- zip -j fingerprint.zip out/Default/gen/fingerprint/fingerprint.jar out/Default/gen/fingerprint/fingerprint.js out/Default/gen/fingerprint/fingerprint.json deploy: provider: releases @@ -57,6 +58,7 @@ script: - cd crosswalk-android-extensions - gyp --depth=. all.gyp - ninja -C out/Default pack_iap_jars +- ninja -C out/Default fingerprint notifications: email: diff --git a/all.gyp b/all.gyp index 218f823..2aed6ef 100644 --- a/all.gyp +++ b/all.gyp @@ -15,6 +15,7 @@ 'ad/ad.gyp:*', 'ardrone_pilot/ardrone_pilot.gyp:*', 'ardrone_video/ardrone_video.gyp:*', + 'fingerprint/fingerprint.gyp:*', 'iap/iap.gyp:*', 'idl_demo/idl_demo.gyp:*', ], diff --git a/fingerprint/fingerprint.gyp b/fingerprint/fingerprint.gyp new file mode 100644 index 0000000..c5d49de --- /dev/null +++ b/fingerprint/fingerprint.gyp @@ -0,0 +1,26 @@ +# Copyright (c) 2016 Intel Corporation. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes':[ + '../build/common.gypi', + ], + + 'targets': [ + { + 'target_name': 'fingerprint', + 'type': 'none', + 'variables': { + 'java_in_dir': '<(DEPTH)/fingerprint', + 'js_file': 'fingerprint.js', + 'json_file': 'fingerprint.json', + 'input_jars_paths': [ + '<(core_library_java_jar)', + '<(android_jar)', + ], + }, + 'includes': [ '../build/java.gypi' ], + }, + ], +} diff --git a/fingerprint/fingerprint.js b/fingerprint/fingerprint.js new file mode 100644 index 0000000..53e85e5 --- /dev/null +++ b/fingerprint/fingerprint.js @@ -0,0 +1,45 @@ +// Copyright (c) 2016 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var g_asyncRequests = {}; +var g_nextRequestId = 0; + +function AsyncRequest(resolve, reject) { + this.resolve = resolve; + this.reject = reject; +} + +function createAsyncRequest(resolve, reject) { + var requestId = ++g_nextRequestId; + var asyncRequest = new AsyncRequest(resolve, reject); + g_asyncRequests[requestId] = asyncRequest; + + return requestId; +} + +function sendAsycRequest(command, requestId, message) { + var message = { "cmd": command, "requestId": requestId, "data": message }; + extension.postMessage(JSON.stringify(message)); +} + +exports.authenticate = function(reason) { + return new Promise(function(resolve, reject) { + var requestId = createAsyncRequest(resolve, reject); + sendAsycRequest("authenticate", requestId, reason); + }); +} + +extension.setMessageListener(function(json) { + var msg = JSON.parse(json); + var request = g_asyncRequests[msg.requestId]; + + if (request) { + if (msg.error) { + request.reject.apply(null, [msg.error]); + } else { + request.resolve.apply(null, [msg.data]); + } + delete g_asyncRequests[msg.requestId]; + } +}); diff --git a/fingerprint/fingerprint.json b/fingerprint/fingerprint.json new file mode 100644 index 0000000..7f2f76e --- /dev/null +++ b/fingerprint/fingerprint.json @@ -0,0 +1,5 @@ +{ + "name": "xwalk.experimental.fingerprint", + "class": "org.xwalk.extensions.FingerprintExtension", + "jsapi": "fingerprint.js" +} diff --git a/fingerprint/src/org/xwalk/extensions/FingerprintExtension.java b/fingerprint/src/org/xwalk/extensions/FingerprintExtension.java new file mode 100644 index 0000000..e637b10 --- /dev/null +++ b/fingerprint/src/org/xwalk/extensions/FingerprintExtension.java @@ -0,0 +1,137 @@ +// Copyright (c) 2016 Intel Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.xwalk.extensions; + +import android.hardware.fingerprint.FingerprintManager; +import android.os.CancellationSignal; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +import org.xwalk.core.extension.XWalkExtensionContextClient; +import org.xwalk.core.extension.XWalkExternalExtension; + +public class FingerprintExtension extends XWalkExternalExtension { + private final FingerprintManager mFingerprintManager; + private CancellationSignal mCancellationSignal; + private boolean mAuthenticating = false; + private int mRequestId; + private int mInstanceId; + + // The names of errors. + public static final String OPERATION_ERROR = "OperationError"; + public static final String NOT_SUPPORT_ERROR = "NotSupportError"; + + // Command used by this extension. + private static final String CMD_AUTHENTICATE = "authenticate"; + + private static final String TAG = "Fingerprint"; + + public FingerprintExtension(String extensionName, String jsApi, XWalkExtensionContextClient context) { + super(extensionName, jsApi, context); + mFingerprintManager = context.getContext().getSystemService(FingerprintManager.class); + } + + public boolean isFingerprintAvailable() { + return mFingerprintManager.isHardwareDetected() && mFingerprintManager.hasEnrolledFingerprints(); + } + + @Override + public void onMessage(int instanceId, String message) { + try { + JSONObject object = new JSONObject(message); + + String cmd = object.getString("cmd"); + int requestId = object.getInt("requestId"); + + Log.e(TAG, "Receive command: " + cmd); + if (cmd.equals(CMD_AUTHENTICATE)) { + if (!isFingerprintAvailable()) { + postErrorMessage(instanceId, requestId, NOT_SUPPORT_ERROR); + return; + } + + if (mAuthenticating) { + Log.e(TAG, "Error: Authentication is already running."); + postErrorMessage(instanceId, requestId, OPERATION_ERROR); + return; + } + + mInstanceId = instanceId; + mRequestId = requestId; + mAuthenticating = true; + mCancellationSignal = new CancellationSignal(); + mFingerprintManager.authenticate(null, mCancellationSignal, 0, mAuthenticationCallback, null); + } else { + Log.e(TAG, "Unexpected command received: " + cmd); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + } + } + + protected void postErrorMessage(int instanceId, int requestId, String errorName) { + try { + JSONObject error = new JSONObject(); + error.put("name", errorName); + JSONObject object = new JSONObject(); + object.put("requestId", requestId); + object.put("error", error); + postMessage(instanceId, object.toString()); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + } + } + + protected void postSuccessMessage(int instanceId, int requestId) { + try { + JSONObject object = new JSONObject(); + object.put("requestId", mRequestId); + postMessage(mInstanceId, object.toString()); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + } + } + + protected void stopAuthenticating() { + if (mCancellationSignal != null) { + mCancellationSignal.cancel(); + mCancellationSignal = null; + } + mAuthenticating = false; + } + + private FingerprintManager.AuthenticationCallback mAuthenticationCallback = + new FingerprintManager.AuthenticationCallback() { + @Override + public void onAuthenticationError(int errorCode, CharSequence errString) { + postErrorMessage(mInstanceId, mRequestId, OPERATION_ERROR); + stopAuthenticating(); + Log.e(TAG, "Error: errorCode = " + errorCode); + } + + @Override + public void onAuthenticationFailed() { + postErrorMessage(mInstanceId, mRequestId, OPERATION_ERROR); + stopAuthenticating(); + Log.e(TAG, "Error: Authentication Failed."); + } + + @Override + public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { + postSuccessMessage(mInstanceId, mRequestId); + mAuthenticating = false; + Log.e(TAG, "Authentication Succeeded."); + } + + @Override + public void onAuthenticationHelp(int helpCode, CharSequence helpString) { + postErrorMessage(mInstanceId, mRequestId, OPERATION_ERROR); + stopAuthenticating(); + Log.e(TAG, "Error: errorCode = " + helpCode); + } + }; +}