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