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.NdefMessage; | |
14 import android.nfc.NfcAdapter; | |
15 import android.nfc.NfcAdapter.ReaderCallback; | |
16 import android.nfc.NfcManager; | |
17 import android.nfc.Tag; | |
18 import android.nfc.TagLostException; | |
19 import android.os.Build; | |
20 import android.os.Handler; | |
21 import android.os.Process; | |
22 import android.util.SparseArray; | |
23 | |
24 import org.chromium.base.Callback; | |
25 import org.chromium.base.Log; | |
26 import org.chromium.device.nfc.mojom.Nfc; | |
27 import org.chromium.device.nfc.mojom.NfcClient; | |
28 import org.chromium.device.nfc.mojom.NfcError; | |
29 import org.chromium.device.nfc.mojom.NfcErrorType; | |
30 import org.chromium.device.nfc.mojom.NfcMessage; | |
31 import org.chromium.device.nfc.mojom.NfcPushOptions; | |
32 import org.chromium.device.nfc.mojom.NfcPushTarget; | |
33 import org.chromium.device.nfc.mojom.NfcWatchMode; | |
34 import org.chromium.device.nfc.mojom.NfcWatchOptions; | |
35 import org.chromium.mojo.bindings.Callbacks; | |
36 import org.chromium.mojo.system.MojoException; | |
37 | |
38 import java.io.IOException; | |
39 import java.io.UnsupportedEncodingException; | |
40 import java.util.ArrayList; | |
41 import java.util.List; | |
42 | |
43 /** Android implementation of the NFC mojo service defined in device/nfc/nfc.moj
om. | |
44 */ | |
45 public class NfcImpl implements Nfc { | |
46 private static final String TAG = "NfcImpl"; | |
47 | |
48 private final int mHostId; | |
49 | |
50 private final NfcDelegate mDelegate; | |
51 | |
52 /** | |
53 * Used to get instance of NFC adapter, @see android.nfc.NfcManager | |
54 */ | |
55 private final NfcManager mNfcManager; | |
56 | |
57 /** | |
58 * NFC adapter. @see android.nfc.NfcAdapter | |
59 */ | |
60 private final NfcAdapter mNfcAdapter; | |
61 | |
62 /** | |
63 * Activity that is in foreground and is used to enable / disable NFC reader
mode operations. | |
64 * Can be updated when activity associated with web page is changed. @see #s
etActivity | |
65 */ | |
66 private Activity mActivity; | |
67 | |
68 /** | |
69 * Flag that indicates whether NFC permission is granted. | |
70 */ | |
71 private final boolean mHasPermission; | |
72 | |
73 /** | |
74 * Implementation of android.nfc.NfcAdapter.ReaderCallback. @see ReaderCallb
ackHandler | |
75 */ | |
76 private ReaderCallbackHandler mReaderCallbackHandler; | |
77 | |
78 /** | |
79 * Object that contains data that was passed to method | |
80 * #push(NfcMessage message, NfcPushOptions options, PushResponse callback) | |
81 * @see PendingPushOperation | |
82 */ | |
83 private PendingPushOperation mPendingPushOperation; | |
84 | |
85 /** | |
86 * Utility that provides I/O operations for a Tag. Created on demand when Ta
g is found. | |
87 * @see NfcTagHandler | |
88 */ | |
89 private NfcTagHandler mTagHandler; | |
90 | |
91 /** | |
92 * Client interface used to deliver NFCMessages for registered watch operati
ons. | |
93 * @see #watch | |
94 */ | |
95 private NfcClient mClient; | |
96 | |
97 /** | |
98 * Watcher id that is incremented for each #watch call. | |
99 */ | |
100 private int mWatcherId; | |
101 | |
102 /** | |
103 * Map of watchId <-> NfcWatchOptions. All NfcWatchOptions are matched again
st tag that is in | |
104 * proximity, when match algorithm (@see #matchesWatchOptions) returns true,
watcher with | |
105 * corresponding ID would be notified using NfcClient interface. | |
106 * @see NfcClient#onWatch(int[] id, NfcMessage message) | |
107 */ | |
108 private final SparseArray<NfcWatchOptions> mWatchers = new SparseArray<>(); | |
109 | |
110 /** | |
111 * Handler that runs delayed push timeout task. | |
112 */ | |
113 private final Handler mPushTimeoutHandler = new Handler(); | |
114 | |
115 /** | |
116 * Runnable responsible for cancelling push operation after specified timeou
t. | |
117 */ | |
118 private Runnable mPushTimeoutRunnable; | |
119 | |
120 public NfcImpl(Context context, int hostId, NfcDelegate delegate) { | |
121 mHostId = hostId; | |
122 mDelegate = delegate; | |
123 int permission = | |
124 context.checkPermission(Manifest.permission.NFC, Process.myPid()
, Process.myUid()); | |
125 mHasPermission = permission == PackageManager.PERMISSION_GRANTED; | |
126 Callback<Activity> onActivityUpdatedCallback = new Callback<Activity>()
{ | |
127 @Override | |
128 public void onResult(Activity activity) { | |
129 setActivity(activity); | |
130 } | |
131 }; | |
132 | |
133 mDelegate.trackActivityForHost(mHostId, onActivityUpdatedCallback); | |
134 | |
135 if (!mHasPermission || Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKA
T) { | |
136 Log.w(TAG, "NFC operations are not permitted."); | |
137 mNfcAdapter = null; | |
138 mNfcManager = null; | |
139 } else { | |
140 mNfcManager = (NfcManager) context.getSystemService(Context.NFC_SERV
ICE); | |
141 if (mNfcManager == null) { | |
142 Log.w(TAG, "NFC is not supported."); | |
143 mNfcAdapter = null; | |
144 } else { | |
145 mNfcAdapter = mNfcManager.getDefaultAdapter(); | |
146 } | |
147 } | |
148 } | |
149 | |
150 /** | |
151 * Sets Activity that is used to enable / disable NFC reader mode. When Acti
vity is set, | |
152 * reader mode is disabled for old Activity and enabled for the new Activity
. | |
153 */ | |
154 protected void setActivity(Activity activity) { | |
155 disableReaderMode(); | |
156 mActivity = activity; | |
157 enableReaderModeIfNeeded(); | |
158 } | |
159 | |
160 /** | |
161 * Sets NfcClient. NfcClient interface is used to notify mojo NFC service cl
ient when NFC | |
162 * device is in proximity and has NfcMessage that matches NfcWatchOptions cr
iteria. | |
163 * @see Nfc#watch(NfcWatchOptions options, WatchResponse callback) | |
164 * | |
165 * @param client @see NfcClient | |
166 */ | |
167 @Override | |
168 public void setClient(NfcClient client) { | |
169 mClient = client; | |
170 } | |
171 | |
172 /** | |
173 * Pushes NfcMessage to Tag or Peer, whenever NFC device is in proximity. At
the moment, only | |
174 * passive NFC devices are supported (NfcPushTarget.TAG). | |
175 * | |
176 * @param message that should be pushed to NFC device. | |
177 * @param options that contain information about timeout and target device t
ype. | |
178 * @param callback that is used to notify when push operation is completed. | |
179 */ | |
180 @Override | |
181 public void push(NfcMessage message, NfcPushOptions options, PushResponse ca
llback) { | |
182 if (!checkIfReady(callback)) return; | |
183 | |
184 if (!NfcMessageValidator.isValid(message)) { | |
185 callback.call(createError(NfcErrorType.INVALID_MESSAGE)); | |
186 return; | |
187 } | |
188 | |
189 // Check NfcPushOptions that are not supported by Android platform. | |
190 if (options.target == NfcPushTarget.PEER || options.timeout < 0 | |
191 || (options.timeout > Long.MAX_VALUE && !Double.isInfinite(optio
ns.timeout))) { | |
192 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
193 return; | |
194 } | |
195 | |
196 // If previous pending push operation is not completed, cancel it. | |
197 if (mPendingPushOperation != null) { | |
198 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA
NCELLED)); | |
199 cancelPushTimeoutTask(); | |
200 } | |
201 | |
202 mPendingPushOperation = new PendingPushOperation(message, options, callb
ack); | |
203 | |
204 // Schedule push timeout task for new #mPendingPushOperation. | |
205 schedulePushTimeoutTask(options); | |
206 enableReaderModeIfNeeded(); | |
207 processPendingPushOperation(); | |
208 } | |
209 | |
210 /** | |
211 * Cancels pending push operation. | |
212 * At the moment, only passive NFC devices are supported (NfcPushTarget.TAG)
. | |
213 * | |
214 * @param target @see NfcPushTarget | |
215 * @param callback that is used to notify caller when cancelPush() is comple
ted. | |
216 */ | |
217 @Override | |
218 public void cancelPush(int target, CancelPushResponse callback) { | |
219 if (!checkIfReady(callback)) return; | |
220 | |
221 if (target == NfcPushTarget.PEER) { | |
222 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
223 return; | |
224 } | |
225 | |
226 if (mPendingPushOperation == null) { | |
227 callback.call(createError(NfcErrorType.NOT_FOUND)); | |
228 } else { | |
229 completePendingPushOperation(createError(NfcErrorType.OPERATION_CANC
ELLED)); | |
230 callback.call(null); | |
231 } | |
232 } | |
233 | |
234 /** | |
235 * Watch method allows to set filtering criteria for NfcMessages that are fo
und when NFC device | |
236 * is within proximity. On success, watch ID is returned to caller through W
atchResponse | |
237 * callback. When NfcMessage that matches NfcWatchOptions is found, it is pa
ssed to NfcClient | |
238 * interface together with corresponding watch ID. | |
239 * @see NfcClient#onWatch(int[] id, NfcMessage message) | |
240 * | |
241 * @param options used to filter NfcMessages, @see NfcWatchOptions. | |
242 * @param callback that is used to notify caller when watch() is completed a
nd return watch ID. | |
243 */ | |
244 @Override | |
245 public void watch(NfcWatchOptions options, WatchResponse callback) { | |
246 if (!checkIfReady(callback)) return; | |
247 int watcherId = ++mWatcherId; | |
248 mWatchers.put(watcherId, options); | |
249 callback.call(watcherId, null); | |
250 enableReaderModeIfNeeded(); | |
251 processPendingWatchOperations(); | |
252 } | |
253 | |
254 /** | |
255 * Cancels NFC watch operation. | |
256 * | |
257 * @param id of watch operation. | |
258 * @param callback that is used to notify caller when cancelWatch() is compl
eted. | |
259 */ | |
260 @Override | |
261 public void cancelWatch(int id, CancelWatchResponse callback) { | |
262 if (!checkIfReady(callback)) return; | |
263 | |
264 if (mWatchers.indexOfKey(id) < 0) { | |
265 callback.call(createError(NfcErrorType.NOT_FOUND)); | |
266 } else { | |
267 mWatchers.remove(id); | |
268 callback.call(null); | |
269 disableReaderModeIfNeeded(); | |
270 } | |
271 } | |
272 | |
273 /** | |
274 * Cancels all NFC watch operations. | |
275 * | |
276 * @param callback that is used to notify caller when cancelAllWatches() is
completed. | |
277 */ | |
278 @Override | |
279 public void cancelAllWatches(CancelAllWatchesResponse callback) { | |
280 if (!checkIfReady(callback)) return; | |
281 | |
282 if (mWatchers.size() == 0) { | |
283 callback.call(createError(NfcErrorType.NOT_FOUND)); | |
284 } else { | |
285 mWatchers.clear(); | |
286 callback.call(null); | |
287 disableReaderModeIfNeeded(); | |
288 } | |
289 } | |
290 | |
291 /** | |
292 * Suspends all pending operations. Should be called when web page visibilit
y is lost. | |
293 */ | |
294 @Override | |
295 public void suspendNfcOperations() { | |
296 disableReaderMode(); | |
297 } | |
298 | |
299 /** | |
300 * Resumes all pending watch / push operations. Should be called when web pa
ge becomes visible. | |
301 */ | |
302 @Override | |
303 public void resumeNfcOperations() { | |
304 enableReaderModeIfNeeded(); | |
305 } | |
306 | |
307 @Override | |
308 public void close() { | |
309 mDelegate.stopTrackingActivityForHost(mHostId); | |
310 disableReaderMode(); | |
311 } | |
312 | |
313 @Override | |
314 public void onConnectionError(MojoException e) { | |
315 close(); | |
316 } | |
317 | |
318 /** | |
319 * Holds information about pending push operation. | |
320 */ | |
321 private static class PendingPushOperation { | |
322 public final NfcMessage nfcMessage; | |
323 public final NfcPushOptions nfcPushOptions; | |
324 private final PushResponse mPushResponseCallback; | |
325 | |
326 public PendingPushOperation( | |
327 NfcMessage message, NfcPushOptions options, PushResponse callbac
k) { | |
328 nfcMessage = message; | |
329 nfcPushOptions = options; | |
330 mPushResponseCallback = callback; | |
331 } | |
332 | |
333 /** | |
334 * Completes pending push operation. | |
335 * | |
336 * @param error should be null when operation is completed successfully,
otherwise, | |
337 * error object with corresponding NfcErrorType should be provided. | |
338 */ | |
339 public void complete(NfcError error) { | |
340 if (mPushResponseCallback != null) mPushResponseCallback.call(error)
; | |
341 } | |
342 } | |
343 | |
344 /** | |
345 * Helper method that creates NfcError object from NfcErrorType. | |
346 */ | |
347 private NfcError createError(int errorType) { | |
348 NfcError error = new NfcError(); | |
349 error.errorType = errorType; | |
350 return error; | |
351 } | |
352 | |
353 /** | |
354 * Checks if NFC funcionality can be used by the mojo service. If permission
to use NFC is | |
355 * granted and hardware is enabled, returns null. | |
356 */ | |
357 private NfcError checkIfReady() { | |
358 if (!mHasPermission || mActivity == null) { | |
359 return createError(NfcErrorType.SECURITY); | |
360 } else if (mNfcManager == null || mNfcAdapter == null) { | |
361 return createError(NfcErrorType.NOT_SUPPORTED); | |
362 } else if (!mNfcAdapter.isEnabled()) { | |
363 return createError(NfcErrorType.DEVICE_DISABLED); | |
364 } | |
365 return null; | |
366 } | |
367 | |
368 /** | |
369 * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback
with NfcError. | |
370 * | |
371 * @param WatchResponse Callback that is provided to watch() method. | |
372 * @return boolean true if NFC functionality can be used, false otherwise. | |
373 */ | |
374 private boolean checkIfReady(WatchResponse callback) { | |
375 NfcError error = checkIfReady(); | |
376 if (error == null) return true; | |
377 | |
378 callback.call(0, error); | |
379 return false; | |
380 } | |
381 | |
382 /** | |
383 * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback
with NfcError. | |
384 * | |
385 * @param callback Generic callback that is provided to push(), cancelPush()
, | |
386 * cancelWatch() and cancelAllWatches() methods. | |
387 * @return boolean true if NFC functionality can be used, false otherwise. | |
388 */ | |
389 private boolean checkIfReady(Callbacks.Callback1<NfcError> callback) { | |
390 NfcError error = checkIfReady(); | |
391 if (error == null) return true; | |
392 | |
393 callback.call(error); | |
394 return false; | |
395 } | |
396 | |
397 /** | |
398 * Implementation of android.nfc.NfcAdapter.ReaderCallback. Callback is call
ed when NFC tag is | |
399 * discovered, Tag object is delegated to mojo service implementation method | |
400 * NfcImpl.onTagDiscovered(). | |
401 */ | |
402 @TargetApi(Build.VERSION_CODES.KITKAT) | |
403 private static class ReaderCallbackHandler implements ReaderCallback { | |
404 private final NfcImpl mNfcImpl; | |
405 | |
406 public ReaderCallbackHandler(NfcImpl impl) { | |
407 mNfcImpl = impl; | |
408 } | |
409 | |
410 @Override | |
411 public void onTagDiscovered(Tag tag) { | |
412 mNfcImpl.onTagDiscovered(tag); | |
413 } | |
414 } | |
415 | |
416 /** | |
417 * Enables reader mode, allowing NFC device to read / write NFC tags. | |
418 * @see android.nfc.NfcAdapter#enableReaderMode | |
419 */ | |
420 private void enableReaderModeIfNeeded() { | |
421 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; | |
422 | |
423 if (mReaderCallbackHandler != null || mActivity == null || mNfcAdapter =
= null) return; | |
424 | |
425 // Do not enable reader mode, if there are no active push / watch operat
ions. | |
426 if (mPendingPushOperation == null && mWatchers.size() == 0) return; | |
427 | |
428 mReaderCallbackHandler = new ReaderCallbackHandler(this); | |
429 mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler, | |
430 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B | |
431 | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_
NFC_V, | |
432 null); | |
433 } | |
434 | |
435 /** | |
436 * Disables reader mode. | |
437 * @see android.nfc.NfcAdapter#disableReaderMode | |
438 */ | |
439 @TargetApi(Build.VERSION_CODES.KITKAT) | |
440 private void disableReaderMode() { | |
441 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; | |
442 | |
443 // There is no API that could query whether reader mode is enabled for a
dapter. | |
444 // If mReaderCallbackHandler is null, reader mode is not enabled. | |
445 if (mReaderCallbackHandler == null) return; | |
446 | |
447 mReaderCallbackHandler = null; | |
448 if (mActivity != null && mNfcAdapter != null && !mActivity.isDestroyed()
) { | |
449 mNfcAdapter.disableReaderMode(mActivity); | |
450 } | |
451 } | |
452 | |
453 /** | |
454 * Checks if there are pending push / watch operations and disables readre m
ode | |
455 * whenever necessary. | |
456 */ | |
457 private void disableReaderModeIfNeeded() { | |
458 if (mPendingPushOperation == null && mWatchers.size() == 0) { | |
459 disableReaderMode(); | |
460 } | |
461 } | |
462 | |
463 /** | |
464 * Handles completion of pending push operation, cancels timeout task and co
mpletes push | |
465 * operation. On error, invalidates #mTagHandler. | |
466 */ | |
467 private void pendingPushOperationCompleted(NfcError error) { | |
468 completePendingPushOperation(error); | |
469 if (error != null) mTagHandler = null; | |
470 } | |
471 | |
472 /** | |
473 * Completes pending push operation and disables reader mode if needed. | |
474 */ | |
475 private void completePendingPushOperation(NfcError error) { | |
476 if (mPendingPushOperation == null) return; | |
477 | |
478 cancelPushTimeoutTask(); | |
479 mPendingPushOperation.complete(error); | |
480 mPendingPushOperation = null; | |
481 disableReaderModeIfNeeded(); | |
482 } | |
483 | |
484 /** | |
485 * Checks whether there is a #mPendingPushOperation and writes data to NFC t
ag. In case of | |
486 * exception calls pendingPushOperationCompleted() with appropriate error ob
ject. | |
487 */ | |
488 private void processPendingPushOperation() { | |
489 if (mTagHandler == null || mPendingPushOperation == null) return; | |
490 | |
491 if (mTagHandler.isTagOutOfRange()) { | |
492 mTagHandler = null; | |
493 return; | |
494 } | |
495 | |
496 try { | |
497 mTagHandler.connect(); | |
498 mTagHandler.write(NfcTypeConverter.toNdefMessage(mPendingPushOperati
on.nfcMessage)); | |
499 pendingPushOperationCompleted(null); | |
500 } catch (InvalidNfcMessageException e) { | |
501 Log.w(TAG, "Cannot write data to NFC tag. Invalid NfcMessage."); | |
502 pendingPushOperationCompleted(createError(NfcErrorType.INVALID_MESSA
GE)); | |
503 } catch (TagLostException e) { | |
504 Log.w(TAG, "Cannot write data to NFC tag. Tag is lost."); | |
505 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR)); | |
506 } catch (FormatException | IllegalStateException | IOException e) { | |
507 Log.w(TAG, "Cannot write data to NFC tag. IO_ERROR."); | |
508 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR)); | |
509 } | |
510 } | |
511 | |
512 /** | |
513 * Reads NfcMessage from a tag and forwards message to matching method. | |
514 */ | |
515 private void processPendingWatchOperations() { | |
516 if (mTagHandler == null || mClient == null || mWatchers.size() == 0) ret
urn; | |
517 | |
518 // Skip reading if there is a pending push operation and ignoreRead flag
is set. | |
519 if (mPendingPushOperation != null && mPendingPushOperation.nfcPushOption
s.ignoreRead) { | |
520 return; | |
521 } | |
522 | |
523 if (mTagHandler.isTagOutOfRange()) { | |
524 mTagHandler = null; | |
525 return; | |
526 } | |
527 | |
528 NdefMessage message = null; | |
529 | |
530 try { | |
531 mTagHandler.connect(); | |
532 message = mTagHandler.read(); | |
533 if (message.getByteArrayLength() > NfcMessage.MAX_SIZE) { | |
534 Log.w(TAG, "Cannot read data from NFC tag. NfcMessage exceeds al
lowed size."); | |
535 return; | |
536 } | |
537 } catch (TagLostException e) { | |
538 Log.w(TAG, "Cannot read data from NFC tag. Tag is lost."); | |
539 } catch (FormatException | IllegalStateException | IOException e) { | |
540 Log.w(TAG, "Cannot read data from NFC tag. IO_ERROR."); | |
541 } | |
542 | |
543 if (message != null) notifyMatchingWatchers(message); | |
544 } | |
545 | |
546 /** | |
547 * Iterates through active watchers and if any of those match NfcWatchOption
s criteria, | |
548 * delivers NfcMessage to the client. | |
549 */ | |
550 private void notifyMatchingWatchers(NdefMessage message) { | |
551 try { | |
552 NfcMessage nfcMessage = NfcTypeConverter.toNfcMessage(message); | |
553 List<Integer> watchIds = new ArrayList<Integer>(); | |
554 for (int i = 0; i < mWatchers.size(); i++) { | |
555 NfcWatchOptions options = mWatchers.valueAt(i); | |
556 if (matchesWatchOptions(nfcMessage, options)) watchIds.add(mWatc
hers.keyAt(i)); | |
557 } | |
558 | |
559 if (watchIds.size() != 0) { | |
560 int[] ids = new int[watchIds.size()]; | |
561 for (int i = 0; i < watchIds.size(); ++i) { | |
562 ids[i] = watchIds.get(i).intValue(); | |
563 } | |
564 mClient.onWatch(ids, nfcMessage); | |
565 } | |
566 } catch (UnsupportedEncodingException e) { | |
567 Log.w(TAG, "Cannot convert NdefMessage to NfcMessage."); | |
568 } | |
569 } | |
570 | |
571 /** | |
572 * Implements matching algorithm. | |
573 */ | |
574 private boolean matchesWatchOptions(NfcMessage message, NfcWatchOptions opti
ons) { | |
575 // Valid WebNFC message must have non-empty url. | |
576 if (options.mode == NfcWatchMode.WEBNFC_ONLY | |
577 && (message.url == null || message.url.isEmpty())) { | |
578 return false; | |
579 } | |
580 | |
581 // Filter by NfcMessage.url | |
582 if (options.url != null && !options.url.isEmpty() && !options.url.equals
(message.url)) { | |
583 return false; | |
584 } | |
585 | |
586 // Matches any record / media type. | |
587 if ((options.mediaType == null || options.mediaType.isEmpty()) | |
588 && options.recordFilter == null) { | |
589 return true; | |
590 } | |
591 | |
592 // Filter by mediaType and recordType | |
593 for (int i = 0; i < message.data.length; i++) { | |
594 boolean matchedMediaType; | |
595 boolean matchedRecordType; | |
596 | |
597 if (options.mediaType == null || options.mediaType.isEmpty()) { | |
598 // If media type for the watch options is empty, match all media
types. | |
599 matchedMediaType = true; | |
600 } else { | |
601 matchedMediaType = options.mediaType.equals(message.data[i].medi
aType); | |
602 } | |
603 | |
604 if (options.recordFilter == null) { | |
605 // If record type filter for the watch options is null, match al
l record types. | |
606 matchedRecordType = true; | |
607 } else { | |
608 matchedRecordType = options.recordFilter.recordType == message.d
ata[i].recordType; | |
609 } | |
610 | |
611 if (matchedMediaType && matchedRecordType) return true; | |
612 } | |
613 | |
614 return false; | |
615 } | |
616 | |
617 /** | |
618 * Called by ReaderCallbackHandler when NFC tag is in proximity. | |
619 */ | |
620 public void onTagDiscovered(Tag tag) { | |
621 processPendingOperations(NfcTagHandler.create(tag)); | |
622 } | |
623 | |
624 /** | |
625 * Processes pending operation when NFC tag is in proximity. | |
626 */ | |
627 protected void processPendingOperations(NfcTagHandler tagHandler) { | |
628 mTagHandler = tagHandler; | |
629 processPendingWatchOperations(); | |
630 processPendingPushOperation(); | |
631 if (mTagHandler != null && mTagHandler.isConnected()) { | |
632 try { | |
633 mTagHandler.close(); | |
634 } catch (IOException e) { | |
635 Log.w(TAG, "Cannot close NFC tag connection."); | |
636 } | |
637 } | |
638 } | |
639 | |
640 /** | |
641 * Schedules task that is executed after timeout and cancels pending push op
eration. | |
642 */ | |
643 private void schedulePushTimeoutTask(NfcPushOptions options) { | |
644 assert mPushTimeoutRunnable == null; | |
645 // Default timeout value. | |
646 if (Double.isInfinite(options.timeout)) return; | |
647 | |
648 // Create and schedule timeout. | |
649 mPushTimeoutRunnable = new Runnable() { | |
650 @Override | |
651 public void run() { | |
652 completePendingPushOperation(createError(NfcErrorType.TIMER_EXPI
RED)); | |
653 } | |
654 }; | |
655 | |
656 mPushTimeoutHandler.postDelayed(mPushTimeoutRunnable, (long) options.tim
eout); | |
657 } | |
658 | |
659 /** | |
660 * Cancels push timeout task. | |
661 */ | |
662 void cancelPushTimeoutTask() { | |
663 if (mPushTimeoutRunnable == null) return; | |
664 | |
665 mPushTimeoutHandler.removeCallbacks(mPushTimeoutRunnable); | |
666 mPushTimeoutRunnable = null; | |
667 } | |
668 } | |
OLD | NEW |