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