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