NativeNfcTag.java revision 1639c10dcfa150cf1c2bac171b8b710f103af766
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
17package com.android.nfc.dhimpl;
18
19import android.annotation.Nullable;
20import com.android.nfc.DeviceHost;
21import com.android.nfc.DeviceHost.TagEndpoint;
22
23import android.nfc.FormatException;
24import android.nfc.NdefMessage;
25import android.nfc.tech.IsoDep;
26import android.nfc.tech.MifareClassic;
27import android.nfc.tech.MifareUltralight;
28import android.nfc.tech.Ndef;
29import android.nfc.tech.NfcA;
30import android.nfc.tech.NfcB;
31import android.nfc.tech.NfcF;
32import android.nfc.tech.NfcV;
33import android.nfc.tech.NfcBarcode;
34import android.nfc.tech.TagTechnology;
35import android.os.Bundle;
36import android.util.Log;
37
38/**
39 * Native interface to the NFC tag functions
40 */
41public 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                reconnect();
816            }
817            break;
818        }
819
820        if (ndefMsg == null && foundFormattable) {
821            // Tag is not NDEF yet, and found a formattable target,
822            // so add formattable tech to tech list.
823            addNdefFormatableTechnology(
824                    formattableHandle,
825                    formattableLibNfcType);
826        }
827
828        return ndefMsg;
829    }
830}
831