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