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

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: Fixes for review comments. Created 4 years, 7 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 object that is requred to enable / disable NFC reader mode opera tions.
55 */
56 private final Activity mActivity;
Ted C 2016/05/12 18:23:20 this has the same problem I mentioned before. the
Yusuf 2016/05/12 22:06:25 Another solution would be to construct the impl cl
57
58 /**
59 * Flag that indicates whether NFC permission is granted.
60 */
61 private final boolean mHasPermission;
62
63 /**
64 * Implementation of android.nfc.NfcAdapter.ReaderCallback. @see ReaderCallb ackHandler
65 */
66 private ReaderCallbackHandler mReaderCallbackHandler;
67
68 /**
69 * Object that contains data that was passed to method
70 * #push(NfcMessage message, NfcPushOptions options, PushResponse callback)
71 * @see PendingPushOperation
72 */
73 private PendingPushOperation mPendingPushOperation;
74
75 /**
76 * Utility that provides I/O operations for a Tag. Created on demand when Ta g is found.
77 * @see NfcTagHandler
78 */
79 private NfcTagHandler mTagHandler;
80
81 public NfcImpl(Activity activity) {
82 mActivity = activity;
83
84 if (mActivity != null) {
85 int permission = mActivity.checkPermission(
86 Manifest.permission.NFC, Process.myPid(), Process.myUid());
87 mHasPermission = permission == PackageManager.PERMISSION_GRANTED;
88 } else {
89 mHasPermission = false;
90 }
91
92 if (mHasPermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKA T) {
93 mNfcManager = (NfcManager) mActivity.getSystemService(Context.NFC_SE RVICE);
94 if (mNfcManager != null) {
95 mNfcAdapter = mNfcManager.getDefaultAdapter();
96 } else {
97 Log.w(TAG, "NFC is not supported.");
98 mNfcAdapter = null;
99 }
100 } else {
101 Log.w(TAG, "NFC operations are not permitted.");
102 mNfcAdapter = null;
103 mNfcManager = null;
104 }
105 }
106
107 /**
108 * Sets NfcClient. NfcClient interface is used to notify mojo NFC service cl ient when NFC
109 * device is in proximity and has NfcMessage that matches NfcWatchOptions cr iteria.
110 * @see Nfc#watch(NfcWatchOptions options, WatchResponse callback)
111 *
112 * @param client @see NfcClient
113 */
114 @Override
115 public void setClient(NfcClient client) {
116 // TODO(shalamov): Should be implemented when watch() is implemented.
117 }
118
119 /**
120 * Pushes NfcMessage to Tag or Peer, whenever NFC device is in proximity. At the moment, only
121 * passive NFC devices are supported (NfcPushTarget.TAG).
122 *
123 * @param message that should be pushed to NFC device.
124 * @param options that contain information about timeout and target device t ype.
125 * @param callback that is used to notify when push operation is completed.
126 */
127 @Override
128 public void push(NfcMessage message, NfcPushOptions options, PushResponse ca llback) {
129 if (!checkIfReady(callback)) return;
130
131 if (options.target == NfcPushTarget.PEER) {
132 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
133 return;
134 }
135
136 // If previous pending push operation is not completed, subsequent call
137 // should cancel pending operation.
138 if (mPendingPushOperation != null) {
139 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED));
140 }
141
142 mPendingPushOperation = new PendingPushOperation(message, options, callb ack);
143 enableReaderMode();
144 processPendingPushOperation();
145 }
146
147 /**
148 * Cancels pending push operation.
149 * At the moment, only passive NFC devices are supported (NfcPushTarget.TAG) .
150 *
151 * @param target @see NfcPushTarget
152 * @param callback that is used to notify caller when cancelPush() is comple ted.
153 */
154 @Override
155 public void cancelPush(int target, CancelPushResponse callback) {
156 if (!checkIfReady(callback)) return;
157
158 if (target == NfcPushTarget.PEER) {
159 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
160 return;
161 }
162
163 if (mPendingPushOperation != null) {
164 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED));
165 mPendingPushOperation = null;
166 callback.call(null);
167 disableReaderMode();
168 } else {
169 callback.call(createError(NfcErrorType.NOT_FOUND));
170 }
171 }
172
173 /**
174 * Watch method allows to set filtering criteria for NfcMessages that are fo und when NFC device
175 * is within proximity. On success, watch ID is returned to caller through W atchResponse
176 * callback. When NfcMessage that matches NfcWatchOptions is found, it is pa ssed to NfcClient
177 * interface together with corresponding watch ID.
178 * @see NfcClient#onWatch(int[] id, NfcMessage message)
179 *
180 * @param options used to filter NfcMessages, @see NfcWatchOptions.
181 * @param callback that is used to notify caller when watch() is completed a nd return watch ID.
182 */
183 @Override
184 public void watch(NfcWatchOptions options, WatchResponse callback) {
185 if (!checkIfReady(callback)) return;
186 // TODO(shalamov): Not implemented.
187 callback.call(0, createError(NfcErrorType.NOT_SUPPORTED));
188 }
189
190 /**
191 * Cancels NFC watch operation.
192 *
193 * @param id of watch operation.
194 * @param callback that is used to notify caller when cancelWatch() is compl eted.
195 */
196 @Override
197 public void cancelWatch(int id, CancelWatchResponse callback) {
198 if (!checkIfReady(callback)) return;
199 // TODO(shalamov): Not implemented.
200 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
201 }
202
203 /**
204 * Cancels all NFC watch operations.
205 *
206 * @param callback that is used to notify caller when cancelAllWatches() is completed.
207 */
208 @Override
209 public void cancelAllWatches(CancelAllWatchesResponse callback) {
210 if (!checkIfReady(callback)) return;
211 // TODO(shalamov): Not implemented.
212 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
213 }
214
215 /**
216 * Suspends all pending operations. Should be called when web page visibilit y is lost.
217 */
218 @Override
219 public void suspendNfcOperations() {
220 disableReaderMode();
221 }
222
223 /**
224 * Resumes all pending watch / push operations. Should be called when web pa ge becomes visible.
225 */
226 @Override
227 public void resumeNfcOperations() {
228 if (mPendingPushOperation != null) enableReaderMode();
229 // TODO(shalamov): check if there are active watchers.
230 }
231
232 @Override
233 public void close() {
234 disableReaderMode();
235 }
236
237 @Override
238 public void onConnectionError(MojoException e) {
239 close();
240 }
241
242 /**
243 * Holds information about pending push operation.
244 */
245 private static class PendingPushOperation {
246 public final NfcMessage nfcMessage;
247 public final NfcPushOptions nfcPushOptions;
248 private final PushResponse mPushResponseCallback;
249
250 public PendingPushOperation(
251 NfcMessage message, NfcPushOptions options, PushResponse callbac k) {
252 nfcMessage = message;
253 nfcPushOptions = options;
254 mPushResponseCallback = callback;
255 }
256
257 /**
258 * Completes pending push operation.
259 *
260 * @param error should be null when operation is completed successfully, otherwise,
261 * error object with corresponding NfcErrorType should be provided.
262 */
263 public void complete(NfcError error) {
264 if (mPushResponseCallback != null) mPushResponseCallback.call(error) ;
265 }
266 }
267
268 /**
269 * Helper method that creates NfcError object from NfcErrorType.
270 */
271 private NfcError createError(int errorType) {
272 NfcError error = new NfcError();
273 error.errorType = errorType;
274 return error;
275 }
276
277 /**
278 * Checks if NFC funcionality can be used by the mojo service. If permission to use NFC is
279 * granted and hardware is enabled, returns null.
280 */
281 @Nullable
282 private NfcError checkIfReady() {
283 if (!mHasPermission) {
284 return createError(NfcErrorType.SECURITY);
285 } else if (mNfcManager == null || mNfcAdapter == null) {
286 return createError(NfcErrorType.NOT_SUPPORTED);
287 } else if (!mNfcAdapter.isEnabled()) {
288 return createError(NfcErrorType.DEVICE_DISABLED);
289 }
290 return null;
291 }
292
293 /**
294 * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback with NfcError.
295 *
296 * @param WatchResponse Callback that is provided to watch() method.
297 * @return boolean true if NFC functionality can be used, false otherwise.
298 */
299 private boolean checkIfReady(WatchResponse callback) {
300 NfcError error = checkIfReady();
301 if (error == null) return true;
302 callback.call(0, error);
303 return false;
304 }
305
306 /**
307 * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback with NfcError.
308 *
309 * @param callback Generic callback that is provided to push(), cancelPush() ,
310 * cancelWatch() and cancelAllWatches() methods.
311 * @return boolean true if NFC functionality can be used, false otherwise.
312 */
313 private boolean checkIfReady(Callbacks.Callback1<NfcError> callback) {
314 NfcError error = checkIfReady();
315 if (error == null) return true;
316 callback.call(error);
317 return false;
318 }
319
320 /**
321 * Implementation of android.nfc.NfcAdapter.ReaderCallback. Callback is call ed when NFC tag is
322 * discovered, Tag object is delegated to mojo service implementation method
323 * NfcImpl.onTagDiscovered().
324 */
325 @TargetApi(Build.VERSION_CODES.KITKAT)
326 private static class ReaderCallbackHandler implements ReaderCallback {
327 private final NfcImpl mNfcImpl;
328
329 public ReaderCallbackHandler(NfcImpl impl) {
330 mNfcImpl = impl;
331 }
332
333 @Override
334 public void onTagDiscovered(Tag tag) {
335 mNfcImpl.onTagDiscovered(tag);
336 }
337 }
338
339 /**
340 * Enables reader mode, allowing NFC device to read / write NFC tags.
341 * @see android.nfc.NfcAdapter#enableReaderMode
342 */
343 private void enableReaderMode() {
344 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
345 if (mReaderCallbackHandler != null || mActivity == null) return;
346
347 mReaderCallbackHandler = new ReaderCallbackHandler(this);
348 mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler,
349 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B
350 | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_ NFC_V,
351 null);
352 }
353
354 /**
355 * Disables reader mode.
356 * @see android.nfc.NfcAdapter#disableReaderMode
357 */
358 private void disableReaderMode() {
359 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
360
361 mReaderCallbackHandler = null;
362 if (mActivity != null) {
363 mNfcAdapter.disableReaderMode(mActivity);
364 }
365 }
366
367 /**
368 * Completes pending push operation. On error, invalidates #mTagHandler.
369 */
370 private void pendingPushOperationCompleted(NfcError error) {
371 if (mPendingPushOperation != null) {
372 mPendingPushOperation.complete(error);
373 mPendingPushOperation = null;
374
375 // TODO(shalamov): When nfc.watch is implemented, disable reader mod e
376 // only when there are no active watch operations.
377 disableReaderMode();
378 }
379
380 if (error != null) mTagHandler = null;
381 }
382
383 /**
384 * Checks whether there is a #mPendingPushOperation and writes data to NFC t ag. In case of
385 * exception calls pendingPushOperationCompleted() with appropriate error ob ject.
386 */
387 private void processPendingPushOperation() {
388 if (mTagHandler == null || mPendingPushOperation == null) return;
389
390 if (mTagHandler.isTagOutOfRange()) {
391 mTagHandler = null;
392 return;
393 }
394
395 try {
396 mTagHandler.connect();
397 mTagHandler.write(NfcTypeConverter.toNdefMessage(mPendingPushOperati on.nfcMessage));
398 pendingPushOperationCompleted(null);
399 mTagHandler.close();
400 } catch (InvalidNfcMessageException e) {
401 Log.w(TAG, "Cannot write data to NFC tag. Invalid NfcMessage.");
402 pendingPushOperationCompleted(createError(NfcErrorType.INVALID_MESSA GE));
403 } catch (TagLostException e) {
404 Log.w(TAG, "Cannot write data to NFC tag. Tag is lost.");
405 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
406 } catch (FormatException | IOException e) {
407 Log.w(TAG, "Cannot write data to NFC tag. IO_ERROR.");
408 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
409 }
410 }
411
412 /**
413 * Called by ReaderCallbackHandler when NFC tag is in proximity.
414 */
415 public void onTagDiscovered(Tag tag) {
416 mTagHandler = NfcTagHandler.create(tag);
417 processPendingPushOperation();
418 }
419 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698