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