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