Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6695)

Unified Diff: device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java

Issue 1486043002: [webnfc] Implement push method for Android nfc mojo service. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@step_6_add_mojo_service_CL
Patch Set: Move WindowAndroidChangedObserver to other observers in content layer Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
diff --git a/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java b/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..35a73205968cabea4f543f80833fec2eaa5fdd42
--- /dev/null
+++ b/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
@@ -0,0 +1,426 @@
+// Copyright 2016 The Chromium Authors. 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.chromium.device.nfc;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.nfc.FormatException;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.ReaderCallback;
+import android.nfc.NfcManager;
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.os.Build;
+import android.os.Process;
+
+import org.chromium.base.Log;
+import org.chromium.mojo.bindings.Callbacks;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojom.device.nfc.Nfc;
+import org.chromium.mojom.device.nfc.NfcClient;
+import org.chromium.mojom.device.nfc.NfcError;
+import org.chromium.mojom.device.nfc.NfcErrorType;
+import org.chromium.mojom.device.nfc.NfcMessage;
+import org.chromium.mojom.device.nfc.NfcPushOptions;
+import org.chromium.mojom.device.nfc.NfcPushTarget;
+import org.chromium.mojom.device.nfc.NfcWatchOptions;
+
+import java.io.IOException;
+
+/**
+ * Android implementation of the NFC mojo service defined in
+ * device/nfc/nfc.mojom.
+ */
+public class NfcImpl implements Nfc {
+ private static final String TAG = "NfcImpl";
+
+ /**
+ * Used to get instance of NFC adapter, @see android.nfc.NfcManager
+ */
+ private final NfcManager mNfcManager;
+
+ /**
+ * NFC adapter. @see android.nfc.NfcAdapter
+ */
+ private final NfcAdapter mNfcAdapter;
+
+ /**
+ * Activity that is in foreground and is used to enable / disable NFC reader mode operations.
+ * Can be updated when activity associated with web page is changed. @see #setActivity
+ */
+ private Activity mActivity;
+
+ /**
+ * Flag that indicates whether NFC permission is granted.
+ */
+ private final boolean mHasPermission;
+
+ /**
+ * Implementation of android.nfc.NfcAdapter.ReaderCallback. @see ReaderCallbackHandler
+ */
+ private ReaderCallbackHandler mReaderCallbackHandler;
+
+ /**
+ * Object that contains data that was passed to method
+ * #push(NfcMessage message, NfcPushOptions options, PushResponse callback)
+ * @see PendingPushOperation
+ */
+ private PendingPushOperation mPendingPushOperation;
+
+ /**
+ * Utility that provides I/O operations for a Tag. Created on demand when Tag is found.
+ * @see NfcTagHandler
+ */
+ private NfcTagHandler mTagHandler;
+
+ public NfcImpl(Context context) {
+ int permission =
+ context.checkPermission(Manifest.permission.NFC, Process.myPid(), Process.myUid());
+ mHasPermission = permission == PackageManager.PERMISSION_GRANTED;
+
+ if (!mHasPermission || Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ Log.w(TAG, "NFC operations are not permitted.");
+ mNfcAdapter = null;
+ mNfcManager = null;
+ } else {
+ mNfcManager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
+ if (mNfcManager == null) {
+ Log.w(TAG, "NFC is not supported.");
+ mNfcAdapter = null;
+ } else {
+ mNfcAdapter = mNfcManager.getDefaultAdapter();
+ }
+ }
+ }
+
+ /**
+ * Sets Activity that is used to enable / disable NFC reader mode. When Activity is set,
+ * reader mode is disabled for old Activity and enabled for the new Activity.
+ */
+ protected void setActivity(Activity activity) {
+ disableReaderMode();
+ mActivity = activity;
+ enableReaderMode();
+ }
+
+ /**
+ * Sets NfcClient. NfcClient interface is used to notify mojo NFC service client when NFC
+ * device is in proximity and has NfcMessage that matches NfcWatchOptions criteria.
+ * @see Nfc#watch(NfcWatchOptions options, WatchResponse callback)
+ *
+ * @param client @see NfcClient
+ */
+ @Override
+ public void setClient(NfcClient client) {
+ // TODO(crbug.com/625589): Should be implemented when watch() is implemented.
+ }
+
+ /**
+ * Pushes NfcMessage to Tag or Peer, whenever NFC device is in proximity. At the moment, only
+ * passive NFC devices are supported (NfcPushTarget.TAG).
+ *
+ * @param message that should be pushed to NFC device.
+ * @param options that contain information about timeout and target device type.
+ * @param callback that is used to notify when push operation is completed.
+ */
+ @Override
+ public void push(NfcMessage message, NfcPushOptions options, PushResponse callback) {
+ if (!checkIfReady(callback)) return;
+
+ if (options.target == NfcPushTarget.PEER) {
+ callback.call(createError(NfcErrorType.NOT_SUPPORTED));
+ return;
+ }
+
+ // If previous pending push operation is not completed, cancel it.
+ if (mPendingPushOperation != null) {
+ mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CANCELLED));
+ }
+
+ mPendingPushOperation = new PendingPushOperation(message, options, callback);
+ enableReaderMode();
+ processPendingPushOperation();
+ }
+
+ /**
+ * Cancels pending push operation.
+ * At the moment, only passive NFC devices are supported (NfcPushTarget.TAG).
+ *
+ * @param target @see NfcPushTarget
+ * @param callback that is used to notify caller when cancelPush() is completed.
+ */
+ @Override
+ public void cancelPush(int target, CancelPushResponse callback) {
+ if (!checkIfReady(callback)) return;
+
+ if (target == NfcPushTarget.PEER) {
+ callback.call(createError(NfcErrorType.NOT_SUPPORTED));
+ return;
+ }
+
+ if (mPendingPushOperation == null) {
+ callback.call(createError(NfcErrorType.NOT_FOUND));
+ } else {
+ mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CANCELLED));
+ mPendingPushOperation = null;
+ callback.call(null);
+ disableReaderMode();
+ }
+ }
+
+ /**
+ * Watch method allows to set filtering criteria for NfcMessages that are found when NFC device
+ * is within proximity. On success, watch ID is returned to caller through WatchResponse
+ * callback. When NfcMessage that matches NfcWatchOptions is found, it is passed to NfcClient
+ * interface together with corresponding watch ID.
+ * @see NfcClient#onWatch(int[] id, NfcMessage message)
+ *
+ * @param options used to filter NfcMessages, @see NfcWatchOptions.
+ * @param callback that is used to notify caller when watch() is completed and return watch ID.
+ */
+ @Override
+ public void watch(NfcWatchOptions options, WatchResponse callback) {
+ if (!checkIfReady(callback)) return;
+ // TODO(crbug.com/625589): Not implemented.
+ callback.call(0, createError(NfcErrorType.NOT_SUPPORTED));
+ }
+
+ /**
+ * Cancels NFC watch operation.
+ *
+ * @param id of watch operation.
+ * @param callback that is used to notify caller when cancelWatch() is completed.
+ */
+ @Override
+ public void cancelWatch(int id, CancelWatchResponse callback) {
+ if (!checkIfReady(callback)) return;
+ // TODO(crbug.com/625589): Not implemented.
+ callback.call(createError(NfcErrorType.NOT_SUPPORTED));
+ }
+
+ /**
+ * Cancels all NFC watch operations.
+ *
+ * @param callback that is used to notify caller when cancelAllWatches() is completed.
+ */
+ @Override
+ public void cancelAllWatches(CancelAllWatchesResponse callback) {
+ if (!checkIfReady(callback)) return;
+ // TODO(crbug.com/625589): Not implemented.
+ callback.call(createError(NfcErrorType.NOT_SUPPORTED));
+ }
+
+ /**
+ * Suspends all pending operations. Should be called when web page visibility is lost.
+ */
+ @Override
+ public void suspendNfcOperations() {
+ disableReaderMode();
+ }
+
+ /**
+ * Resumes all pending watch / push operations. Should be called when web page becomes visible.
+ */
+ @Override
+ public void resumeNfcOperations() {
+ enableReaderMode();
+ }
+
+ @Override
+ public void close() {
+ disableReaderMode();
+ }
+
+ @Override
+ public void onConnectionError(MojoException e) {
+ close();
+ }
+
+ /**
+ * Holds information about pending push operation.
+ */
+ private static class PendingPushOperation {
+ public final NfcMessage nfcMessage;
+ public final NfcPushOptions nfcPushOptions;
+ private final PushResponse mPushResponseCallback;
+
+ public PendingPushOperation(
+ NfcMessage message, NfcPushOptions options, PushResponse callback) {
+ nfcMessage = message;
+ nfcPushOptions = options;
+ mPushResponseCallback = callback;
+ }
+
+ /**
+ * Completes pending push operation.
+ *
+ * @param error should be null when operation is completed successfully, otherwise,
+ * error object with corresponding NfcErrorType should be provided.
+ */
+ public void complete(NfcError error) {
+ if (mPushResponseCallback != null) mPushResponseCallback.call(error);
+ }
+ }
+
+ /**
+ * Helper method that creates NfcError object from NfcErrorType.
+ */
+ private NfcError createError(int errorType) {
+ NfcError error = new NfcError();
+ error.errorType = errorType;
+ return error;
+ }
+
+ /**
+ * Checks if NFC funcionality can be used by the mojo service. If permission to use NFC is
+ * granted and hardware is enabled, returns null.
+ */
+ private NfcError checkIfReady() {
+ if (!mHasPermission || mActivity == null) {
+ return createError(NfcErrorType.SECURITY);
+ } else if (mNfcManager == null || mNfcAdapter == null) {
+ return createError(NfcErrorType.NOT_SUPPORTED);
+ } else if (!mNfcAdapter.isEnabled()) {
+ return createError(NfcErrorType.DEVICE_DISABLED);
+ }
+ return null;
+ }
+
+ /**
+ * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback with NfcError.
+ *
+ * @param WatchResponse Callback that is provided to watch() method.
+ * @return boolean true if NFC functionality can be used, false otherwise.
+ */
+ private boolean checkIfReady(WatchResponse callback) {
+ NfcError error = checkIfReady();
+ if (error == null) return true;
+
+ callback.call(0, error);
+ return false;
+ }
+
+ /**
+ * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback with NfcError.
+ *
+ * @param callback Generic callback that is provided to push(), cancelPush(),
+ * cancelWatch() and cancelAllWatches() methods.
+ * @return boolean true if NFC functionality can be used, false otherwise.
+ */
+ private boolean checkIfReady(Callbacks.Callback1<NfcError> callback) {
+ NfcError error = checkIfReady();
+ if (error == null) return true;
+
+ callback.call(error);
+ return false;
+ }
+
+ /**
+ * Implementation of android.nfc.NfcAdapter.ReaderCallback. Callback is called when NFC tag is
+ * discovered, Tag object is delegated to mojo service implementation method
+ * NfcImpl.onTagDiscovered().
+ */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static class ReaderCallbackHandler implements ReaderCallback {
+ private final NfcImpl mNfcImpl;
+
+ public ReaderCallbackHandler(NfcImpl impl) {
+ mNfcImpl = impl;
+ }
+
+ @Override
+ public void onTagDiscovered(Tag tag) {
+ mNfcImpl.onTagDiscovered(tag);
+ }
+ }
+
+ /**
+ * Enables reader mode, allowing NFC device to read / write NFC tags.
+ * @see android.nfc.NfcAdapter#enableReaderMode
+ */
+ private void enableReaderMode() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
+
+ if (mReaderCallbackHandler != null || mActivity == null) return;
+
+ // TODO(crbug.com/625589): Check if there are active watch operations.
+ if (mPendingPushOperation == null) return;
+
+ mReaderCallbackHandler = new ReaderCallbackHandler(this);
+ mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler,
+ NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B
+ | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_NFC_V,
+ null);
+ }
+
+ /**
+ * Disables reader mode.
+ * @see android.nfc.NfcAdapter#disableReaderMode
+ */
+ private void disableReaderMode() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
+
+ mReaderCallbackHandler = null;
+ if (mActivity != null) {
+ mNfcAdapter.disableReaderMode(mActivity);
+ }
+ }
+
+ /**
+ * Completes pending push operation. On error, invalidates #mTagHandler.
+ */
+ private void pendingPushOperationCompleted(NfcError error) {
+ if (mPendingPushOperation != null) {
+ mPendingPushOperation.complete(error);
+ mPendingPushOperation = null;
+
+ // TODO(crbug.com/625589): When nfc.watch is implemented, disable reader mode
+ // only when there are no active watch operations.
+ disableReaderMode();
+ }
+
+ if (error != null) mTagHandler = null;
+ }
+
+ /**
+ * Checks whether there is a #mPendingPushOperation and writes data to NFC tag. In case of
+ * exception calls pendingPushOperationCompleted() with appropriate error object.
+ */
+ private void processPendingPushOperation() {
+ if (mTagHandler == null || mPendingPushOperation == null) return;
+
+ if (mTagHandler.isTagOutOfRange()) {
+ mTagHandler = null;
+ return;
+ }
+
+ try {
+ mTagHandler.connect();
+ mTagHandler.write(NfcTypeConverter.toNdefMessage(mPendingPushOperation.nfcMessage));
+ pendingPushOperationCompleted(null);
+ mTagHandler.close();
+ } catch (InvalidNfcMessageException e) {
+ Log.w(TAG, "Cannot write data to NFC tag. Invalid NfcMessage.");
+ pendingPushOperationCompleted(createError(NfcErrorType.INVALID_MESSAGE));
+ } catch (TagLostException e) {
+ Log.w(TAG, "Cannot write data to NFC tag. Tag is lost.");
+ pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
+ } catch (FormatException | IOException e) {
+ Log.w(TAG, "Cannot write data to NFC tag. IO_ERROR.");
+ pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
+ }
+ }
+
+ /**
+ * Called by ReaderCallbackHandler when NFC tag is in proximity.
+ */
+ public void onTagDiscovered(Tag tag) {
+ mTagHandler = NfcTagHandler.create(tag);
+ processPendingPushOperation();
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698