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