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