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