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