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