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