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