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

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. Created 4 years, 8 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 2015 The Chromium Authors. All rights reserved.
timvolodine 2016/04/26 14:58:27 2015 -> 2016
shalamov 2016/04/26 21:56:52 Done.
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.NdefMessage;
14 import android.nfc.NdefRecord;
15 import android.nfc.NfcAdapter;
16 import android.nfc.NfcAdapter.ReaderCallback;
17 import android.nfc.NfcManager;
18 import android.nfc.Tag;
19 import android.nfc.TagLostException;
20 import android.nfc.tech.Ndef;
21 import android.nfc.tech.NdefFormatable;
22 import android.nfc.tech.TagTechnology;
23 import android.os.Build;
24 import android.os.Process;
25 import android.support.annotation.Nullable;
26
27 import org.chromium.base.ApplicationStatus;
28 import org.chromium.base.Log;
29 import org.chromium.mojo.bindings.Callbacks;
30 import org.chromium.mojo.system.MojoException;
31 import org.chromium.mojom.device.Nfc;
32 import org.chromium.mojom.device.NfcClient;
33 import org.chromium.mojom.device.NfcError;
34 import org.chromium.mojom.device.NfcErrorType;
35 import org.chromium.mojom.device.NfcMessage;
36 import org.chromium.mojom.device.NfcPushOptions;
37 import org.chromium.mojom.device.NfcPushTarget;
38 import org.chromium.mojom.device.NfcRecord;
39 import org.chromium.mojom.device.NfcRecordType;
40 import org.chromium.mojom.device.NfcWatchOptions;
41
42 import java.io.IOException;
43 import java.io.UnsupportedEncodingException;
44 import java.util.ArrayList;
45 import java.util.List;
46
47 /**
48 * Android implementation of the NFC mojo service defined in
49 * device/nfc/nfc.mojom.
50 */
51 public class NfcImpl implements Nfc {
52 private static final String TAG = "NfcImpl";
53 private static final String DOMAIN = "w3.org";
54 private static final String TYPE = "webnfc";
55 private static final String TEXT_MIME = "text/plain";
56 private static final String CHARSET_UTF8 = ";charset=UTF-8";
57 private static final String CHARSET_UTF16 = ";charset=UTF-16";
58
59 private NfcManager mNfcManager;
timvolodine 2016/04/26 14:58:27 final?
shalamov 2016/04/26 21:56:53 Done.
60 private NfcAdapter mNfcAdapter;
timvolodine 2016/04/26 14:58:27 final?
shalamov 2016/04/26 21:56:53 Done.
61 private final Context mContext;
62 private Activity mActivity;
63 private boolean mHasPermission;
timvolodine 2016/04/26 14:58:26 final? same for mAcitivy above
shalamov 2016/04/26 21:56:53 Done.
64 private ReaderCallbackHandler mReaderCallbackHandler;
65 private PendingPushOperation mPendingPushOperation;
66 private NfcTagWriter mTagWriter;
ncarter (slow) 2016/04/25 20:03:14 There are very few comments in this file overall.
timvolodine 2016/04/26 14:58:26 +1, bit more documentation would be great
shalamov 2016/04/26 21:56:53 Done.
shalamov 2016/04/26 21:56:53 Done.
67
68 public NfcImpl(Context context) {
69 mContext = context;
70 int permission =
71 context.checkPermission(Manifest.permission.NFC, Process.myPid() , Process.myUid());
72 mHasPermission = permission == PackageManager.PERMISSION_GRANTED;
73 if (mHasPermission) {
74 mActivity = ApplicationStatus.getLastTrackedFocusedActivity();
75 mNfcManager = (NfcManager) mContext.getSystemService(Context.NFC_SER VICE);
76 if (mNfcManager == null) {
77 Log.w(TAG, "NFC is not supported.");
78 } else {
79 mNfcAdapter = mNfcManager.getDefaultAdapter();
80 }
81 } else {
82 Log.w(TAG, "NFC operations are not permitted.");
83 }
84 }
85
86 // Public methods
ncarter (slow) 2016/04/25 20:03:14 I don't think this is necessary.
shalamov 2016/04/26 21:56:52 Done.
87 @Override
88 public void setClient(NfcClient client) {}
89
90 @Override
91 public void push(NfcMessage message, NfcPushOptions options, PushResponse ca llback) {
92 if (!checkIfReady(callback)) return;
93
94 if (options.target == NfcPushTarget.PEER) {
95 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
96 return;
97 }
98
99 if (mPendingPushOperation != null) {
100 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED));
101 }
102
103 mPendingPushOperation = new PendingPushOperation(message, options, callb ack);
104 enableReaderMode();
105 processPendingPushOperation();
106 }
107
108 @Override
109 public void cancelPush(int target, CancelPushResponse callback) {
110 if (!checkIfReady(callback)) return;
111
112 if (target == NfcPushTarget.PEER) {
113 callback.call(createError(NfcErrorType.NOT_SUPPORTED));
114 return;
115 }
116
117 if (mPendingPushOperation != null) {
118 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED));
119 mPendingPushOperation = null;
120 callback.call(null);
121 } else {
122 callback.call(createError(NfcErrorType.NOT_FOUND));
123 }
124 }
125
126 @Override
127 public void watch(NfcWatchOptions options, WatchResponse callback) {
128 if (!checkIfReady(callback)) return;
timvolodine 2016/04/26 14:58:27 should this have a TODO? also below for cancelWatc
shalamov 2016/04/26 21:56:53 Done.
129 }
130
131 @Override
132 public void cancelWatch(int id, CancelWatchResponse callback) {
133 if (!checkIfReady(callback)) return;
134 }
135
136 @Override
137 public void cancelAllWatches(CancelAllWatchesResponse callback) {
138 if (!checkIfReady(callback)) return;
139 }
140
141 @Override
142 public void suspendNfcOperations() {}
timvolodine 2016/04/26 14:58:27 is this intentionally empty or is this a also TODO
shalamov 2016/04/26 21:56:52 Done.
143
144 @Override
145 public void resumeNfcOperations() {}
146
147 @Override
148 public void close() {}
149
150 @Override
151 public void onConnectionError(MojoException e) {}
152
153 // Implementation
ncarter (slow) 2016/04/25 20:03:14 Class comment for PendingPushOperation? Can be a o
shalamov 2016/04/26 21:56:52 Done.
154 private static class PendingPushOperation {
155 private final NfcMessage mNfcMessage;
156 private final NfcPushOptions mNfcPushOptions;
157 private final PushResponse mPushResponseCallback;
158
159 public PendingPushOperation(
160 NfcMessage message, NfcPushOptions options, PushResponse callbac k) {
161 mNfcMessage = message;
162 mNfcPushOptions = options;
163 mPushResponseCallback = callback;
164 }
165
166 public void complete(NfcError error) {
167 if (mPushResponseCallback != null) mPushResponseCallback.call(error) ;
168 }
169
170 public NfcMessage message() {
171 return mNfcMessage;
172 }
173 public NfcPushOptions pushOptions() {
174 return mNfcPushOptions;
175 }
176 }
177
178 private NfcError createError(int errorType) {
179 NfcError error = new NfcError();
180 error.errorType = errorType;
181 return error;
182 }
183
184 @Nullable
185 private NfcError checkIfReady() {
186 if (!mHasPermission) {
187 return createError(NfcErrorType.SECURITY);
188 } else if (mNfcManager == null || mNfcAdapter == null
189 || Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
190 return createError(NfcErrorType.NOT_SUPPORTED);
191 } else if (!mNfcAdapter.isEnabled()) {
192 return createError(NfcErrorType.DEVICE_DISABLED);
193 }
194 return null;
195 }
196
197 private boolean checkIfReady(Callbacks.Callback2<Integer, NfcError> callback ) {
198 NfcError error = checkIfReady();
199 if (error == null) return true;
200 callback.call(0, error);
201 return false;
202 }
203
204 private boolean checkIfReady(Callbacks.Callback1<NfcError> callback) {
205 NfcError error = checkIfReady();
206 if (error == null) return true;
207 callback.call(error);
208 return false;
209 }
210
211 @TargetApi(Build.VERSION_CODES.KITKAT)
212 private static class ReaderCallbackHandler implements ReaderCallback {
213 private final NfcImpl mNfcImpl;
214
215 public ReaderCallbackHandler(NfcImpl impl) {
216 mNfcImpl = impl;
217 }
218
219 @Override
220 public void onTagDiscovered(Tag tag) {
221 mNfcImpl.onTagDiscovered(tag);
222 }
223 }
224
225 private void enableReaderMode() {
226 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
227 if (mReaderCallbackHandler != null) return;
228
229 mReaderCallbackHandler = new ReaderCallbackHandler(this);
230 mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler,
231 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B
232 | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_ NFC_V,
233 null);
234 }
235
236 private void disableReaderMode() {
237 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
238
239 mReaderCallbackHandler = null;
240 mNfcAdapter.disableReaderMode(mActivity);
241 }
242
243 private interface TagTechnologyWriter {
244 public void write(NdefMessage message)
245 throws IOException, TagLostException, FormatException;
246 }
247
248 private static class NdefWriter implements TagTechnologyWriter {
249 private final Ndef mNdef;
250
251 NdefWriter(Ndef ndef) {
252 mNdef = ndef;
253 }
254
255 public void write(NdefMessage message)
256 throws IOException, TagLostException, FormatException {
257 mNdef.writeNdefMessage(message);
258 }
259 }
260
261 private static class NdefFormattableWriter implements TagTechnologyWriter {
262 private final NdefFormatable mNdefFormattable;
263
264 NdefFormattableWriter(NdefFormatable ndefFormattable) {
265 mNdefFormattable = ndefFormattable;
266 }
267
268 public void write(NdefMessage message)
269 throws IOException, TagLostException, FormatException {
270 mNdefFormattable.format(message);
271 }
272 }
273
274 private static class NfcTagWriter {
275 private final TagTechnology mTech;
276 private final TagTechnologyWriter mTechWriter;
277 private boolean mWasConnected = false;
278
279 public static NfcTagWriter create(Tag tag) {
280 if (tag == null) return null;
281
282 Ndef ndef = Ndef.get(tag);
283 if (ndef != null) return new NfcTagWriter(ndef, new NdefWriter(ndef) );
284
285 NdefFormatable formattable = NdefFormatable.get(tag);
286 if (formattable != null) {
287 return new NfcTagWriter(formattable, new NdefFormattableWriter(f ormattable));
288 }
289
290 return null;
291 }
292
293 private NfcTagWriter(TagTechnology tech, TagTechnologyWriter writer) {
294 mTech = tech;
295 mTechWriter = writer;
296 }
297
298 public void connect() throws IOException, TagLostException {
299 if (!mTech.isConnected()) {
300 mTech.connect();
301 mWasConnected = true;
302 }
303 }
304
305 public void close() throws IOException {
306 mTech.close();
307 }
308
309 public void write(NdefMessage message)
310 throws IOException, TagLostException, FormatException {
311 mTechWriter.write(message);
312 }
313
314 public boolean isTagOutOfRange() {
315 try {
316 connect();
317 } catch (IOException e) {
318 return mWasConnected;
319 }
320 return false;
321 }
322 }
323
324 private static class InvalidMessageException extends Exception {}
325
326 private NdefMessage toNdefMessage(NfcMessage message) throws InvalidMessageE xception {
327 if (message == null || message.data.length == 0) throw new InvalidMessag eException();
328
329 try {
330 List<NdefRecord> records = new ArrayList<NdefRecord>();
331 for (NfcRecord record : message.data) {
332 records.add(toNdefRecord(record));
333 }
334 records.add(NdefRecord.createExternal(DOMAIN, TYPE, message.url.getB ytes()));
335 NdefRecord[] ndefRecords = new NdefRecord[records.size()];
336 records.toArray(ndefRecords);
337 return new NdefMessage(ndefRecords);
338 } catch (UnsupportedEncodingException | InvalidMessageException
339 | IllegalArgumentException e) {
340 throw new InvalidMessageException();
341 }
342 }
343
344 private String getCharset(NfcRecord record) {
345 if (record.mediaType.endsWith(CHARSET_UTF8)) return "UTF-8";
346
347 if (record.mediaType.endsWith(CHARSET_UTF16)) return "UTF-16LE";
timvolodine 2016/04/26 14:58:27 why UTF-16LE? CHARSET_UTF16 doesn't specify LE.
shalamov 2016/04/26 21:56:53 When 16bit WTF::String data is converted to bytear
timvolodine 2016/04/28 17:06:34 yes utf-16be usually tends to be the default, mayb
shalamov 2016/05/11 14:09:57 Done.
348
349 Log.w(TAG, "Unknown charset, using UTF-8 by default.");
350 return "UTF-8";
351 }
352
353 private NdefRecord toNdefRecord(NfcRecord record)
354 throws InvalidMessageException, IllegalArgumentException, Unsupporte dEncodingException {
355 switch (record.recordType) {
356 case NfcRecordType.URL:
357 return NdefRecord.createUri(new String(record.data, getCharset(r ecord)));
358 case NfcRecordType.TEXT:
359 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
360 return NdefRecord.createTextRecord(
361 "en-US", new String(record.data, getCharset(record)) );
362 } else {
363 return NdefRecord.createMime(TEXT_MIME, record.data);
364 }
365 case NfcRecordType.JSON:
366 case NfcRecordType.OPAQUE_RECORD:
367 return NdefRecord.createMime(record.mediaType, record.data);
368 default:
369 throw new InvalidMessageException();
370 }
371 }
372
373 private void pendingPushOperationCompleted(NfcError error) {
374 if (mPendingPushOperation != null) {
375 mPendingPushOperation.complete(error);
376 mPendingPushOperation = null;
377 }
378
379 if (error != null) mTagWriter = null;
380 }
381
382 private void processPendingPushOperation() {
383 if (mTagWriter == null || mPendingPushOperation == null) return;
384
385 if (mTagWriter.isTagOutOfRange()) {
386 mTagWriter = null;
387 return;
388 }
389
390 try {
391 mTagWriter.connect();
392 mTagWriter.write(toNdefMessage(mPendingPushOperation.message()));
393 pendingPushOperationCompleted(null);
394 mTagWriter.close();
395 } catch (InvalidMessageException e) {
396 Log.w(TAG, "Cannot write data to NFC tag. Invalid NfcMessage.");
397 pendingPushOperationCompleted(createError(NfcErrorType.INVALID_MESSA GE));
398 } catch (TagLostException e) {
399 Log.w(TAG, "Cannot write data to NFC tag. Tag is lost.");
400 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
401 } catch (FormatException | IOException e) {
402 Log.w(TAG, "Cannot write data to NFC tag. IO_ERROR.");
403 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR));
404 }
405 }
406
407 public void onTagDiscovered(Tag tag) {
408 mTagWriter = NfcTagWriter.create(tag);
409 processPendingPushOperation();
410 }
411 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698