blob: 4fd0afb0e959a295b76b1c884402b30b6b6813d0 [file] [log] [blame]
nxpandroid64fd68c2015-09-23 16:45:15 +05301/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
nxf500513a018e72019-04-23 17:11:41 +053016
nxpandroid64fd68c2015-09-23 16:45:15 +053017package com.android.nfc.dhimpl;
18
19import android.annotation.Nullable;
20import com.android.nfc.DeviceHost;
21import com.android.nfc.DeviceHost.TagEndpoint;
22
23import android.nfc.FormatException;
24import android.nfc.NdefMessage;
25import android.nfc.tech.IsoDep;
26import android.nfc.tech.MifareClassic;
27import android.nfc.tech.MifareUltralight;
28import android.nfc.tech.Ndef;
29import android.nfc.tech.NfcA;
30import android.nfc.tech.NfcB;
31import android.nfc.tech.NfcF;
32import android.nfc.tech.NfcV;
33import android.nfc.tech.NfcBarcode;
34import android.nfc.tech.TagTechnology;
35import android.os.Bundle;
36import android.util.Log;
37
38/**
39 * Native interface to the NFC tag functions
40 */
41public class NativeNfcTag implements TagEndpoint {
42 static final boolean DBG = true;
43
44 static final int STATUS_CODE_TARGET_LOST = 146;
45
46 private int[] mTechList;
47 private int[] mTechHandles;
48 private int[] mTechLibNfcTypes;
49 private Bundle[] mTechExtras;
50 private byte[][] mTechPollBytes;
51 private byte[][] mTechActBytes;
52 private byte[] mUid;
53
54 // mConnectedHandle stores the *real* libnfc handle
55 // that we're connected to.
56 private int mConnectedHandle;
57
58 // mConnectedTechIndex stores to which technology
59 // the upper layer stack is connected. Note that
60 // we may be connected to a libnfchandle without being
61 // connected to a technology - technology changes
62 // may occur runtime, whereas the underlying handle
63 // could stay present. Usually all technologies are on the
64 // same handle, with the exception of multi-protocol
65 // tags.
66 private int mConnectedTechIndex; // Index in mTechHandles
67
68 private final String TAG = "NativeNfcTag";
69
70 private boolean mIsPresent; // Whether the tag is known to be still present
71
72 private PresenceCheckWatchdog mWatchdog;
73 class PresenceCheckWatchdog extends Thread {
74
75 private final int watchdogTimeout;
Suhas Suresh5efc5432018-04-27 15:31:02 +053076 private DeviceHost.TagDisconnectedCallback tagDisconnectedCallback;
nxpandroid64fd68c2015-09-23 16:45:15 +053077
78 private boolean isPresent = true;
79 private boolean isStopped = false;
80 private boolean isPaused = false;
81 private boolean doCheck = true;
82
83 public PresenceCheckWatchdog(int presenceCheckDelay,
84 @Nullable DeviceHost.TagDisconnectedCallback callback) {
85 watchdogTimeout = presenceCheckDelay;
86 tagDisconnectedCallback = callback;
87 }
88
89 public synchronized void pause() {
90 isPaused = true;
91 doCheck = false;
92 this.notifyAll();
93 }
94
95 public synchronized void doResume() {
96 isPaused = false;
97 // We don't want to resume presence checking immediately,
98 // but go through at least one more wait period.
99 doCheck = false;
100 this.notifyAll();
101 }
102
Suhas Suresh5efc5432018-04-27 15:31:02 +0530103 public synchronized void end(boolean disableCallback) {
nxpandroid64fd68c2015-09-23 16:45:15 +0530104 isStopped = true;
105 doCheck = false;
Suhas Suresh5efc5432018-04-27 15:31:02 +0530106 if (disableCallback) {
107 tagDisconnectedCallback = null;
108 }
nxpandroid64fd68c2015-09-23 16:45:15 +0530109 this.notifyAll();
110 }
111
112 @Override
113 public void run() {
114 synchronized (this) {
115 if (DBG) Log.d(TAG, "Starting background presence check");
116 while (isPresent && !isStopped) {
117 try {
118 if (!isPaused) {
119 doCheck = true;
120 }
121 this.wait(watchdogTimeout);
122 if (doCheck) {
123 isPresent = doPresenceCheck();
124 } else {
125 // 1) We are paused, waiting for unpause
126 // 2) We just unpaused, do pres check in next iteration
127 // (after watchdogTimeout ms sleep)
128 // 3) We just set the timeout, wait for this timeout
129 // to expire once first.
130 // 4) We just stopped, exit loop anyway
131 }
132 } catch (InterruptedException e) {
133 // Activity detected, loop
134 }
135 }
136 }
137
nxf500513a018e72019-04-23 17:11:41 +0530138 synchronized (NativeNfcTag.this) {
nxpandroid64fd68c2015-09-23 16:45:15 +0530139 mIsPresent = false;
nxf500513a018e72019-04-23 17:11:41 +0530140 }
nxpandroid64fd68c2015-09-23 16:45:15 +0530141 // Restart the polling loop
142
143 Log.d(TAG, "Tag lost, restarting polling loop");
144 doDisconnect();
145 if (tagDisconnectedCallback != null) {
146 tagDisconnectedCallback.onTagDisconnected(mConnectedHandle);
147 }
148 if (DBG) Log.d(TAG, "Stopping background presence check");
149 }
150 }
151
152 private native int doConnect(int handle);
153 public synchronized int connectWithStatus(int technology) {
154 if (mWatchdog != null) {
155 mWatchdog.pause();
156 }
157 int status = -1;
158 for (int i = 0; i < mTechList.length; i++) {
nxpandroid64fd68c2015-09-23 16:45:15 +0530159 if (mTechList[i] == technology) {
160 // Get the handle and connect, if not already connected
161 if (mConnectedHandle != mTechHandles[i]) {
162 // We're not yet connected to this handle, there are
163 // a few scenario's here:
164 // 1) We are not connected to anything yet - allow
165 // 2) We are connected to a technology which has
166 // a different handle (multi-protocol tag); we support
167 // switching to that.
168 if (mConnectedHandle == -1) {
169 // Not connected yet
170 //status = doConnect(mTechHandles[i]);
171 status = doConnect(i);
172 } else {
173 // Connect to a tech with a different handle
174 Log.d(TAG,"Connect to a tech with a different handle");
175 //status = reconnectWithStatus(mTechHandles[i]);
176 status = reconnectWithStatus(i);
177 }
178 if (status == 0) {
179 mConnectedHandle = mTechHandles[i];
180 mConnectedTechIndex = i;
181 }
182 } else {
183 // 1) We are connected to a technology which has the same
184 // handle; we do not support connecting at a different
185 // level (libnfc auto-activates to the max level on
186 // any handle).
187 // 2) We are connecting to the ndef technology - always
188 // allowed.
189 if ((technology == TagTechnology.NDEF) ||
190 (technology == TagTechnology.NDEF_FORMATABLE)) {
191 // special case for NDEF, this will cause switch to ISO_DEP frame intf
192 i = 0;
193 // status = 0;
nxf35421199851a2019-05-28 15:16:07 +0530194 }
nxpandroid64fd68c2015-09-23 16:45:15 +0530195 status = reconnectWithStatus(i);
196 /*
197 if ((technology != TagTechnology.ISO_DEP) &&
198 (hasTechOnHandle(TagTechnology.ISO_DEP, mTechHandles[i]))) {
199 // Don't allow to connect a -4 tag at a different level
200 // than IsoDep, as this is not supported by
201 // libNFC.
202 // revised for NFCA... do allow to connect a -4 tag at this level.
203 Log.d(TAG,"Connect to a tech with same different handle (rf intf change)");
204 status = reconnectWithStatus(i);
205 if (status == 0) {
206 mConnectedHandle = mTechHandles[i];
207 mConnectedTechIndex = i;
208 }
209 //status = 0;
210 } else {
211 status = 0;
212 }
213 */
nxf35421199851a2019-05-28 15:16:07 +0530214
215
nxpandroid64fd68c2015-09-23 16:45:15 +0530216 if (status == 0) {
217 mConnectedTechIndex = i;
218 // Handle was already identical
219 }
220 }
221 break;
222 }
223 }
224 if (mWatchdog != null) {
225 mWatchdog.doResume();
226 }
227 return status;
228 }
229 @Override
230 public synchronized boolean connect(int technology) {
231 return connectWithStatus(technology) == 0;
232 }
233
234 @Override
Suhas Suresh5efc5432018-04-27 15:31:02 +0530235 public synchronized void stopPresenceChecking() {
236 mIsPresent = false;
237 if (mWatchdog != null) {
238 mWatchdog.end(true);
239 }
240 }
241
242 @Override
nxpandroid64fd68c2015-09-23 16:45:15 +0530243 public synchronized void startPresenceChecking(int presenceCheckDelay,
244 DeviceHost.TagDisconnectedCallback callback) {
245 // Once we start presence checking, we allow the upper layers
246 // to know the tag is in the field.
247 mIsPresent = true;
248 if (mWatchdog == null) {
249 mWatchdog = new PresenceCheckWatchdog(presenceCheckDelay, callback);
250 mWatchdog.start();
251 }
252 }
253
254 @Override
255 public synchronized boolean isPresent() {
256 // Returns whether the tag is still in the field to the best
257 // of our knowledge.
258 return mIsPresent;
259 }
260 native boolean doDisconnect();
261 @Override
nxpandroid281eb922016-08-25 20:27:46 +0530262 public boolean disconnect() {
nxpandroid64fd68c2015-09-23 16:45:15 +0530263 boolean result = false;
nxpandroid281eb922016-08-25 20:27:46 +0530264 PresenceCheckWatchdog watchdog;
265 synchronized (this) {
266 mIsPresent = false;
267 watchdog = mWatchdog;
268 }
269 if (watchdog != null) {
nxpandroid64fd68c2015-09-23 16:45:15 +0530270 // Watchdog has already disconnected or will do it
Suhas Suresh5efc5432018-04-27 15:31:02 +0530271 watchdog.end(false);
nxpandroid64fd68c2015-09-23 16:45:15 +0530272 try {
nxpandroid281eb922016-08-25 20:27:46 +0530273 watchdog.join();
nxpandroid64fd68c2015-09-23 16:45:15 +0530274 } catch (InterruptedException e) {
275 // Should never happen.
276 }
nxpandroid281eb922016-08-25 20:27:46 +0530277 synchronized (this) {
278 mWatchdog = null;
279 }
nxpandroid64fd68c2015-09-23 16:45:15 +0530280 result = true;
281 } else {
282 result = doDisconnect();
283 }
284
285 mConnectedTechIndex = -1;
286 mConnectedHandle = -1;
287 return result;
288 }
289
290 native int doReconnect();
291 public synchronized int reconnectWithStatus() {
292 if (mWatchdog != null) {
293 mWatchdog.pause();
294 }
295 int status = doReconnect();
296 if (mWatchdog != null) {
297 mWatchdog.doResume();
298 }
299 return status;
300 }
301 @Override
302 public synchronized boolean reconnect() {
303 return reconnectWithStatus() == 0;
304 }
305
306 native int doHandleReconnect(int handle);
307 public synchronized int reconnectWithStatus(int handle) {
308 if (mWatchdog != null) {
309 mWatchdog.pause();
310 }
311 int status = doHandleReconnect(handle);
312 if (mWatchdog != null) {
313 mWatchdog.doResume();
314 }
315 return status;
316 }
317
318 private native byte[] doTransceive(byte[] data, boolean raw, int[] returnCode);
319 @Override
320 public synchronized byte[] transceive(byte[] data, boolean raw, int[] returnCode) {
321 if (mWatchdog != null) {
322 mWatchdog.pause();
323 }
324 byte[] result = doTransceive(data, raw, returnCode);
325 if (mWatchdog != null) {
326 mWatchdog.doResume();
327 }
328 return result;
329 }
330
331 private native int doCheckNdef(int[] ndefinfo);
332 private synchronized int checkNdefWithStatus(int[] ndefinfo) {
333 if (mWatchdog != null) {
334 mWatchdog.pause();
335 }
336 int status = doCheckNdef(ndefinfo);
337 if (mWatchdog != null) {
338 mWatchdog.doResume();
339 }
340 return status;
341 }
342 @Override
343 public synchronized boolean checkNdef(int[] ndefinfo) {
344 return checkNdefWithStatus(ndefinfo) == 0;
345 }
346
347 private native byte[] doRead();
348 @Override
349 public synchronized byte[] readNdef() {
350 if (mWatchdog != null) {
351 mWatchdog.pause();
352 }
353 byte[] result = doRead();
354 if (mWatchdog != null) {
355 mWatchdog.doResume();
356 }
357 return result;
358 }
359
360 private native boolean doWrite(byte[] buf);
361 @Override
362 public synchronized boolean writeNdef(byte[] buf) {
363 if (mWatchdog != null) {
364 mWatchdog.pause();
365 }
366 boolean result = doWrite(buf);
367 if (mWatchdog != null) {
368 mWatchdog.doResume();
369 }
370 return result;
371 }
372
373 native boolean doPresenceCheck();
374 @Override
375 public synchronized boolean presenceCheck() {
376 if (mWatchdog != null) {
377 mWatchdog.pause();
378 }
379 boolean result = doPresenceCheck();
380 if (mWatchdog != null) {
381 mWatchdog.doResume();
382 }
383 return result;
384 }
385
386 native boolean doNdefFormat(byte[] key);
387 @Override
388 public synchronized boolean formatNdef(byte[] key) {
389 if (mWatchdog != null) {
390 mWatchdog.pause();
391 }
392 boolean result = doNdefFormat(key);
393 if (mWatchdog != null) {
394 mWatchdog.doResume();
395 }
396 return result;
397 }
398
399 native boolean doMakeReadonly(byte[] key);
400 @Override
401 public synchronized boolean makeReadOnly() {
402 if (mWatchdog != null) {
403 mWatchdog.pause();
404 }
405 boolean result;
406 if (hasTech(TagTechnology.MIFARE_CLASSIC)) {
407 result = doMakeReadonly(MifareClassic.KEY_DEFAULT);
408 } else {
409 // No key needed for other technologies
410 result = doMakeReadonly(new byte[] {});
411 }
412 if (mWatchdog != null) {
413 mWatchdog.doResume();
414 }
415 return result;
416 }
417
418 native boolean doIsIsoDepNdefFormatable(byte[] poll, byte[] act);
419 @Override
420 public synchronized boolean isNdefFormatable() {
421 // Let native code decide whether the currently activated tag
422 // is formatable. Although the name of the JNI function refers
423 // to ISO-DEP, the JNI function checks all tag types.
424 return doIsIsoDepNdefFormatable(mTechPollBytes[0],
425 mTechActBytes[0]);
426 }
427
428 @Override
429 public int getHandle() {
430 // This is just a handle for the clients; it can simply use the first
431 // technology handle we have.
432 if (mTechHandles.length > 0) {
433 return mTechHandles[0];
434 } else {
435 return 0;
436 }
437 }
438
439 @Override
440 public byte[] getUid() {
441 return mUid;
442 }
443
444 @Override
445 public int[] getTechList() {
446 return mTechList;
447 }
448
449 private int getConnectedHandle() {
450 return mConnectedHandle;
451 }
452
453 private int getConnectedLibNfcType() {
454 if (mConnectedTechIndex != -1 && mConnectedTechIndex < mTechLibNfcTypes.length) {
455 return mTechLibNfcTypes[mConnectedTechIndex];
456 } else {
457 return 0;
458 }
459 }
460
461 @Override
462 public int getConnectedTechnology() {
463 if (mConnectedTechIndex != -1 && mConnectedTechIndex < mTechList.length) {
464 return mTechList[mConnectedTechIndex];
465 } else {
466 return 0;
467 }
468 }
469 native int doGetNdefType(int libnfctype, int javatype);
470 private int getNdefType(int libnfctype, int javatype) {
471 return doGetNdefType(libnfctype, javatype);
472 }
473
474 private void addTechnology(int tech, int handle, int libnfctype) {
475 int[] mNewTechList = new int[mTechList.length + 1];
476 System.arraycopy(mTechList, 0, mNewTechList, 0, mTechList.length);
477 mNewTechList[mTechList.length] = tech;
478 mTechList = mNewTechList;
479
480 int[] mNewHandleList = new int[mTechHandles.length + 1];
481 System.arraycopy(mTechHandles, 0, mNewHandleList, 0, mTechHandles.length);
482 mNewHandleList[mTechHandles.length] = handle;
483 mTechHandles = mNewHandleList;
484
485 int[] mNewTypeList = new int[mTechLibNfcTypes.length + 1];
486 System.arraycopy(mTechLibNfcTypes, 0, mNewTypeList, 0, mTechLibNfcTypes.length);
487 mNewTypeList[mTechLibNfcTypes.length] = libnfctype;
488 mTechLibNfcTypes = mNewTypeList;
489 }
490
491 @Override
492 public void removeTechnology(int tech) {
493 synchronized (this) {
494 int techIndex = getTechIndex(tech);
495 if (techIndex != -1) {
496 int[] mNewTechList = new int[mTechList.length - 1];
497 System.arraycopy(mTechList, 0, mNewTechList, 0, techIndex);
498 System.arraycopy(mTechList, techIndex + 1, mNewTechList, techIndex,
499 mTechList.length - techIndex - 1);
500 mTechList = mNewTechList;
501
502 int[] mNewHandleList = new int[mTechHandles.length - 1];
503 System.arraycopy(mTechHandles, 0, mNewHandleList, 0, techIndex);
504 System.arraycopy(mTechHandles, techIndex + 1, mNewTechList, techIndex,
505 mTechHandles.length - techIndex - 1);
506 mTechHandles = mNewHandleList;
507
508 int[] mNewTypeList = new int[mTechLibNfcTypes.length - 1];
509 System.arraycopy(mTechLibNfcTypes, 0, mNewTypeList, 0, techIndex);
510 System.arraycopy(mTechLibNfcTypes, techIndex + 1, mNewTypeList, techIndex,
511 mTechLibNfcTypes.length - techIndex - 1);
512 mTechLibNfcTypes = mNewTypeList;
513
514 //The technology must be removed from the mTechExtras array,
515 //just like the above arrays.
516 //Remove the specified element from the array,
517 //then shift the remaining elements by one.
518 if (mTechExtras != null)
519 {
520 Bundle[] mNewTechExtras = new Bundle[mTechExtras.length - 1];
521 System.arraycopy(mTechExtras, 0, mNewTechExtras, 0, techIndex);
522 System.arraycopy(mTechExtras, techIndex + 1, mNewTechExtras, techIndex,
523 mTechExtras.length - techIndex - 1);
524 mTechExtras = mNewTechExtras;
525 }
526 }
527 }
528 }
529
530 public void addNdefFormatableTechnology(int handle, int libnfcType) {
531 synchronized (this) {
532 addTechnology(TagTechnology.NDEF_FORMATABLE, handle, libnfcType);
533 }
534 }
535
536 // This method exists to "patch in" the ndef technologies,
537 // which is done inside Java instead of the native JNI code.
538 // To not create some nasty dependencies on the order on which things
539 // are called (most notably getTechExtras()), it needs some additional
540 // checking.
541 public void addNdefTechnology(NdefMessage msg, int handle, int libnfcType,
542 int javaType, int maxLength, int cardState) {
543 synchronized (this) {
544 addTechnology(TagTechnology.NDEF, handle, libnfcType);
545
546 Bundle extras = new Bundle();
547 extras.putParcelable(Ndef.EXTRA_NDEF_MSG, msg);
548 extras.putInt(Ndef.EXTRA_NDEF_MAXLENGTH, maxLength);
549 extras.putInt(Ndef.EXTRA_NDEF_CARDSTATE, cardState);
550 extras.putInt(Ndef.EXTRA_NDEF_TYPE, getNdefType(libnfcType, javaType));
551
552 if (mTechExtras == null) {
553 // This will build the tech extra's for the first time,
554 // including a NULL ref for the NDEF tech we generated above.
555 Bundle[] builtTechExtras = getTechExtras();
556 builtTechExtras[builtTechExtras.length - 1] = extras;
557 }
558 else {
559 // Tech extras were built before, patch the NDEF one in
560 Bundle[] oldTechExtras = getTechExtras();
561 Bundle[] newTechExtras = new Bundle[oldTechExtras.length + 1];
562 System.arraycopy(oldTechExtras, 0, newTechExtras, 0, oldTechExtras.length);
563 newTechExtras[oldTechExtras.length] = extras;
564 mTechExtras = newTechExtras;
565 }
566
567
568 }
569 }
570
571 private int getTechIndex(int tech) {
572 int techIndex = -1;
573 for (int i = 0; i < mTechList.length; i++) {
574 if (mTechList[i] == tech) {
575 techIndex = i;
576 break;
577 }
578 }
579 return techIndex;
580 }
581
582 private boolean hasTech(int tech) {
583 boolean hasTech = false;
584 for (int i = 0; i < mTechList.length; i++) {
585 if (mTechList[i] == tech) {
586 hasTech = true;
587 break;
588 }
589 }
590 return hasTech;
591 }
592
593 private boolean hasTechOnHandle(int tech, int handle) {
594 boolean hasTech = false;
595 for (int i = 0; i < mTechList.length; i++) {
596 if (mTechList[i] == tech && mTechHandles[i] == handle) {
597 hasTech = true;
598 break;
599 }
600 }
601 return hasTech;
602
603 }
604
605 private boolean isUltralightC() {
606 /* Make a best-effort attempt at classifying ULTRALIGHT
607 * vs ULTRALIGHT-C (based on NXP's public AN1303).
608 * The memory layout is as follows:
609 * Page # BYTE1 BYTE2 BYTE3 BYTE4
610 * 2 INT1 INT2 LOCK LOCK
611 * 3 OTP OTP OTP OTP (NDEF CC if NDEF-formatted)
612 * 4 DATA DATA DATA DATA (version info if factory-state)
613 *
614 * Read four blocks from page 2, which will get us both
615 * the lock page, the OTP page and the version info.
616 */
617 boolean isUltralightC = false;
618 byte[] readCmd = { 0x30, 0x02 };
619 int[] retCode = new int[2];
620 byte[] respData = transceive(readCmd, false, retCode);
621 if (respData != null && respData.length == 16) {
622 // Check the lock bits (last 2 bytes in page2)
623 // and the OTP bytes (entire page 3)
624 if (respData[2] == 0 && respData[3] == 0 && respData[4] == 0 &&
625 respData[5] == 0 && respData[6] == 0 && respData[7] == 0) {
626 // Very likely to be a blank card, look at version info
627 // in page 4.
628 if ((respData[8] == (byte)0x02) && respData[9] == (byte)0x00) {
629 // This is Ultralight-C
630 isUltralightC = true;
631 } else {
632 // 0xFF 0xFF would indicate Ultralight, but we also use Ultralight
633 // as a fallback if it's anything else
634 isUltralightC = false;
635 }
636 } else {
637 // See if we can find the NDEF CC in the OTP page and if it's
638 // smaller than major version two
639 if (respData[4] == (byte)0xE1 && ((respData[5] & 0xff) < 0x20)) {
640 // OK, got NDEF. Technically we'd have to search for the
641 // NDEF TLV as well. However, this would add too much
642 // time for discovery and we can make already make a good guess
643 // with the data we have here. Byte 2 of the OTP page
644 // indicates the size of the tag - 0x06 is UL, anything
645 // above indicates UL-C.
646 if ((respData[6] & 0xff) > 0x06) {
647 isUltralightC = true;
648 }
649 } else {
650 // Fall back to ultralight
651 isUltralightC = false;
652 }
653 }
654 }
655 return isUltralightC;
656 }
657
658 @Override
659 public Bundle[] getTechExtras() {
660 synchronized (this) {
661 if (mTechExtras != null) return mTechExtras;
662 mTechExtras = new Bundle[mTechList.length];
663 for (int i = 0; i < mTechList.length; i++) {
664 Bundle extras = new Bundle();
665 switch (mTechList[i]) {
666 case TagTechnology.NFC_A: {
667 byte[] actBytes = mTechActBytes[i];
668 if ((actBytes != null) && (actBytes.length > 0)) {
669 extras.putShort(NfcA.EXTRA_SAK, (short) (actBytes[0] & (short) 0xFF));
670 } else {
671 // Unfortunately Jewel doesn't have act bytes,
672 // ignore this case.
673 }
674 extras.putByteArray(NfcA.EXTRA_ATQA, mTechPollBytes[i]);
675 break;
676 }
677
678 case TagTechnology.NFC_B: {
679 // What's returned from the PN544 is actually:
680 // 4 bytes app data
681 // 3 bytes prot info
682 byte[] appData = new byte[4];
683 byte[] protInfo = new byte[3];
684 if (mTechPollBytes[i].length >= 7) {
685 System.arraycopy(mTechPollBytes[i], 0, appData, 0, 4);
686 System.arraycopy(mTechPollBytes[i], 4, protInfo, 0, 3);
687
688 extras.putByteArray(NfcB.EXTRA_APPDATA, appData);
689 extras.putByteArray(NfcB.EXTRA_PROTINFO, protInfo);
690 }
691 break;
692 }
693
694 case TagTechnology.NFC_F: {
695 byte[] pmm = new byte[8];
696 byte[] sc = new byte[2];
697 if (mTechPollBytes[i].length >= 8) {
698 // At least pmm is present
699 System.arraycopy(mTechPollBytes[i], 0, pmm, 0, 8);
700 extras.putByteArray(NfcF.EXTRA_PMM, pmm);
701 }
702 if (mTechPollBytes[i].length == 10) {
703 System.arraycopy(mTechPollBytes[i], 8, sc, 0, 2);
704 extras.putByteArray(NfcF.EXTRA_SC, sc);
705 }
706 break;
707 }
708
709 case TagTechnology.ISO_DEP: {
710 if (hasTech(TagTechnology.NFC_A)) {
711 extras.putByteArray(IsoDep.EXTRA_HIST_BYTES, mTechActBytes[i]);
712 }
713 else {
714 extras.putByteArray(IsoDep.EXTRA_HI_LAYER_RESP, mTechActBytes[i]);
715 }
716 break;
717 }
718
719 case TagTechnology.NFC_V: {
720 // First byte response flags, second byte DSFID
721 if (mTechPollBytes[i] != null && mTechPollBytes[i].length >= 2) {
722 extras.putByte(NfcV.EXTRA_RESP_FLAGS, mTechPollBytes[i][0]);
723 extras.putByte(NfcV.EXTRA_DSFID, mTechPollBytes[i][1]);
724 }
725 break;
726 }
727
728 case TagTechnology.MIFARE_ULTRALIGHT: {
729 boolean isUlc = isUltralightC();
730 extras.putBoolean(MifareUltralight.EXTRA_IS_UL_C, isUlc);
731 break;
732 }
733
734 case TagTechnology.MIFARE_CLASSIC: {
735 byte[] actBytes = mTechActBytes[i];
736 if ((actBytes != null) && (actBytes.length > 0)) {
737 extras.putShort(NfcA.EXTRA_SAK, (short) (actBytes[0] & (short) 0xFF));
738 } else {
739 // ignore this case.
740 }
741 extras.putByteArray(NfcA.EXTRA_ATQA, mTechPollBytes[i]);
742 break;
743 }
744
745 case TagTechnology.NFC_BARCODE: {
746 // hard code this for now, this is the only valid type
747 extras.putInt(NfcBarcode.EXTRA_BARCODE_TYPE, NfcBarcode.TYPE_KOVIO);
748 break;
749 }
750
751 default: {
752 // Leave the entry in the array null
753 continue;
754 }
755 }
756 mTechExtras[i] = extras;
757 }
758 return mTechExtras;
759 }
760 }
761
762 @Override
763 public NdefMessage findAndReadNdef() {
764 // Try to find NDEF on any of the technologies.
765 int[] technologies = getTechList();
766 int[] handles = mTechHandles;
767 NdefMessage ndefMsg = null;
768 boolean foundFormattable = false;
769 int formattableHandle = 0;
770 int formattableLibNfcType = 0;
771 int status;
772
773 for (int techIndex = 0; techIndex < technologies.length; techIndex++) {
774 // have we seen this handle before?
775 for (int i = 0; i < techIndex; i++) {
776 if (handles[i] == handles[techIndex]) {
777 continue; // don't check duplicate handles
778 }
779 }
780
781 status = connectWithStatus(technologies[techIndex]);
782 if (status != 0) {
783 Log.d(TAG, "Connect Failed - status = "+ status);
784 if (status == STATUS_CODE_TARGET_LOST) {
785 break;
786 }
787 continue; // try next handle
788 }
789 // Check if this type is NDEF formatable
790 if (!foundFormattable) {
791 if (isNdefFormatable()) {
792 foundFormattable = true;
793 formattableHandle = getConnectedHandle();
794 formattableLibNfcType = getConnectedLibNfcType();
795 // We'll only add formattable tech if no ndef is
796 // found - this is because libNFC refuses to format
797 // an already NDEF formatted tag.
798 }
799 reconnect();
800 }
801
802 int[] ndefinfo = new int[2];
803 status = checkNdefWithStatus(ndefinfo);
804 if (status != 0) {
805 Log.d(TAG, "Check NDEF Failed - status = " + status);
806 if (status == STATUS_CODE_TARGET_LOST) {
807 break;
808 }
809 continue; // try next handle
810 }
811
812 // found our NDEF handle
813 boolean generateEmptyNdef = false;
814
815 int supportedNdefLength = ndefinfo[0];
816 int cardState = ndefinfo[1];
817 byte[] buff = readNdef();
nxpandroid6fd9cdb2017-07-12 18:25:41 +0530818 if (buff != null && buff.length > 0) {
nxpandroid64fd68c2015-09-23 16:45:15 +0530819 try {
820 ndefMsg = new NdefMessage(buff);
821 addNdefTechnology(ndefMsg,
822 getConnectedHandle(),
823 getConnectedLibNfcType(),
824 getConnectedTechnology(),
825 supportedNdefLength, cardState);
nxpandroid64fd68c2015-09-23 16:45:15 +0530826 reconnect();
827 } catch (FormatException e) {
828 // Create an intent anyway, without NDEF messages
829 generateEmptyNdef = true;
830 }
nxpandroid6fd9cdb2017-07-12 18:25:41 +0530831 } else if(buff != null){
832 // Empty buffer, unformatted tags fall into this case
nxpandroid64fd68c2015-09-23 16:45:15 +0530833 generateEmptyNdef = true;
834 }
835
836 if (generateEmptyNdef) {
837 ndefMsg = null;
838 addNdefTechnology(null,
nxpandroid6fd9cdb2017-07-12 18:25:41 +0530839 getConnectedHandle(),
840 getConnectedLibNfcType(),
841 getConnectedTechnology(),
842 supportedNdefLength, cardState);
nxpandroid5d64ce92016-11-18 19:48:53 +0530843 foundFormattable = false;
nxpandroid64fd68c2015-09-23 16:45:15 +0530844 reconnect();
845 }
846 break;
847 }
848
849 if (ndefMsg == null && foundFormattable) {
850 // Tag is not NDEF yet, and found a formattable target,
851 // so add formattable tech to tech list.
852 addNdefFormatableTechnology(
853 formattableHandle,
854 formattableLibNfcType);
855 }
856
857 return ndefMsg;
858 }
859}