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 |