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 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