Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.device.nfc; | |
| 6 | |
| 7 import android.Manifest; | |
| 8 import android.annotation.TargetApi; | |
| 9 import android.app.Activity; | |
| 10 import android.content.Context; | |
| 11 import android.content.pm.PackageManager; | |
| 12 import android.nfc.FormatException; | |
| 13 import android.nfc.NfcAdapter; | |
| 14 import android.nfc.NfcAdapter.ReaderCallback; | |
| 15 import android.nfc.NfcManager; | |
| 16 import android.nfc.Tag; | |
| 17 import android.nfc.TagLostException; | |
| 18 import android.os.Build; | |
| 19 import android.os.Process; | |
| 20 import android.support.annotation.Nullable; | |
| 21 | |
| 22 import org.chromium.base.Log; | |
| 23 import org.chromium.mojo.bindings.Callbacks; | |
| 24 import org.chromium.mojo.system.MojoException; | |
| 25 import org.chromium.mojom.device.nfc.Nfc; | |
| 26 import org.chromium.mojom.device.nfc.NfcClient; | |
| 27 import org.chromium.mojom.device.nfc.NfcError; | |
| 28 import org.chromium.mojom.device.nfc.NfcErrorType; | |
| 29 import org.chromium.mojom.device.nfc.NfcMessage; | |
| 30 import org.chromium.mojom.device.nfc.NfcPushOptions; | |
| 31 import org.chromium.mojom.device.nfc.NfcPushTarget; | |
| 32 import org.chromium.mojom.device.nfc.NfcWatchOptions; | |
| 33 | |
| 34 import java.io.IOException; | |
| 35 | |
| 36 /** | |
| 37 * Android implementation of the NFC mojo service defined in | |
| 38 * device/nfc/nfc.mojom. | |
| 39 */ | |
| 40 public class NfcImpl implements Nfc { | |
| 41 private static final String TAG = "NfcImpl"; | |
| 42 | |
| 43 /** | |
| 44 * Used to get instance of NFC adapter, @see android.nfc.NfcManager | |
| 45 */ | |
| 46 private final NfcManager mNfcManager; | |
| 47 | |
| 48 /** | |
| 49 * NFC adapter. @see android.nfc.NfcAdapter | |
| 50 */ | |
| 51 private final NfcAdapter mNfcAdapter; | |
| 52 | |
| 53 /** | |
| 54 * Activity that is in foreground and is used to enable / disable NFC reader mode operations. | |
| 55 * Can be Updated when activity associated with web page is changed. @see #s etActivity | |
|
Reilly Grant (use Gerrit)
2016/07/01 20:51:44
s/Updated/updated/
shalamov
2016/07/04 10:08:16
Done.
| |
| 56 */ | |
| 57 private Activity mActivity; | |
| 58 | |
| 59 /** | |
| 60 * Flag that indicates whether NFC permission is granted. | |
| 61 */ | |
| 62 private final boolean mHasPermission; | |
| 63 | |
| 64 /** | |
| 65 * Implementation of android.nfc.NfcAdapter.ReaderCallback. @see ReaderCallb ackHandler | |
| 66 */ | |
| 67 private ReaderCallbackHandler mReaderCallbackHandler; | |
| 68 | |
| 69 /** | |
| 70 * Object that contains data that was passed to method | |
| 71 * #push(NfcMessage message, NfcPushOptions options, PushResponse callback) | |
| 72 * @see PendingPushOperation | |
| 73 */ | |
| 74 private PendingPushOperation mPendingPushOperation; | |
| 75 | |
| 76 /** | |
| 77 * Utility that provides I/O operations for a Tag. Created on demand when Ta g is found. | |
| 78 * @see NfcTagHandler | |
| 79 */ | |
| 80 private NfcTagHandler mTagHandler; | |
| 81 | |
| 82 public NfcImpl(Context context) { | |
| 83 int permission = | |
| 84 context.checkPermission(Manifest.permission.NFC, Process.myPid() , Process.myUid()); | |
| 85 mHasPermission = permission == PackageManager.PERMISSION_GRANTED; | |
| 86 | |
| 87 if (mHasPermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKA T) { | |
| 88 mNfcManager = (NfcManager) context.getSystemService(Context.NFC_SERV ICE); | |
| 89 if (mNfcManager != null) { | |
|
Reilly Grant (use Gerrit)
2016/07/01 20:51:44
nit: Inverting this conditional will make the code
shalamov
2016/07/04 10:08:16
I inverted conditional. Blank final class variable
Reilly Grant (use Gerrit)
2016/07/06 20:01:02
My apologies. I don't review very much Java code.
| |
| 90 mNfcAdapter = mNfcManager.getDefaultAdapter(); | |
| 91 } else { | |
| 92 Log.w(TAG, "NFC is not supported."); | |
| 93 mNfcAdapter = null; | |
| 94 } | |
| 95 } else { | |
| 96 Log.w(TAG, "NFC operations are not permitted."); | |
| 97 mNfcAdapter = null; | |
| 98 mNfcManager = null; | |
|
Reilly Grant (use Gerrit)
2016/07/01 20:51:45
Ditto here, these fields will already be null.
shalamov
2016/07/04 10:08:16
same as above, blank final class members must be d
| |
| 99 } | |
| 100 } | |
| 101 | |
| 102 /** | |
| 103 * Sets Activity that is used to enable / disable NFC reader mode. When Acti vity is set, | |
| 104 * reader mode is disabled for old Activity and enabled for the new Activity . | |
| 105 */ | |
| 106 protected void setActivity(Activity activity) { | |
| 107 disableReaderMode(); | |
| 108 mActivity = activity; | |
| 109 enableReaderMode(); | |
| 110 } | |
| 111 | |
| 112 /** | |
| 113 * Sets NfcClient. NfcClient interface is used to notify mojo NFC service cl ient when NFC | |
| 114 * device is in proximity and has NfcMessage that matches NfcWatchOptions cr iteria. | |
| 115 * @see Nfc#watch(NfcWatchOptions options, WatchResponse callback) | |
| 116 * | |
| 117 * @param client @see NfcClient | |
| 118 */ | |
| 119 @Override | |
| 120 public void setClient(NfcClient client) { | |
| 121 // TODO(shalamov): Should be implemented when watch() is implemented. | |
|
Reilly Grant (use Gerrit)
2016/07/01 20:51:44
Please use TODO(crbug.com/XXXXXX) instead of a use
shalamov
2016/07/04 10:08:16
Done.
| |
| 122 } | |
| 123 | |
| 124 /** | |
| 125 * Pushes NfcMessage to Tag or Peer, whenever NFC device is in proximity. At the moment, only | |
| 126 * passive NFC devices are supported (NfcPushTarget.TAG). | |
| 127 * | |
| 128 * @param message that should be pushed to NFC device. | |
| 129 * @param options that contain information about timeout and target device t ype. | |
| 130 * @param callback that is used to notify when push operation is completed. | |
| 131 */ | |
| 132 @Override | |
| 133 public void push(NfcMessage message, NfcPushOptions options, PushResponse ca llback) { | |
| 134 if (!checkIfReady(callback)) return; | |
| 135 | |
| 136 if (options.target == NfcPushTarget.PEER) { | |
| 137 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
| 138 return; | |
| 139 } | |
| 140 | |
| 141 // If previous pending push operation is not completed, cancel it. | |
| 142 if (mPendingPushOperation != null) { | |
| 143 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED)); | |
| 144 } | |
| 145 | |
| 146 mPendingPushOperation = new PendingPushOperation(message, options, callb ack); | |
| 147 enableReaderMode(); | |
| 148 processPendingPushOperation(); | |
| 149 } | |
| 150 | |
| 151 /** | |
| 152 * Cancels pending push operation. | |
| 153 * At the moment, only passive NFC devices are supported (NfcPushTarget.TAG) . | |
| 154 * | |
| 155 * @param target @see NfcPushTarget | |
| 156 * @param callback that is used to notify caller when cancelPush() is comple ted. | |
| 157 */ | |
| 158 @Override | |
| 159 public void cancelPush(int target, CancelPushResponse callback) { | |
| 160 if (!checkIfReady(callback)) return; | |
| 161 | |
| 162 if (target == NfcPushTarget.PEER) { | |
| 163 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
| 164 return; | |
| 165 } | |
| 166 | |
| 167 if (mPendingPushOperation != null) { | |
|
Reilly Grant (use Gerrit)
2016/07/01 20:51:44
nit: Invert this condition for better readability.
shalamov
2016/07/04 10:08:16
Done.
| |
| 168 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED)); | |
| 169 mPendingPushOperation = null; | |
| 170 callback.call(null); | |
| 171 disableReaderMode(); | |
| 172 } else { | |
| 173 callback.call(createError(NfcErrorType.NOT_FOUND)); | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 /** | |
| 178 * Watch method allows to set filtering criteria for NfcMessages that are fo und when NFC device | |
| 179 * is within proximity. On success, watch ID is returned to caller through W atchResponse | |
| 180 * callback. When NfcMessage that matches NfcWatchOptions is found, it is pa ssed to NfcClient | |
| 181 * interface together with corresponding watch ID. | |
| 182 * @see NfcClient#onWatch(int[] id, NfcMessage message) | |
| 183 * | |
| 184 * @param options used to filter NfcMessages, @see NfcWatchOptions. | |
| 185 * @param callback that is used to notify caller when watch() is completed a nd return watch ID. | |
| 186 */ | |
| 187 @Override | |
| 188 public void watch(NfcWatchOptions options, WatchResponse callback) { | |
| 189 if (!checkIfReady(callback)) return; | |
| 190 // TODO(shalamov): Not implemented. | |
|
Reilly Grant (use Gerrit)
2016/07/01 20:51:44
Same here, please file a bug.
shalamov
2016/07/04 10:08:16
Done.
| |
| 191 callback.call(0, createError(NfcErrorType.NOT_SUPPORTED)); | |
| 192 } | |
| 193 | |
| 194 /** | |
| 195 * Cancels NFC watch operation. | |
| 196 * | |
| 197 * @param id of watch operation. | |
| 198 * @param callback that is used to notify caller when cancelWatch() is compl eted. | |
| 199 */ | |
| 200 @Override | |
| 201 public void cancelWatch(int id, CancelWatchResponse callback) { | |
| 202 if (!checkIfReady(callback)) return; | |
|
Reilly Grant (use Gerrit)
2016/07/01 20:51:44
Bug.
shalamov
2016/07/04 10:08:16
Done.
| |
| 203 // TODO(shalamov): Not implemented. | |
| 204 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
| 205 } | |
| 206 | |
| 207 /** | |
| 208 * Cancels all NFC watch operations. | |
| 209 * | |
| 210 * @param callback that is used to notify caller when cancelAllWatches() is completed. | |
| 211 */ | |
| 212 @Override | |
| 213 public void cancelAllWatches(CancelAllWatchesResponse callback) { | |
| 214 if (!checkIfReady(callback)) return; | |
| 215 // TODO(shalamov): Not implemented. | |
|
Reilly Grant (use Gerrit)
2016/07/01 20:51:44
Bug (note, this can all be the same bug).
shalamov
2016/07/04 10:08:16
Done.
| |
| 216 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
| 217 } | |
| 218 | |
| 219 /** | |
| 220 * Suspends all pending operations. Should be called when web page visibilit y is lost. | |
| 221 */ | |
| 222 @Override | |
| 223 public void suspendNfcOperations() { | |
| 224 disableReaderMode(); | |
| 225 } | |
| 226 | |
| 227 /** | |
| 228 * Resumes all pending watch / push operations. Should be called when web pa ge becomes visible. | |
| 229 */ | |
| 230 @Override | |
| 231 public void resumeNfcOperations() { | |
| 232 enableReaderMode(); | |
| 233 } | |
| 234 | |
| 235 @Override | |
| 236 public void close() { | |
| 237 disableReaderMode(); | |
| 238 } | |
| 239 | |
| 240 @Override | |
| 241 public void onConnectionError(MojoException e) { | |
| 242 close(); | |
| 243 } | |
| 244 | |
| 245 /** | |
| 246 * Holds information about pending push operation. | |
| 247 */ | |
| 248 private static class PendingPushOperation { | |
| 249 public final NfcMessage nfcMessage; | |
| 250 public final NfcPushOptions nfcPushOptions; | |
| 251 private final PushResponse mPushResponseCallback; | |
| 252 | |
| 253 public PendingPushOperation( | |
| 254 NfcMessage message, NfcPushOptions options, PushResponse callbac k) { | |
| 255 nfcMessage = message; | |
| 256 nfcPushOptions = options; | |
| 257 mPushResponseCallback = callback; | |
| 258 } | |
| 259 | |
| 260 /** | |
| 261 * Completes pending push operation. | |
| 262 * | |
| 263 * @param error should be null when operation is completed successfully, otherwise, | |
| 264 * error object with corresponding NfcErrorType should be provided. | |
| 265 */ | |
| 266 public void complete(NfcError error) { | |
| 267 if (mPushResponseCallback != null) mPushResponseCallback.call(error) ; | |
|
Reilly Grant (use Gerrit)
2016/07/01 20:51:44
Please break this into two lines here and elsewher
shalamov
2016/07/04 10:08:16
I think it is default java style that is used in C
| |
| 268 } | |
| 269 } | |
| 270 | |
| 271 /** | |
| 272 * Helper method that creates NfcError object from NfcErrorType. | |
| 273 */ | |
| 274 private NfcError createError(int errorType) { | |
| 275 NfcError error = new NfcError(); | |
| 276 error.errorType = errorType; | |
| 277 return error; | |
| 278 } | |
| 279 | |
| 280 /** | |
| 281 * Checks if NFC funcionality can be used by the mojo service. If permission to use NFC is | |
| 282 * granted and hardware is enabled, returns null. | |
| 283 */ | |
| 284 @Nullable | |
| 285 private NfcError checkIfReady() { | |
| 286 if (!mHasPermission || mActivity == null) { | |
| 287 return createError(NfcErrorType.SECURITY); | |
| 288 } else if (mNfcManager == null || mNfcAdapter == null) { | |
| 289 return createError(NfcErrorType.NOT_SUPPORTED); | |
| 290 } else if (!mNfcAdapter.isEnabled()) { | |
| 291 return createError(NfcErrorType.DEVICE_DISABLED); | |
| 292 } | |
| 293 return null; | |
| 294 } | |
| 295 | |
| 296 /** | |
| 297 * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback with NfcError. | |
| 298 * | |
| 299 * @param WatchResponse Callback that is provided to watch() method. | |
| 300 * @return boolean true if NFC functionality can be used, false otherwise. | |
| 301 */ | |
| 302 private boolean checkIfReady(WatchResponse callback) { | |
| 303 NfcError error = checkIfReady(); | |
| 304 if (error == null) return true; | |
| 305 callback.call(0, error); | |
| 306 return false; | |
| 307 } | |
| 308 | |
| 309 /** | |
| 310 * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback with NfcError. | |
| 311 * | |
| 312 * @param callback Generic callback that is provided to push(), cancelPush() , | |
| 313 * cancelWatch() and cancelAllWatches() methods. | |
| 314 * @return boolean true if NFC functionality can be used, false otherwise. | |
| 315 */ | |
| 316 private boolean checkIfReady(Callbacks.Callback1<NfcError> callback) { | |
| 317 NfcError error = checkIfReady(); | |
| 318 if (error == null) return true; | |
| 319 callback.call(error); | |
| 320 return false; | |
| 321 } | |
| 322 | |
| 323 /** | |
| 324 * Implementation of android.nfc.NfcAdapter.ReaderCallback. Callback is call ed when NFC tag is | |
| 325 * discovered, Tag object is delegated to mojo service implementation method | |
| 326 * NfcImpl.onTagDiscovered(). | |
| 327 */ | |
| 328 @TargetApi(Build.VERSION_CODES.KITKAT) | |
| 329 private static class ReaderCallbackHandler implements ReaderCallback { | |
| 330 private final NfcImpl mNfcImpl; | |
| 331 | |
| 332 public ReaderCallbackHandler(NfcImpl impl) { | |
| 333 mNfcImpl = impl; | |
| 334 } | |
| 335 | |
| 336 @Override | |
| 337 public void onTagDiscovered(Tag tag) { | |
| 338 mNfcImpl.onTagDiscovered(tag); | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 /** | |
| 343 * Enables reader mode, allowing NFC device to read / write NFC tags. | |
| 344 * @see android.nfc.NfcAdapter#enableReaderMode | |
| 345 */ | |
| 346 private void enableReaderMode() { | |
| 347 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; | |
| 348 if (mReaderCallbackHandler != null || mActivity == null) return; | |
| 349 | |
| 350 // TODO(shalamov): Check if there are active watch operations. | |
| 351 if (mPendingPushOperation == null) return; | |
| 352 | |
| 353 mReaderCallbackHandler = new ReaderCallbackHandler(this); | |
| 354 mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler, | |
| 355 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B | |
| 356 | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_ NFC_V, | |
| 357 null); | |
| 358 } | |
| 359 | |
| 360 /** | |
| 361 * Disables reader mode. | |
| 362 * @see android.nfc.NfcAdapter#disableReaderMode | |
| 363 */ | |
| 364 private void disableReaderMode() { | |
| 365 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; | |
| 366 | |
| 367 mReaderCallbackHandler = null; | |
| 368 if (mActivity != null) { | |
| 369 mNfcAdapter.disableReaderMode(mActivity); | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 /** | |
| 374 * Completes pending push operation. On error, invalidates #mTagHandler. | |
| 375 */ | |
| 376 private void pendingPushOperationCompleted(NfcError error) { | |
| 377 if (mPendingPushOperation != null) { | |
| 378 mPendingPushOperation.complete(error); | |
| 379 mPendingPushOperation = null; | |
| 380 | |
| 381 // TODO(shalamov): When nfc.watch is implemented, disable reader mod e | |
| 382 // only when there are no active watch operations. | |
| 383 disableReaderMode(); | |
| 384 } | |
| 385 | |
| 386 if (error != null) mTagHandler = null; | |
| 387 } | |
| 388 | |
| 389 /** | |
| 390 * Checks whether there is a #mPendingPushOperation and writes data to NFC t ag. In case of | |
| 391 * exception calls pendingPushOperationCompleted() with appropriate error ob ject. | |
| 392 */ | |
| 393 private void processPendingPushOperation() { | |
| 394 if (mTagHandler == null || mPendingPushOperation == null) return; | |
| 395 | |
| 396 if (mTagHandler.isTagOutOfRange()) { | |
| 397 mTagHandler = null; | |
| 398 return; | |
| 399 } | |
| 400 | |
| 401 try { | |
| 402 mTagHandler.connect(); | |
| 403 mTagHandler.write(NfcTypeConverter.toNdefMessage(mPendingPushOperati on.nfcMessage)); | |
| 404 pendingPushOperationCompleted(null); | |
| 405 mTagHandler.close(); | |
| 406 } catch (InvalidNfcMessageException e) { | |
| 407 Log.w(TAG, "Cannot write data to NFC tag. Invalid NfcMessage."); | |
| 408 pendingPushOperationCompleted(createError(NfcErrorType.INVALID_MESSA GE)); | |
| 409 } catch (TagLostException e) { | |
| 410 Log.w(TAG, "Cannot write data to NFC tag. Tag is lost."); | |
| 411 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR)); | |
| 412 } catch (FormatException | IOException e) { | |
| 413 Log.w(TAG, "Cannot write data to NFC tag. IO_ERROR."); | |
| 414 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR)); | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 /** | |
| 419 * Called by ReaderCallbackHandler when NFC tag is in proximity. | |
| 420 */ | |
| 421 public void onTagDiscovered(Tag tag) { | |
| 422 mTagHandler = NfcTagHandler.create(tag); | |
| 423 processPendingPushOperation(); | |
| 424 } | |
| 425 } | |
| OLD | NEW |