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

Side by Side 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: Rebased to master. Created 4 years, 5 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
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698