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

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

Issue 1765533004: [webnfc] Implement watch method for Android nfc mojo service. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@implement_nfc_watch_in_blink
Patch Set: Fixes for comments from dcheng + unit tests. Created 4 years, 2 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 unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.device.nfc; 5 package org.chromium.device.nfc;
6 6
7 import android.Manifest; 7 import android.Manifest;
8 import android.annotation.TargetApi; 8 import android.annotation.TargetApi;
9 import android.app.Activity; 9 import android.app.Activity;
10 import android.content.Context; 10 import android.content.Context;
11 import android.content.pm.PackageManager; 11 import android.content.pm.PackageManager;
12 import android.nfc.FormatException; 12 import android.nfc.FormatException;
13 import android.nfc.NdefMessage;
13 import android.nfc.NfcAdapter; 14 import android.nfc.NfcAdapter;
14 import android.nfc.NfcAdapter.ReaderCallback; 15 import android.nfc.NfcAdapter.ReaderCallback;
15 import android.nfc.NfcManager; 16 import android.nfc.NfcManager;
16 import android.nfc.Tag; 17 import android.nfc.Tag;
17 import android.nfc.TagLostException; 18 import android.nfc.TagLostException;
18 import android.os.Build; 19 import android.os.Build;
19 import android.os.Process; 20 import android.os.Process;
21 import android.util.SparseArray;
20 22
21 import org.chromium.base.Log; 23 import org.chromium.base.Log;
22 import org.chromium.mojo.bindings.Callbacks; 24 import org.chromium.mojo.bindings.Callbacks;
23 import org.chromium.mojo.system.MojoException; 25 import org.chromium.mojo.system.MojoException;
24 import org.chromium.mojom.device.nfc.Nfc; 26 import org.chromium.mojom.device.nfc.Nfc;
25 import org.chromium.mojom.device.nfc.NfcClient; 27 import org.chromium.mojom.device.nfc.NfcClient;
26 import org.chromium.mojom.device.nfc.NfcError; 28 import org.chromium.mojom.device.nfc.NfcError;
27 import org.chromium.mojom.device.nfc.NfcErrorType; 29 import org.chromium.mojom.device.nfc.NfcErrorType;
28 import org.chromium.mojom.device.nfc.NfcMessage; 30 import org.chromium.mojom.device.nfc.NfcMessage;
29 import org.chromium.mojom.device.nfc.NfcPushOptions; 31 import org.chromium.mojom.device.nfc.NfcPushOptions;
30 import org.chromium.mojom.device.nfc.NfcPushTarget; 32 import org.chromium.mojom.device.nfc.NfcPushTarget;
33 import org.chromium.mojom.device.nfc.NfcWatchMode;
31 import org.chromium.mojom.device.nfc.NfcWatchOptions; 34 import org.chromium.mojom.device.nfc.NfcWatchOptions;
32 35
33 import java.io.IOException; 36 import java.io.IOException;
37 import java.io.UnsupportedEncodingException;
38 import java.util.ArrayList;
39 import java.util.List;
34 40
35 /** 41 /**
36 * Android implementation of the NFC mojo service defined in 42 * Android implementation of the NFC mojo service defined in
37 * device/nfc/nfc.mojom. 43 * device/nfc/nfc.mojom.
38 */ 44 */
39 public class NfcImpl implements Nfc { 45 public class NfcImpl implements Nfc {
40 private static final String TAG = "NfcImpl"; 46 private static final String TAG = "NfcImpl";
41 47
42 /** 48 /**
43 * Used to get instance of NFC adapter, @see android.nfc.NfcManager 49 * Used to get instance of NFC adapter, @see android.nfc.NfcManager
(...skipping 27 matching lines...) Expand all
71 * @see PendingPushOperation 77 * @see PendingPushOperation
72 */ 78 */
73 private PendingPushOperation mPendingPushOperation; 79 private PendingPushOperation mPendingPushOperation;
74 80
75 /** 81 /**
76 * Utility that provides I/O operations for a Tag. Created on demand when Ta g is found. 82 * Utility that provides I/O operations for a Tag. Created on demand when Ta g is found.
77 * @see NfcTagHandler 83 * @see NfcTagHandler
78 */ 84 */
79 private NfcTagHandler mTagHandler; 85 private NfcTagHandler mTagHandler;
80 86
87 /**
88 * Client interface used to deliver NFCMessages for registered watch operati ons.
89 * @see #watch
90 */
91 private NfcClient mClient;
92
93 /**
94 * Watcher id that is incremented for each #watch call.
95 */
96 private int mWatcherId;
97
98 /**
99 * Map of watchId <-> NfcWatchOptions. All NfcWatchOptions are matched again st tag that is in
100 * proximnity, when match algorithm (@see #matchesWatchOptions) returns true , watcher with
101 * corresponding ID would be notified using NfcClient interface.
102 * @see NfcClient#onWatch(int[] id, NfcMessage message)
103 */
104 private final SparseArray<NfcWatchOptions> mWatchers = new SparseArray<>();
105
81 public NfcImpl(Context context) { 106 public NfcImpl(Context context) {
82 int permission = 107 int permission =
83 context.checkPermission(Manifest.permission.NFC, Process.myPid() , Process.myUid()); 108 context.checkPermission(Manifest.permission.NFC, Process.myPid() , Process.myUid());
84 mHasPermission = permission == PackageManager.PERMISSION_GRANTED; 109 mHasPermission = permission == PackageManager.PERMISSION_GRANTED;
85 110
86 if (!mHasPermission || Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKA T) { 111 if (!mHasPermission || Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKA T) {
87 Log.w(TAG, "NFC operations are not permitted."); 112 Log.w(TAG, "NFC operations are not permitted.");
88 mNfcAdapter = null; 113 mNfcAdapter = null;
89 mNfcManager = null; 114 mNfcManager = null;
90 } else { 115 } else {
91 mNfcManager = (NfcManager) context.getSystemService(Context.NFC_SERV ICE); 116 mNfcManager = (NfcManager) context.getSystemService(Context.NFC_SERV ICE);
92 if (mNfcManager == null) { 117 if (mNfcManager == null) {
93 Log.w(TAG, "NFC is not supported."); 118 Log.w(TAG, "NFC is not supported.");
94 mNfcAdapter = null; 119 mNfcAdapter = null;
95 } else { 120 } else {
96 mNfcAdapter = mNfcManager.getDefaultAdapter(); 121 mNfcAdapter = mNfcManager.getDefaultAdapter();
97 } 122 }
98 } 123 }
99 } 124 }
100 125
101 /** 126 /**
102 * Sets Activity that is used to enable / disable NFC reader mode. When Acti vity is set, 127 * Sets Activity that is used to enable / disable NFC reader mode. When Acti vity is set,
103 * reader mode is disabled for old Activity and enabled for the new Activity . 128 * reader mode is disabled for old Activity and enabled for the new Activity .
104 */ 129 */
105 protected void setActivity(Activity activity) { 130 protected void setActivity(Activity activity) {
106 disableReaderMode(); 131 disableReaderMode();
107 mActivity = activity; 132 mActivity = activity;
108 enableReaderMode(); 133 enableReaderModeIfNeeded();
109 } 134 }
110 135
111 /** 136 /**
112 * Sets NfcClient. NfcClient interface is used to notify mojo NFC service cl ient when NFC 137 * Sets NfcClient. NfcClient interface is used to notify mojo NFC service cl ient when NFC
113 * device is in proximity and has NfcMessage that matches NfcWatchOptions cr iteria. 138 * device is in proximity and has NfcMessage that matches NfcWatchOptions cr iteria.
114 * @see Nfc#watch(NfcWatchOptions options, WatchResponse callback) 139 * @see Nfc#watch(NfcWatchOptions options, WatchResponse callback)
115 * 140 *
116 * @param client @see NfcClient 141 * @param client @see NfcClient
117 */ 142 */
118 @Override 143 @Override
119 public void setClient(NfcClient client) { 144 public void setClient(NfcClient client) {
120 // TODO(crbug.com/625589): Should be implemented when watch() is impleme nted. 145 mClient = client;
121 } 146 }
122 147
123 /** 148 /**
124 * Pushes NfcMessage to Tag or Peer, whenever NFC device is in proximity. At the moment, only 149 * Pushes NfcMessage to Tag or Peer, whenever NFC device is in proximity. At the moment, only
125 * passive NFC devices are supported (NfcPushTarget.TAG). 150 * passive NFC devices are supported (NfcPushTarget.TAG).
126 * 151 *
127 * @param message that should be pushed to NFC device. 152 * @param message that should be pushed to NFC device.
128 * @param options that contain information about timeout and target device t ype. 153 * @param options that contain information about timeout and target device t ype.
129 * @param callback that is used to notify when push operation is completed. 154 * @param callback that is used to notify when push operation is completed.
130 */ 155 */
131 @Override 156 @Override
132 public void push(NfcMessage message, NfcPushOptions options, PushResponse ca llback) { 157 public void push(NfcMessage message, NfcPushOptions options, PushResponse ca llback) {
133 if (!checkIfReady(callback)) return; 158 if (!checkIfReady(callback)) return;
134 159
160 if (!NfcMessageValidator.isValid(message)) {
161 callback.call(createError(NfcErrorType.INVALID_MESSAGE));
162 return;
163 }
164
135 if (options.target == NfcPushTarget.PEER) { 165 if (options.target == NfcPushTarget.PEER) {
136 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); 166 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
137 return; 167 return;
138 } 168 }
139 169
140 // If previous pending push operation is not completed, cancel it. 170 // If previous pending push operation is not completed, cancel it.
141 if (mPendingPushOperation != null) { 171 if (mPendingPushOperation != null) {
142 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED)); 172 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED));
143 } 173 }
144 174
145 mPendingPushOperation = new PendingPushOperation(message, options, callb ack); 175 mPendingPushOperation = new PendingPushOperation(message, options, callb ack);
146 enableReaderMode(); 176 enableReaderModeIfNeeded();
147 processPendingPushOperation(); 177 processPendingPushOperation();
148 } 178 }
149 179
150 /** 180 /**
151 * Cancels pending push operation. 181 * Cancels pending push operation.
152 * At the moment, only passive NFC devices are supported (NfcPushTarget.TAG) . 182 * At the moment, only passive NFC devices are supported (NfcPushTarget.TAG) .
153 * 183 *
154 * @param target @see NfcPushTarget 184 * @param target @see NfcPushTarget
155 * @param callback that is used to notify caller when cancelPush() is comple ted. 185 * @param callback that is used to notify caller when cancelPush() is comple ted.
156 */ 186 */
157 @Override 187 @Override
158 public void cancelPush(int target, CancelPushResponse callback) { 188 public void cancelPush(int target, CancelPushResponse callback) {
159 if (!checkIfReady(callback)) return; 189 if (!checkIfReady(callback)) return;
160 190
161 if (target == NfcPushTarget.PEER) { 191 if (target == NfcPushTarget.PEER) {
162 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); 192 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
163 return; 193 return;
164 } 194 }
165 195
166 if (mPendingPushOperation == null) { 196 if (mPendingPushOperation == null) {
167 callback.call(createError(NfcErrorType.NOT_FOUND)); 197 callback.call(createError(NfcErrorType.NOT_FOUND));
168 } else { 198 } else {
169 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED)); 199 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED));
170 mPendingPushOperation = null; 200 mPendingPushOperation = null;
171 callback.call(null); 201 callback.call(null);
172 disableReaderMode(); 202 disableReaderModeIfNeeded();
173 } 203 }
174 } 204 }
175 205
176 /** 206 /**
177 * Watch method allows to set filtering criteria for NfcMessages that are fo und when NFC device 207 * Watch method allows to set filtering criteria for NfcMessages that are fo und when NFC device
178 * is within proximity. On success, watch ID is returned to caller through W atchResponse 208 * is within proximity. On success, watch ID is returned to caller through W atchResponse
179 * callback. When NfcMessage that matches NfcWatchOptions is found, it is pa ssed to NfcClient 209 * callback. When NfcMessage that matches NfcWatchOptions is found, it is pa ssed to NfcClient
180 * interface together with corresponding watch ID. 210 * interface together with corresponding watch ID.
181 * @see NfcClient#onWatch(int[] id, NfcMessage message) 211 * @see NfcClient#onWatch(int[] id, NfcMessage message)
182 * 212 *
183 * @param options used to filter NfcMessages, @see NfcWatchOptions. 213 * @param options used to filter NfcMessages, @see NfcWatchOptions.
184 * @param callback that is used to notify caller when watch() is completed a nd return watch ID. 214 * @param callback that is used to notify caller when watch() is completed a nd return watch ID.
185 */ 215 */
186 @Override 216 @Override
187 public void watch(NfcWatchOptions options, WatchResponse callback) { 217 public void watch(NfcWatchOptions options, WatchResponse callback) {
188 if (!checkIfReady(callback)) return; 218 if (!checkIfReady(callback)) return;
189 // TODO(crbug.com/625589): Not implemented. 219 mWatchers.put(++mWatcherId, options);
190 callback.call(0, createError(NfcErrorType.NOT_SUPPORTED)); 220 callback.call(mWatcherId, null);
Reilly Grant (use Gerrit) 2016/09/29 04:38:02 This logic makes me nervous. I suggest instead: i
shalamov 2016/09/30 11:51:35 Done.
221 enableReaderModeIfNeeded();
222 processPendingWatchOperations();
191 } 223 }
192 224
193 /** 225 /**
194 * Cancels NFC watch operation. 226 * Cancels NFC watch operation.
195 * 227 *
196 * @param id of watch operation. 228 * @param id of watch operation.
197 * @param callback that is used to notify caller when cancelWatch() is compl eted. 229 * @param callback that is used to notify caller when cancelWatch() is compl eted.
198 */ 230 */
199 @Override 231 @Override
200 public void cancelWatch(int id, CancelWatchResponse callback) { 232 public void cancelWatch(int id, CancelWatchResponse callback) {
201 if (!checkIfReady(callback)) return; 233 if (!checkIfReady(callback)) return;
202 // TODO(crbug.com/625589): Not implemented. 234
203 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); 235 if (mWatchers.indexOfKey(id) < 0) {
236 callback.call(createError(NfcErrorType.NOT_FOUND));
237 } else {
238 mWatchers.remove(id);
239 callback.call(null);
240 disableReaderModeIfNeeded();
241 }
204 } 242 }
205 243
206 /** 244 /**
207 * Cancels all NFC watch operations. 245 * Cancels all NFC watch operations.
208 * 246 *
209 * @param callback that is used to notify caller when cancelAllWatches() is completed. 247 * @param callback that is used to notify caller when cancelAllWatches() is completed.
210 */ 248 */
211 @Override 249 @Override
212 public void cancelAllWatches(CancelAllWatchesResponse callback) { 250 public void cancelAllWatches(CancelAllWatchesResponse callback) {
213 if (!checkIfReady(callback)) return; 251 if (!checkIfReady(callback)) return;
214 // TODO(crbug.com/625589): Not implemented. 252
215 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); 253 if (mWatchers.size() == 0) {
254 callback.call(createError(NfcErrorType.NOT_FOUND));
255 } else {
256 mWatchers.clear();
257 callback.call(null);
258 disableReaderModeIfNeeded();
259 }
216 } 260 }
217 261
218 /** 262 /**
219 * Suspends all pending operations. Should be called when web page visibilit y is lost. 263 * Suspends all pending operations. Should be called when web page visibilit y is lost.
220 */ 264 */
221 @Override 265 @Override
222 public void suspendNfcOperations() { 266 public void suspendNfcOperations() {
223 disableReaderMode(); 267 disableReaderMode();
224 } 268 }
225 269
226 /** 270 /**
227 * Resumes all pending watch / push operations. Should be called when web pa ge becomes visible. 271 * Resumes all pending watch / push operations. Should be called when web pa ge becomes visible.
228 */ 272 */
229 @Override 273 @Override
230 public void resumeNfcOperations() { 274 public void resumeNfcOperations() {
231 enableReaderMode(); 275 enableReaderModeIfNeeded();
232 } 276 }
233 277
234 @Override 278 @Override
235 public void close() { 279 public void close() {
236 disableReaderMode(); 280 disableReaderMode();
237 } 281 }
238 282
239 @Override 283 @Override
240 public void onConnectionError(MojoException e) { 284 public void onConnectionError(MojoException e) {
241 close(); 285 close();
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 @Override 380 @Override
337 public void onTagDiscovered(Tag tag) { 381 public void onTagDiscovered(Tag tag) {
338 mNfcImpl.onTagDiscovered(tag); 382 mNfcImpl.onTagDiscovered(tag);
339 } 383 }
340 } 384 }
341 385
342 /** 386 /**
343 * Enables reader mode, allowing NFC device to read / write NFC tags. 387 * Enables reader mode, allowing NFC device to read / write NFC tags.
344 * @see android.nfc.NfcAdapter#enableReaderMode 388 * @see android.nfc.NfcAdapter#enableReaderMode
345 */ 389 */
346 private void enableReaderMode() { 390 private void enableReaderModeIfNeeded() {
347 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; 391 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
348 392
349 if (mReaderCallbackHandler != null || mActivity == null || mNfcAdapter = = null) return; 393 if (mReaderCallbackHandler != null || mActivity == null || mNfcAdapter = = null) return;
350 394
351 // TODO(crbug.com/625589): Check if there are active watch operations. 395 // Do not enable reader mode, if there are no active push / watch operat ions.
352 if (mPendingPushOperation == null) return; 396 if (mPendingPushOperation == null && mWatchers.size() == 0) return;
353 397
354 mReaderCallbackHandler = new ReaderCallbackHandler(this); 398 mReaderCallbackHandler = new ReaderCallbackHandler(this);
355 mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler, 399 mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler,
356 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B 400 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B
357 | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_ NFC_V, 401 | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_ NFC_V,
358 null); 402 null);
359 } 403 }
360 404
361 /** 405 /**
362 * Disables reader mode. 406 * Disables reader mode.
363 * @see android.nfc.NfcAdapter#disableReaderMode 407 * @see android.nfc.NfcAdapter#disableReaderMode
364 */ 408 */
365 @TargetApi(Build.VERSION_CODES.KITKAT) 409 @TargetApi(Build.VERSION_CODES.KITKAT)
366 private void disableReaderMode() { 410 private void disableReaderMode() {
367 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; 411 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
368 412
369 // There is no API that could query whether reader mode is enabled for a dapter. 413 // There is no API that could query whether reader mode is enabled for a dapter.
370 // If mReaderCallbackHandler is null, reader mode is not enabled. 414 // If mReaderCallbackHandler is null, reader mode is not enabled.
371 if (mReaderCallbackHandler == null) return; 415 if (mReaderCallbackHandler == null) return;
372 416
373 mReaderCallbackHandler = null; 417 mReaderCallbackHandler = null;
374 if (mActivity != null && mNfcAdapter != null && !mActivity.isDestroyed() ) { 418 if (mActivity != null && mNfcAdapter != null && !mActivity.isDestroyed() ) {
375 mNfcAdapter.disableReaderMode(mActivity); 419 mNfcAdapter.disableReaderMode(mActivity);
376 } 420 }
377 } 421 }
378 422
379 /** 423 /**
424 * Checks if there are pending push / watch operations and disables readre m ode
425 * whenever necessary.
426 */
427 private void disableReaderModeIfNeeded() {
428 if (mPendingPushOperation == null && mWatchers.size() == 0) {
429 disableReaderMode();
430 }
431 }
432
433 /**
380 * Completes pending push operation. On error, invalidates #mTagHandler. 434 * Completes pending push operation. On error, invalidates #mTagHandler.
381 */ 435 */
382 private void pendingPushOperationCompleted(NfcError error) { 436 private void pendingPushOperationCompleted(NfcError error) {
383 if (mPendingPushOperation != null) { 437 if (mPendingPushOperation != null) {
384 mPendingPushOperation.complete(error); 438 mPendingPushOperation.complete(error);
385 mPendingPushOperation = null; 439 mPendingPushOperation = null;
386 440 disableReaderModeIfNeeded();
387 // TODO(crbug.com/625589): When nfc.watch is implemented, disable re ader mode
388 // only when there are no active watch operations.
389 disableReaderMode();
390 } 441 }
391 442
392 if (error != null) mTagHandler = null; 443 if (error != null) mTagHandler = null;
393 } 444 }
394 445
395 /** 446 /**
396 * Checks whether there is a #mPendingPushOperation and writes data to NFC t ag. In case of 447 * Checks whether there is a #mPendingPushOperation and writes data to NFC t ag. In case of
397 * exception calls pendingPushOperationCompleted() with appropriate error ob ject. 448 * exception calls pendingPushOperationCompleted() with appropriate error ob ject.
398 */ 449 */
399 private void processPendingPushOperation() { 450 private void processPendingPushOperation() {
400 if (mTagHandler == null || mPendingPushOperation == null) return; 451 if (mTagHandler == null || mPendingPushOperation == null) return;
401 452
402 if (mTagHandler.isTagOutOfRange()) { 453 if (mTagHandler.isTagOutOfRange()) {
403 mTagHandler = null; 454 mTagHandler = null;
404 return; 455 return;
405 } 456 }
406 457
407 try { 458 try {
408 mTagHandler.connect(); 459 mTagHandler.connect();
409 mTagHandler.write(NfcTypeConverter.toNdefMessage(mPendingPushOperati on.nfcMessage)); 460 mTagHandler.write(NfcTypeConverter.toNdefMessage(mPendingPushOperati on.nfcMessage));
410 pendingPushOperationCompleted(null); 461 pendingPushOperationCompleted(null);
411 mTagHandler.close();
412 } catch (InvalidNfcMessageException e) { 462 } catch (InvalidNfcMessageException e) {
413 Log.w(TAG, "Cannot write data to NFC tag. Invalid NfcMessage."); 463 Log.w(TAG, "Cannot write data to NFC tag. Invalid NfcMessage.");
414 pendingPushOperationCompleted(createError(NfcErrorType.INVALID_MESSA GE)); 464 pendingPushOperationCompleted(createError(NfcErrorType.INVALID_MESSA GE));
415 } catch (TagLostException e) { 465 } catch (TagLostException e) {
416 Log.w(TAG, "Cannot write data to NFC tag. Tag is lost."); 466 Log.w(TAG, "Cannot write data to NFC tag. Tag is lost.");
417 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR)); 467 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
418 } catch (FormatException | IOException e) { 468 } catch (FormatException | IOException e) {
419 Log.w(TAG, "Cannot write data to NFC tag. IO_ERROR."); 469 Log.w(TAG, "Cannot write data to NFC tag. IO_ERROR.");
420 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR)); 470 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
421 } 471 }
422 } 472 }
423 473
424 /** 474 /**
475 * Reads NfcMessage from a tag and forwards message to matching method.
476 */
477 private void processPendingWatchOperations() {
478 if (mTagHandler == null || mClient == null || mWatchers.size() == 0) ret urn;
479
480 // Skip reading if there is a pending push operation and ignoreRead flag is set.
481 if (mPendingPushOperation != null && mPendingPushOperation.nfcPushOption s.ignoreRead) {
482 return;
483 }
484
485 if (mTagHandler.isTagOutOfRange()) {
486 mTagHandler = null;
487 return;
488 }
489
490 NdefMessage message = null;
491
492 try {
493 mTagHandler.connect();
494 message = mTagHandler.read();
495 if (message.getByteArrayLength() > NfcMessage.MAX_SIZE) {
496 Log.w(TAG, "Cannot read data from NFC tag. NfcMessage exceeds al lowed size.");
497 return;
498 }
499 } catch (TagLostException e) {
500 Log.w(TAG, "Cannot read data from NFC tag. Tag is lost.");
501 } catch (FormatException | IOException e) {
502 Log.w(TAG, "Cannot read data from NFC tag. IO_ERROR.");
503 }
504
505 if (message != null) notifyMatchingWatchers(message);
506 }
507
508 /**
509 * Iterates through active watchers and if any of those match NfcWatchOption s criteria,
510 * delivers NfcMessage to the client.
511 */
512 private void notifyMatchingWatchers(NdefMessage message) {
513 try {
514 NfcMessage nfcMessage = NfcTypeConverter.toNfcMessage(message);
515 List<Integer> watchIds = new ArrayList<Integer>();
516 for (int i = 0; i < mWatchers.size(); i++) {
517 NfcWatchOptions options = mWatchers.valueAt(i);
518 if (matchesWatchOptions(nfcMessage, options)) watchIds.add(mWatc hers.keyAt(i));
519 }
520
521 if (watchIds.size() != 0) {
522 int[] ids = new int[watchIds.size()];
523 for (int i = 0; i < watchIds.size(); ++i) {
524 ids[i] = watchIds.get(i).intValue();
525 }
526 mClient.onWatch(ids, nfcMessage);
527 }
528 } catch (UnsupportedEncodingException e) {
529 Log.w(TAG, "Cannot convert NdefMessage to NfcMessage.");
530 }
531 }
532
533 /**
534 * Implements matching algorithm.
535 */
536 private boolean matchesWatchOptions(NfcMessage message, NfcWatchOptions opti ons) {
537 // Valid WebNFC message must have non-empty url.
538 if (options.mode == NfcWatchMode.WEBNFC_ONLY
539 && (message.url == null || message.url.isEmpty())) {
540 return false;
541 }
542
543 // Filter by NfcMessage.url
544 if (options.url != null && !options.url.isEmpty() && !options.url.equals (message.url)) {
545 return false;
546 }
547
548 // Matches any record / media type.
549 if ((options.mediaType == null || options.mediaType.isEmpty())
550 && options.recordFilter == null) {
551 return true;
552 }
553
554 // Filter by mediaType and recordType
555 for (int i = 0; i < message.data.length; i++) {
556 boolean matchedMediaType;
557 boolean matchedRecordType;
558
559 if (options.mediaType == null || options.mediaType.isEmpty()) {
560 // If media type for the watch options is empty, match all media types.
561 matchedMediaType = true;
562 } else {
563 matchedMediaType = options.mediaType.equals(message.data[i].medi aType);
564 }
565
566 if (options.recordFilter == null) {
567 // If record type filter for the watch options is null, match al l record types.
568 matchedRecordType = true;
569 } else {
570 matchedRecordType = options.recordFilter.recordType == message.d ata[i].recordType;
571 }
572
573 if (matchedMediaType && matchedRecordType) return true;
574 }
575
576 return false;
577 }
578
579 /**
425 * Called by ReaderCallbackHandler when NFC tag is in proximity. 580 * Called by ReaderCallbackHandler when NFC tag is in proximity.
426 */ 581 */
427 public void onTagDiscovered(Tag tag) { 582 public void onTagDiscovered(Tag tag) {
428 mTagHandler = NfcTagHandler.create(tag); 583 processPendingOperations(NfcTagHandler.create(tag));
584 }
585
586 /**
587 * Processes pending operation when NFC tag is in proximity.
588 */
589 protected void processPendingOperations(NfcTagHandler tagHandler) {
590 mTagHandler = tagHandler;
591 processPendingWatchOperations();
429 processPendingPushOperation(); 592 processPendingPushOperation();
593 if (mTagHandler != null && mTagHandler.isConnected()) {
594 try {
595 mTagHandler.close();
596 } catch (IOException e) {
597 Log.w(TAG, "Cannot close NFC tag connection.");
598 }
599 }
430 } 600 }
431 } 601 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698