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

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: Make InvalidMessageException static. 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
« no previous file with comments | « device/nfc/android/BUILD.gn ('k') | device/nfc/nfc.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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.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;
60 private NfcAdapter mNfcAdapter;
61 private final Context mContext;
62 private Activity mActivity;
63 private boolean mHasPermission;
64 private ReaderCallbackHandler mReaderCallbackHandler;
65 private PendingPushOperation mPendingPushOperation;
66 private NfcTagWriter mTagWriter;
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
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;
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() {}
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
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) {
227 if (mReaderCallbackHandler == null) {
kenneth.r.christiansen 2016/04/18 16:00:30 wouldnt it be nicer with early returns?
shalamov 2016/04/19 12:17:13 Done.
228 mReaderCallbackHandler = new ReaderCallbackHandler(this);
229 mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler,
230 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NF C_B
231 | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG _READER_NFC_V,
232 null);
233 }
234 }
235 }
236
237 private void disableReaderMode() {
238 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
239 mReaderCallbackHandler = null;
240 mNfcAdapter.disableReaderMode(mActivity);
241 }
242 }
243
244 private interface TagTechnologyWriter {
245 public void write(NdefMessage message)
246 throws IOException, TagLostException, FormatException;
247 }
248
249 private static class NdefWriter implements TagTechnologyWriter {
250 private final Ndef mNdef;
251
252 NdefWriter(Ndef ndef) {
253 mNdef = ndef;
254 }
255
256 public void write(NdefMessage message)
257 throws IOException, TagLostException, FormatException {
258 mNdef.writeNdefMessage(message);
259 }
260 }
261
262 private static class NdefFormattableWriter implements TagTechnologyWriter {
263 private final NdefFormatable mNdefFormattable;
264
265 NdefFormattableWriter(NdefFormatable ndefFormattable) {
266 mNdefFormattable = ndefFormattable;
267 }
268
269 public void write(NdefMessage message)
270 throws IOException, TagLostException, FormatException {
271 mNdefFormattable.format(message);
272 }
273 }
274
275 private static class NfcTagWriter {
276 private final TagTechnology mTech;
277 private final TagTechnologyWriter mTechWriter;
278 private boolean mWasConnected = false;
279
280 public static NfcTagWriter create(Tag tag) {
281 if (tag == null) return null;
282
283 Ndef ndef = Ndef.get(tag);
284 if (ndef != null) return new NfcTagWriter(ndef, new NdefWriter(ndef) );
285
286 NdefFormatable formattable = NdefFormatable.get(tag);
287 if (formattable != null) {
288 return new NfcTagWriter(formattable, new NdefFormattableWriter(f ormattable));
289 }
290
291 return null;
292 }
293
294 private NfcTagWriter(TagTechnology tech, TagTechnologyWriter writer) {
295 mTech = tech;
296 mTechWriter = writer;
297 }
298
299 public void connect() throws IOException, TagLostException {
300 if (!mTech.isConnected()) {
301 mTech.connect();
302 mWasConnected = true;
kenneth.r.christiansen 2016/04/18 16:00:30 mDidConnect?
303 }
304 }
305
306 public void close() throws IOException {
307 mTech.close();
308 }
309
310 public void write(NdefMessage message)
311 throws IOException, TagLostException, FormatException {
312 mTechWriter.write(message);
313 }
314
315 public boolean isTagOutOfRange() {
316 try {
317 connect();
318 } catch (IOException e) {
319 return mWasConnected;
320 }
321 return false;
322 }
323 }
324
325 private static class InvalidMessageException extends Exception {}
326
327 private NdefMessage toNdefMessage(NfcMessage message) throws InvalidMessageE xception {
328 if (message == null || message.data.length == 0) throw new InvalidMessag eException();
329
330 try {
331 List<NdefRecord> records = new ArrayList<NdefRecord>();
332 for (NfcRecord record : message.data) {
333 records.add(toNdefRecord(record));
334 }
335 records.add(NdefRecord.createExternal(DOMAIN, TYPE, message.url.getB ytes()));
336 NdefRecord[] ndefRecords = new NdefRecord[records.size()];
337 records.toArray(ndefRecords);
338 return new NdefMessage(ndefRecords);
339 } catch (UnsupportedEncodingException | InvalidMessageException
340 | IllegalArgumentException e) {
341 throw new InvalidMessageException();
342 }
343 }
344
345 private String getCharset(NfcRecord record) {
346 if (record.mediaType.endsWith(CHARSET_UTF8)) return "UTF-8";
347
348 if (record.mediaType.endsWith(CHARSET_UTF16)) return "UTF-16";
349
350 return "UTF-8";
kenneth.r.christiansen 2016/04/18 16:00:30 no warning?
shalamov 2016/04/19 12:17:13 Done.
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
« no previous file with comments | « device/nfc/android/BUILD.gn ('k') | device/nfc/nfc.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698