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 com.android.nfc.DeviceHost;
21 import com.android.nfc.DeviceHost.TagEndpoint;
22 
23 import android.nfc.FormatException;
24 import android.nfc.NdefMessage;
25 import android.nfc.tech.IsoDep;
26 import android.nfc.tech.MifareClassic;
27 import android.nfc.tech.MifareUltralight;
28 import android.nfc.tech.Ndef;
29 import android.nfc.tech.NfcA;
30 import android.nfc.tech.NfcB;
31 import android.nfc.tech.NfcF;
32 import android.nfc.tech.NfcV;
33 import android.nfc.tech.NfcBarcode;
34 import android.nfc.tech.TagTechnology;
35 import android.os.Bundle;
36 import android.util.Log;
37 
38 /**
39  * Native interface to the NFC tag functions
40  */
41 public 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;
76         private DeviceHost.TagDisconnectedCallback tagDisconnectedCallback;
77 
78         private boolean isPresent = true;
79         private boolean isStopped = false;
80         private boolean isPaused = false;
81         private boolean doCheck = true;
82 
PresenceCheckWatchdog(int presenceCheckDelay, @Nullable DeviceHost.TagDisconnectedCallback callback)83         public PresenceCheckWatchdog(int presenceCheckDelay,
84                                      @Nullable DeviceHost.TagDisconnectedCallback callback) {
85             watchdogTimeout = presenceCheckDelay;
86             tagDisconnectedCallback = callback;
87         }
88 
pause()89         public synchronized void pause() {
90             isPaused = true;
91             doCheck = false;
92             this.notifyAll();
93         }
94 
doResume()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 
end(boolean disableCallback)103         public synchronized void end(boolean disableCallback) {
104             isStopped = true;
105             doCheck = false;
106             if (disableCallback) {
107                 tagDisconnectedCallback = null;
108             }
109             this.notifyAll();
110         }
111 
112         @Override
run()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 
138             synchronized (NativeNfcTag.this) {
139                 mIsPresent = false;
140             }
141             // 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 
doConnect(int handle)152     private native int doConnect(int handle);
connectWithStatus(int technology)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++) {
159             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;
194                     }
195                     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                         */
214 
215 
216                     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
connect(int technology)230     public synchronized boolean connect(int technology) {
231         return connectWithStatus(technology) == 0;
232     }
233 
234     @Override
stopPresenceChecking()235     public synchronized void stopPresenceChecking() {
236         mIsPresent = false;
237         if (mWatchdog != null) {
238             mWatchdog.end(true);
239         }
240     }
241 
242     @Override
startPresenceChecking(int presenceCheckDelay, DeviceHost.TagDisconnectedCallback callback)243     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
isPresent()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     }
doDisconnect()260     native boolean doDisconnect();
261     @Override
disconnect()262     public boolean disconnect() {
263         boolean result = false;
264         PresenceCheckWatchdog watchdog;
265         synchronized (this) {
266             mIsPresent = false;
267             watchdog = mWatchdog;
268         }
269         if (watchdog != null) {
270             // Watchdog has already disconnected or will do it
271             watchdog.end(false);
272             try {
273                 watchdog.join();
274             } catch (InterruptedException e) {
275                 // Should never happen.
276             }
277             synchronized (this) {
278                 mWatchdog = null;
279             }
280             result = true;
281         } else {
282             result = doDisconnect();
283         }
284 
285         mConnectedTechIndex = -1;
286         mConnectedHandle = -1;
287         return result;
288     }
289 
doReconnect()290     native int doReconnect();
reconnectWithStatus()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
reconnect()302     public synchronized boolean reconnect() {
303         return reconnectWithStatus() == 0;
304     }
305 
doHandleReconnect(int handle)306     native int doHandleReconnect(int handle);
reconnectWithStatus(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 
doTransceive(byte[] data, boolean raw, int[] returnCode)318     private native byte[] doTransceive(byte[] data, boolean raw, int[] returnCode);
319     @Override
transceive(byte[] data, boolean raw, int[] returnCode)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 
doCheckNdef(int[] ndefinfo)331     private native int doCheckNdef(int[] ndefinfo);
checkNdefWithStatus(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
checkNdef(int[] ndefinfo)343     public synchronized boolean checkNdef(int[] ndefinfo) {
344         return checkNdefWithStatus(ndefinfo) == 0;
345     }
346 
doRead()347     private native byte[] doRead();
348     @Override
readNdef()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 
doWrite(byte[] buf)360     private native boolean doWrite(byte[] buf);
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     @Override
presenceCheck()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 
doNdefFormat(byte[] key)386     native boolean doNdefFormat(byte[] key);
387     @Override
formatNdef(byte[] key)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 
doMakeReadonly(byte[] key)399     native boolean doMakeReadonly(byte[] key);
400     @Override
makeReadOnly()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 
doIsIsoDepNdefFormatable(byte[] poll, byte[] act)418     native boolean doIsIsoDepNdefFormatable(byte[] poll, byte[] act);
419     @Override
isNdefFormatable()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
getHandle()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
getUid()440     public byte[] getUid() {
441         return mUid;
442     }
443 
444     @Override
getTechList()445     public int[] getTechList() {
446         return mTechList;
447     }
448 
getConnectedHandle()449     private int getConnectedHandle() {
450         return mConnectedHandle;
451     }
452 
getConnectedLibNfcType()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
getConnectedTechnology()462     public int getConnectedTechnology() {
463         if (mConnectedTechIndex != -1 && mConnectedTechIndex < mTechList.length) {
464             return mTechList[mConnectedTechIndex];
465         } else {
466             return 0;
467         }
468     }
doGetNdefType(int libnfctype, int javatype)469     native int doGetNdefType(int libnfctype, int javatype);
getNdefType(int libnfctype, int javatype)470     private int getNdefType(int libnfctype, int javatype) {
471         return doGetNdefType(libnfctype, javatype);
472     }
473 
addTechnology(int tech, int handle, int libnfctype)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
removeTechnology(int tech)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 
addNdefFormatableTechnology(int handle, int libnfcType)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.
addNdefTechnology(NdefMessage msg, int handle, int libnfcType, int javaType, int maxLength, int cardState)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 
getTechIndex(int tech)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 
hasTech(int tech)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 
hasTechOnHandle(int tech, int handle)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 
isUltralightC()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
getTechExtras()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.NFC_BARCODE: {
735                         // hard code this for now, this is the only valid type
736                         extras.putInt(NfcBarcode.EXTRA_BARCODE_TYPE, NfcBarcode.TYPE_KOVIO);
737                         break;
738                     }
739 
740                     default: {
741                         // Leave the entry in the array null
742                         continue;
743                     }
744                 }
745                 mTechExtras[i] = extras;
746             }
747             return mTechExtras;
748         }
749     }
750 
751     @Override
findAndReadNdef()752     public NdefMessage findAndReadNdef() {
753         // Try to find NDEF on any of the technologies.
754         int[] technologies = getTechList();
755         int[] handles = mTechHandles;
756         NdefMessage ndefMsg = null;
757         boolean foundFormattable = false;
758         int formattableHandle = 0;
759         int formattableLibNfcType = 0;
760         int status;
761 
762         for (int techIndex = 0; techIndex < technologies.length; techIndex++) {
763             // have we seen this handle before?
764             for (int i = 0; i < techIndex; i++) {
765                 if (handles[i] == handles[techIndex]) {
766                     continue;  // don't check duplicate handles
767                 }
768             }
769 
770             status = connectWithStatus(technologies[techIndex]);
771             if (status != 0) {
772                 Log.d(TAG, "Connect Failed - status = "+ status);
773                 if (status == STATUS_CODE_TARGET_LOST) {
774                     break;
775                 }
776                 continue;  // try next handle
777             }
778             // Check if this type is NDEF formatable
779             if (!foundFormattable) {
780                 if (isNdefFormatable()) {
781                     foundFormattable = true;
782                     formattableHandle = getConnectedHandle();
783                     formattableLibNfcType = getConnectedLibNfcType();
784                     // We'll only add formattable tech if no ndef is
785                     // found - this is because libNFC refuses to format
786                     // an already NDEF formatted tag.
787                 }
788                 reconnect();
789             }
790 
791             int[] ndefinfo = new int[2];
792             status = checkNdefWithStatus(ndefinfo);
793             if (status != 0) {
794                 Log.d(TAG, "Check NDEF Failed - status = " + status);
795                 if (status == STATUS_CODE_TARGET_LOST) {
796                     break;
797                 }
798                 continue;  // try next handle
799             }
800 
801             // found our NDEF handle
802             boolean generateEmptyNdef = false;
803 
804             int supportedNdefLength = ndefinfo[0];
805             int cardState = ndefinfo[1];
806             byte[] buff = readNdef();
807             if (buff != null && buff.length > 0) {
808                 try {
809                     ndefMsg = new NdefMessage(buff);
810                     addNdefTechnology(ndefMsg,
811                             getConnectedHandle(),
812                             getConnectedLibNfcType(),
813                             getConnectedTechnology(),
814                             supportedNdefLength, cardState);
815                     reconnect();
816                 } catch (FormatException e) {
817                    // Create an intent anyway, without NDEF messages
818                    generateEmptyNdef = true;
819                 }
820             } else if(buff != null){
821                 // Empty buffer, unformatted tags fall into this case
822                 generateEmptyNdef = true;
823             }
824 
825             if (generateEmptyNdef) {
826                 ndefMsg = null;
827                 addNdefTechnology(null,
828                       getConnectedHandle(),
829                       getConnectedLibNfcType(),
830                       getConnectedTechnology(),
831                       supportedNdefLength, cardState);
832                 foundFormattable = false;
833                 reconnect();
834             }
835             break;
836         }
837 
838         if (ndefMsg == null && foundFormattable) {
839             // Tag is not NDEF yet, and found a formattable target,
840             // so add formattable tech to tech list.
841             addNdefFormatableTechnology(
842                     formattableHandle,
843                     formattableLibNfcType);
844         }
845 
846         return ndefMsg;
847     }
848 }
849