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

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: Move WindowAndroidChangedObserver to other observers in content layer Created 4 years, 4 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
21 import org.chromium.base.Log;
22 import org.chromium.mojo.bindings.Callbacks;
23 import org.chromium.mojo.system.MojoException;
24 import org.chromium.mojom.device.nfc.Nfc;
25 import org.chromium.mojom.device.nfc.NfcClient;
26 import org.chromium.mojom.device.nfc.NfcError;
27 import org.chromium.mojom.device.nfc.NfcErrorType;
28 import org.chromium.mojom.device.nfc.NfcMessage;
29 import org.chromium.mojom.device.nfc.NfcPushOptions;
30 import org.chromium.mojom.device.nfc.NfcPushTarget;
31 import org.chromium.mojom.device.nfc.NfcWatchOptions;
32
33 import java.io.IOException;
34
35 /**
36 * Android implementation of the NFC mojo service defined in
37 * device/nfc/nfc.mojom.
38 */
39 public class NfcImpl implements Nfc {
40 private static final String TAG = "NfcImpl";
41
42 /**
43 * Used to get instance of NFC adapter, @see android.nfc.NfcManager
44 */
45 private final NfcManager mNfcManager;
46
47 /**
48 * NFC adapter. @see android.nfc.NfcAdapter
49 */
50 private final NfcAdapter mNfcAdapter;
51
52 /**
53 * Activity that is in foreground and is used to enable / disable NFC reader mode operations.
54 * Can be updated when activity associated with web page is changed. @see #s etActivity
55 */
56 private Activity mActivity;
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(Context context) {
82 int permission =
83 context.checkPermission(Manifest.permission.NFC, Process.myPid() , Process.myUid());
84 mHasPermission = permission == PackageManager.PERMISSION_GRANTED;
85
86 if (!mHasPermission || Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKA T) {
87 Log.w(TAG, "NFC operations are not permitted.");
88 mNfcAdapter = null;
89 mNfcManager = null;
90 } else {
91 mNfcManager = (NfcManager) context.getSystemService(Context.NFC_SERV ICE);
92 if (mNfcManager == null) {
93 Log.w(TAG, "NFC is not supported.");
94 mNfcAdapter = null;
95 } else {
96 mNfcAdapter = mNfcManager.getDefaultAdapter();
97 }
98 }
99 }
100
101 /**
102 * 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 .
104 */
105 protected void setActivity(Activity activity) {
106 disableReaderMode();
107 mActivity = activity;
108 enableReaderMode();
109 }
110
111 /**
112 * 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.
114 * @see Nfc#watch(NfcWatchOptions options, WatchResponse callback)
115 *
116 * @param client @see NfcClient
117 */
118 @Override
119 public void setClient(NfcClient client) {
120 // TODO(crbug.com/625589): Should be implemented when watch() is impleme nted.
121 }
122
123 /**
124 * Pushes NfcMessage to Tag or Peer, whenever NFC device is in proximity. At the moment, only
125 * passive NFC devices are supported (NfcPushTarget.TAG).
126 *
127 * @param message that should be pushed to NFC device.
128 * @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.
130 */
131 @Override
132 public void push(NfcMessage message, NfcPushOptions options, PushResponse ca llback) {
133 if (!checkIfReady(callback)) return;
134
135 if (options.target == NfcPushTarget.PEER) {
136 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
137 return;
138 }
139
140 // If previous pending push operation is not completed, cancel it.
141 if (mPendingPushOperation != null) {
142 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED));
143 }
144
145 mPendingPushOperation = new PendingPushOperation(message, options, callb ack);
146 enableReaderMode();
147 processPendingPushOperation();
148 }
149
150 /**
151 * Cancels pending push operation.
152 * At the moment, only passive NFC devices are supported (NfcPushTarget.TAG) .
153 *
154 * @param target @see NfcPushTarget
155 * @param callback that is used to notify caller when cancelPush() is comple ted.
156 */
157 @Override
158 public void cancelPush(int target, CancelPushResponse callback) {
159 if (!checkIfReady(callback)) return;
160
161 if (target == NfcPushTarget.PEER) {
162 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
163 return;
164 }
165
166 if (mPendingPushOperation == null) {
167 callback.call(createError(NfcErrorType.NOT_FOUND));
168 } else {
169 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED));
170 mPendingPushOperation = null;
171 callback.call(null);
172 disableReaderMode();
173 }
174 }
175
176 /**
177 * 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
179 * callback. When NfcMessage that matches NfcWatchOptions is found, it is pa ssed to NfcClient
180 * interface together with corresponding watch ID.
181 * @see NfcClient#onWatch(int[] id, NfcMessage message)
182 *
183 * @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.
185 */
186 @Override
187 public void watch(NfcWatchOptions options, WatchResponse callback) {
188 if (!checkIfReady(callback)) return;
189 // TODO(crbug.com/625589): Not implemented.
190 callback.call(0, createError(NfcErrorType.NOT_SUPPORTED));
191 }
192
193 /**
194 * Cancels NFC watch operation.
195 *
196 * @param id of watch operation.
197 * @param callback that is used to notify caller when cancelWatch() is compl eted.
198 */
199 @Override
200 public void cancelWatch(int id, CancelWatchResponse callback) {
201 if (!checkIfReady(callback)) return;
202 // TODO(crbug.com/625589): Not implemented.
203 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
204 }
205
206 /**
207 * Cancels all NFC watch operations.
208 *
209 * @param callback that is used to notify caller when cancelAllWatches() is completed.
210 */
211 @Override
212 public void cancelAllWatches(CancelAllWatchesResponse callback) {
213 if (!checkIfReady(callback)) return;
214 // TODO(crbug.com/625589): Not implemented.
215 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
216 }
217
218 /**
219 * Suspends all pending operations. Should be called when web page visibilit y is lost.
220 */
221 @Override
222 public void suspendNfcOperations() {
223 disableReaderMode();
224 }
225
226 /**
227 * Resumes all pending watch / push operations. Should be called when web pa ge becomes visible.
228 */
229 @Override
230 public void resumeNfcOperations() {
231 enableReaderMode();
232 }
233
234 @Override
235 public void close() {
236 disableReaderMode();
237 }
238
239 @Override
240 public void onConnectionError(MojoException e) {
241 close();
242 }
243
244 /**
245 * Holds information about pending push operation.
246 */
247 private static class PendingPushOperation {
248 public final NfcMessage nfcMessage;
249 public final NfcPushOptions nfcPushOptions;
250 private final PushResponse mPushResponseCallback;
251
252 public PendingPushOperation(
253 NfcMessage message, NfcPushOptions options, PushResponse callbac k) {
254 nfcMessage = message;
255 nfcPushOptions = options;
256 mPushResponseCallback = callback;
257 }
258
259 /**
260 * Completes pending push operation.
261 *
262 * @param error should be null when operation is completed successfully, otherwise,
263 * error object with corresponding NfcErrorType should be provided.
264 */
265 public void complete(NfcError error) {
266 if (mPushResponseCallback != null) mPushResponseCallback.call(error) ;
267 }
268 }
269
270 /**
271 * Helper method that creates NfcError object from NfcErrorType.
272 */
273 private NfcError createError(int errorType) {
274 NfcError error = new NfcError();
275 error.errorType = errorType;
276 return error;
277 }
278
279 /**
280 * Checks if NFC funcionality can be used by the mojo service. If permission to use NFC is
281 * granted and hardware is enabled, returns null.
282 */
283 private NfcError checkIfReady() {
284 if (!mHasPermission || mActivity == null) {
285 return createError(NfcErrorType.SECURITY);
286 } else if (mNfcManager == null || mNfcAdapter == null) {
287 return createError(NfcErrorType.NOT_SUPPORTED);
288 } else if (!mNfcAdapter.isEnabled()) {
289 return createError(NfcErrorType.DEVICE_DISABLED);
290 }
291 return null;
292 }
293
294 /**
295 * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback with NfcError.
296 *
297 * @param WatchResponse Callback that is provided to watch() method.
298 * @return boolean true if NFC functionality can be used, false otherwise.
299 */
300 private boolean checkIfReady(WatchResponse callback) {
301 NfcError error = checkIfReady();
302 if (error == null) return true;
303
304 callback.call(0, error);
305 return false;
306 }
307
308 /**
309 * Uses checkIfReady() method and if NFC cannot be used, calls mojo callback with NfcError.
310 *
311 * @param callback Generic callback that is provided to push(), cancelPush() ,
312 * cancelWatch() and cancelAllWatches() methods.
313 * @return boolean true if NFC functionality can be used, false otherwise.
314 */
315 private boolean checkIfReady(Callbacks.Callback1<NfcError> callback) {
316 NfcError error = checkIfReady();
317 if (error == null) return true;
318
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
349 if (mReaderCallbackHandler != null || mActivity == null) return;
350
351 // TODO(crbug.com/625589): Check if there are active watch operations.
352 if (mPendingPushOperation == null) return;
353
354 mReaderCallbackHandler = new ReaderCallbackHandler(this);
355 mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler,
356 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B
357 | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_ NFC_V,
358 null);
359 }
360
361 /**
362 * Disables reader mode.
363 * @see android.nfc.NfcAdapter#disableReaderMode
364 */
365 private void disableReaderMode() {
366 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
367
368 mReaderCallbackHandler = null;
369 if (mActivity != null) {
370 mNfcAdapter.disableReaderMode(mActivity);
371 }
372 }
373
374 /**
375 * Completes pending push operation. On error, invalidates #mTagHandler.
376 */
377 private void pendingPushOperationCompleted(NfcError error) {
378 if (mPendingPushOperation != null) {
379 mPendingPushOperation.complete(error);
380 mPendingPushOperation = null;
381
382 // TODO(crbug.com/625589): When nfc.watch is implemented, disable re ader mode
383 // only when there are no active watch operations.
384 disableReaderMode();
385 }
386
387 if (error != null) mTagHandler = null;
388 }
389
390 /**
391 * Checks whether there is a #mPendingPushOperation and writes data to NFC t ag. In case of
392 * exception calls pendingPushOperationCompleted() with appropriate error ob ject.
393 */
394 private void processPendingPushOperation() {
395 if (mTagHandler == null || mPendingPushOperation == null) return;
396
397 if (mTagHandler.isTagOutOfRange()) {
398 mTagHandler = null;
399 return;
400 }
401
402 try {
403 mTagHandler.connect();
404 mTagHandler.write(NfcTypeConverter.toNdefMessage(mPendingPushOperati on.nfcMessage));
405 pendingPushOperationCompleted(null);
406 mTagHandler.close();
407 } catch (InvalidNfcMessageException e) {
408 Log.w(TAG, "Cannot write data to NFC tag. Invalid NfcMessage.");
409 pendingPushOperationCompleted(createError(NfcErrorType.INVALID_MESSA GE));
410 } catch (TagLostException e) {
411 Log.w(TAG, "Cannot write data to NFC tag. Tag is lost.");
412 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
413 } catch (FormatException | IOException e) {
414 Log.w(TAG, "Cannot write data to NFC tag. IO_ERROR.");
415 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
416 }
417 }
418
419 /**
420 * Called by ReaderCallbackHandler when NFC tag is in proximity.
421 */
422 public void onTagDiscovered(Tag tag) {
423 mTagHandler = NfcTagHandler.create(tag);
424 processPendingPushOperation();
425 }
426 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698