| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.nfc.dhimpl; |
| |
| import android.annotation.Nullable; |
| import com.android.nfc.DeviceHost; |
| import com.android.nfc.DeviceHost.TagEndpoint; |
| |
| import android.nfc.FormatException; |
| import android.nfc.NdefMessage; |
| import android.nfc.tech.IsoDep; |
| import android.nfc.tech.MifareClassic; |
| import android.nfc.tech.MifareUltralight; |
| import android.nfc.tech.Ndef; |
| import android.nfc.tech.NfcA; |
| import android.nfc.tech.NfcB; |
| import android.nfc.tech.NfcF; |
| import android.nfc.tech.NfcV; |
| import android.nfc.tech.NfcBarcode; |
| import android.nfc.tech.TagTechnology; |
| import android.os.Bundle; |
| import android.util.Log; |
| |
| /** |
| * Native interface to the NFC tag functions |
| */ |
| public class NativeNfcTag implements TagEndpoint { |
| static final boolean DBG = true; |
| |
| static final int STATUS_CODE_TARGET_LOST = 146; |
| |
| private int[] mTechList; |
| private int[] mTechHandles; |
| private int[] mTechLibNfcTypes; |
| private Bundle[] mTechExtras; |
| private byte[][] mTechPollBytes; |
| private byte[][] mTechActBytes; |
| private byte[] mUid; |
| |
| // mConnectedHandle stores the *real* libnfc handle |
| // that we're connected to. |
| private int mConnectedHandle; |
| |
| // mConnectedTechIndex stores to which technology |
| // the upper layer stack is connected. Note that |
| // we may be connected to a libnfchandle without being |
| // connected to a technology - technology changes |
| // may occur runtime, whereas the underlying handle |
| // could stay present. Usually all technologies are on the |
| // same handle, with the exception of multi-protocol |
| // tags. |
| private int mConnectedTechIndex; // Index in mTechHandles |
| |
| private final String TAG = "NativeNfcTag"; |
| |
| private boolean mIsPresent; // Whether the tag is known to be still present |
| |
| private PresenceCheckWatchdog mWatchdog; |
| class PresenceCheckWatchdog extends Thread { |
| |
| private final int watchdogTimeout; |
| private final DeviceHost.TagDisconnectedCallback tagDisconnectedCallback; |
| |
| private boolean isPresent = true; |
| private boolean isStopped = false; |
| private boolean isPaused = false; |
| private boolean doCheck = true; |
| |
| public PresenceCheckWatchdog(int presenceCheckDelay, |
| @Nullable DeviceHost.TagDisconnectedCallback callback) { |
| watchdogTimeout = presenceCheckDelay; |
| tagDisconnectedCallback = callback; |
| } |
| |
| public synchronized void pause() { |
| isPaused = true; |
| doCheck = false; |
| this.notifyAll(); |
| } |
| |
| public synchronized void doResume() { |
| isPaused = false; |
| // We don't want to resume presence checking immediately, |
| // but go through at least one more wait period. |
| doCheck = false; |
| this.notifyAll(); |
| } |
| |
| public synchronized void end() { |
| isStopped = true; |
| doCheck = false; |
| this.notifyAll(); |
| } |
| |
| @Override |
| public void run() { |
| synchronized (this) { |
| if (DBG) Log.d(TAG, "Starting background presence check"); |
| while (isPresent && !isStopped) { |
| try { |
| if (!isPaused) { |
| doCheck = true; |
| } |
| this.wait(watchdogTimeout); |
| if (doCheck) { |
| isPresent = doPresenceCheck(); |
| } else { |
| // 1) We are paused, waiting for unpause |
| // 2) We just unpaused, do pres check in next iteration |
| // (after watchdogTimeout ms sleep) |
| // 3) We just set the timeout, wait for this timeout |
| // to expire once first. |
| // 4) We just stopped, exit loop anyway |
| } |
| } catch (InterruptedException e) { |
| // Activity detected, loop |
| } |
| } |
| } |
| |
| synchronized (NativeNfcTag.this) { |
| mIsPresent = false; |
| } |
| // Restart the polling loop |
| |
| Log.d(TAG, "Tag lost, restarting polling loop"); |
| doDisconnect(); |
| if (tagDisconnectedCallback != null) { |
| tagDisconnectedCallback.onTagDisconnected(mConnectedHandle); |
| } |
| if (DBG) Log.d(TAG, "Stopping background presence check"); |
| } |
| } |
| |
| private native int doConnect(int handle); |
| public synchronized int connectWithStatus(int technology) { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| int status = -1; |
| for (int i = 0; i < mTechList.length; i++) { |
| if (mTechList[i] == technology) { |
| // Get the handle and connect, if not already connected |
| if (mConnectedHandle != mTechHandles[i]) { |
| // We're not yet connected to this handle, there are |
| // a few scenario's here: |
| // 1) We are not connected to anything yet - allow |
| // 2) We are connected to a technology which has |
| // a different handle (multi-protocol tag); we support |
| // switching to that. |
| if (mConnectedHandle == -1) { |
| // Not connected yet |
| //status = doConnect(mTechHandles[i]); |
| status = doConnect(i); |
| } else { |
| // Connect to a tech with a different handle |
| Log.d(TAG,"Connect to a tech with a different handle"); |
| //status = reconnectWithStatus(mTechHandles[i]); |
| status = reconnectWithStatus(i); |
| } |
| if (status == 0) { |
| mConnectedHandle = mTechHandles[i]; |
| mConnectedTechIndex = i; |
| } |
| } else { |
| // 1) We are connected to a technology which has the same |
| // handle; we do not support connecting at a different |
| // level (libnfc auto-activates to the max level on |
| // any handle). |
| // 2) We are connecting to the ndef technology - always |
| // allowed. |
| if ((technology == TagTechnology.NDEF) || |
| (technology == TagTechnology.NDEF_FORMATABLE)) { |
| // special case for NDEF, this will cause switch to ISO_DEP frame intf |
| i = 0; |
| // status = 0; |
| } |
| status = reconnectWithStatus(i); |
| /* |
| if ((technology != TagTechnology.ISO_DEP) && |
| (hasTechOnHandle(TagTechnology.ISO_DEP, mTechHandles[i]))) { |
| // Don't allow to connect a -4 tag at a different level |
| // than IsoDep, as this is not supported by |
| // libNFC. |
| // revised for NFCA... do allow to connect a -4 tag at this level. |
| Log.d(TAG,"Connect to a tech with same different handle (rf intf change)"); |
| status = reconnectWithStatus(i); |
| if (status == 0) { |
| mConnectedHandle = mTechHandles[i]; |
| mConnectedTechIndex = i; |
| } |
| //status = 0; |
| } else { |
| status = 0; |
| } |
| */ |
| |
| |
| if (status == 0) { |
| mConnectedTechIndex = i; |
| // Handle was already identical |
| } |
| } |
| break; |
| } |
| } |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return status; |
| } |
| @Override |
| public synchronized boolean connect(int technology) { |
| return connectWithStatus(technology) == 0; |
| } |
| |
| @Override |
| public synchronized void startPresenceChecking(int presenceCheckDelay, |
| DeviceHost.TagDisconnectedCallback callback) { |
| // Once we start presence checking, we allow the upper layers |
| // to know the tag is in the field. |
| mIsPresent = true; |
| if (mWatchdog == null) { |
| mWatchdog = new PresenceCheckWatchdog(presenceCheckDelay, callback); |
| mWatchdog.start(); |
| } |
| } |
| |
| @Override |
| public synchronized boolean isPresent() { |
| // Returns whether the tag is still in the field to the best |
| // of our knowledge. |
| return mIsPresent; |
| } |
| native boolean doDisconnect(); |
| @Override |
| public boolean disconnect() { |
| boolean result = false; |
| PresenceCheckWatchdog watchdog; |
| synchronized (this) { |
| mIsPresent = false; |
| watchdog = mWatchdog; |
| } |
| if (watchdog != null) { |
| // Watchdog has already disconnected or will do it |
| watchdog.end(); |
| try { |
| watchdog.join(); |
| } catch (InterruptedException e) { |
| // Should never happen. |
| } |
| synchronized (this) { |
| mWatchdog = null; |
| } |
| result = true; |
| } else { |
| result = doDisconnect(); |
| } |
| |
| mConnectedTechIndex = -1; |
| mConnectedHandle = -1; |
| return result; |
| } |
| |
| native int doReconnect(); |
| public synchronized int reconnectWithStatus() { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| int status = doReconnect(); |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return status; |
| } |
| @Override |
| public synchronized boolean reconnect() { |
| return reconnectWithStatus() == 0; |
| } |
| |
| native int doHandleReconnect(int handle); |
| public synchronized int reconnectWithStatus(int handle) { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| int status = doHandleReconnect(handle); |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return status; |
| } |
| |
| private native byte[] doTransceive(byte[] data, boolean raw, int[] returnCode); |
| @Override |
| public synchronized byte[] transceive(byte[] data, boolean raw, int[] returnCode) { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| byte[] result = doTransceive(data, raw, returnCode); |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return result; |
| } |
| |
| private native int doCheckNdef(int[] ndefinfo); |
| private synchronized int checkNdefWithStatus(int[] ndefinfo) { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| int status = doCheckNdef(ndefinfo); |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return status; |
| } |
| @Override |
| public synchronized boolean checkNdef(int[] ndefinfo) { |
| return checkNdefWithStatus(ndefinfo) == 0; |
| } |
| |
| private native byte[] doRead(); |
| @Override |
| public synchronized byte[] readNdef() { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| byte[] result = doRead(); |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return result; |
| } |
| |
| private native boolean doWrite(byte[] buf); |
| @Override |
| public synchronized boolean writeNdef(byte[] buf) { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| boolean result = doWrite(buf); |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return result; |
| } |
| |
| native boolean doPresenceCheck(); |
| @Override |
| public synchronized boolean presenceCheck() { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| boolean result = doPresenceCheck(); |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return result; |
| } |
| |
| native boolean doNdefFormat(byte[] key); |
| @Override |
| public synchronized boolean formatNdef(byte[] key) { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| boolean result = doNdefFormat(key); |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return result; |
| } |
| |
| native boolean doMakeReadonly(byte[] key); |
| @Override |
| public synchronized boolean makeReadOnly() { |
| if (mWatchdog != null) { |
| mWatchdog.pause(); |
| } |
| boolean result; |
| if (hasTech(TagTechnology.MIFARE_CLASSIC)) { |
| result = doMakeReadonly(MifareClassic.KEY_DEFAULT); |
| } else { |
| // No key needed for other technologies |
| result = doMakeReadonly(new byte[] {}); |
| } |
| if (mWatchdog != null) { |
| mWatchdog.doResume(); |
| } |
| return result; |
| } |
| |
| native boolean doIsIsoDepNdefFormatable(byte[] poll, byte[] act); |
| @Override |
| public synchronized boolean isNdefFormatable() { |
| // Let native code decide whether the currently activated tag |
| // is formatable. Although the name of the JNI function refers |
| // to ISO-DEP, the JNI function checks all tag types. |
| return doIsIsoDepNdefFormatable(mTechPollBytes[0], |
| mTechActBytes[0]); |
| } |
| |
| @Override |
| public int getHandle() { |
| // This is just a handle for the clients; it can simply use the first |
| // technology handle we have. |
| if (mTechHandles.length > 0) { |
| return mTechHandles[0]; |
| } else { |
| return 0; |
| } |
| } |
| |
| @Override |
| public byte[] getUid() { |
| return mUid; |
| } |
| |
| @Override |
| public int[] getTechList() { |
| return mTechList; |
| } |
| |
| private int getConnectedHandle() { |
| return mConnectedHandle; |
| } |
| |
| private int getConnectedLibNfcType() { |
| if (mConnectedTechIndex != -1 && mConnectedTechIndex < mTechLibNfcTypes.length) { |
| return mTechLibNfcTypes[mConnectedTechIndex]; |
| } else { |
| return 0; |
| } |
| } |
| |
| @Override |
| public int getConnectedTechnology() { |
| if (mConnectedTechIndex != -1 && mConnectedTechIndex < mTechList.length) { |
| return mTechList[mConnectedTechIndex]; |
| } else { |
| return 0; |
| } |
| } |
| native int doGetNdefType(int libnfctype, int javatype); |
| private int getNdefType(int libnfctype, int javatype) { |
| return doGetNdefType(libnfctype, javatype); |
| } |
| |
| private void addTechnology(int tech, int handle, int libnfctype) { |
| int[] mNewTechList = new int[mTechList.length + 1]; |
| System.arraycopy(mTechList, 0, mNewTechList, 0, mTechList.length); |
| mNewTechList[mTechList.length] = tech; |
| mTechList = mNewTechList; |
| |
| int[] mNewHandleList = new int[mTechHandles.length + 1]; |
| System.arraycopy(mTechHandles, 0, mNewHandleList, 0, mTechHandles.length); |
| mNewHandleList[mTechHandles.length] = handle; |
| mTechHandles = mNewHandleList; |
| |
| int[] mNewTypeList = new int[mTechLibNfcTypes.length + 1]; |
| System.arraycopy(mTechLibNfcTypes, 0, mNewTypeList, 0, mTechLibNfcTypes.length); |
| mNewTypeList[mTechLibNfcTypes.length] = libnfctype; |
| mTechLibNfcTypes = mNewTypeList; |
| } |
| |
| @Override |
| public void removeTechnology(int tech) { |
| synchronized (this) { |
| int techIndex = getTechIndex(tech); |
| if (techIndex != -1) { |
| int[] mNewTechList = new int[mTechList.length - 1]; |
| System.arraycopy(mTechList, 0, mNewTechList, 0, techIndex); |
| System.arraycopy(mTechList, techIndex + 1, mNewTechList, techIndex, |
| mTechList.length - techIndex - 1); |
| mTechList = mNewTechList; |
| |
| int[] mNewHandleList = new int[mTechHandles.length - 1]; |
| System.arraycopy(mTechHandles, 0, mNewHandleList, 0, techIndex); |
| System.arraycopy(mTechHandles, techIndex + 1, mNewTechList, techIndex, |
| mTechHandles.length - techIndex - 1); |
| mTechHandles = mNewHandleList; |
| |
| int[] mNewTypeList = new int[mTechLibNfcTypes.length - 1]; |
| System.arraycopy(mTechLibNfcTypes, 0, mNewTypeList, 0, techIndex); |
| System.arraycopy(mTechLibNfcTypes, techIndex + 1, mNewTypeList, techIndex, |
| mTechLibNfcTypes.length - techIndex - 1); |
| mTechLibNfcTypes = mNewTypeList; |
| |
| //The technology must be removed from the mTechExtras array, |
| //just like the above arrays. |
| //Remove the specified element from the array, |
| //then shift the remaining elements by one. |
| if (mTechExtras != null) |
| { |
| Bundle[] mNewTechExtras = new Bundle[mTechExtras.length - 1]; |
| System.arraycopy(mTechExtras, 0, mNewTechExtras, 0, techIndex); |
| System.arraycopy(mTechExtras, techIndex + 1, mNewTechExtras, techIndex, |
| mTechExtras.length - techIndex - 1); |
| mTechExtras = mNewTechExtras; |
| } |
| } |
| } |
| } |
| |
| public void addNdefFormatableTechnology(int handle, int libnfcType) { |
| synchronized (this) { |
| addTechnology(TagTechnology.NDEF_FORMATABLE, handle, libnfcType); |
| } |
| } |
| |
| // This method exists to "patch in" the ndef technologies, |
| // which is done inside Java instead of the native JNI code. |
| // To not create some nasty dependencies on the order on which things |
| // are called (most notably getTechExtras()), it needs some additional |
| // checking. |
| public void addNdefTechnology(NdefMessage msg, int handle, int libnfcType, |
| int javaType, int maxLength, int cardState) { |
| synchronized (this) { |
| addTechnology(TagTechnology.NDEF, handle, libnfcType); |
| |
| Bundle extras = new Bundle(); |
| extras.putParcelable(Ndef.EXTRA_NDEF_MSG, msg); |
| extras.putInt(Ndef.EXTRA_NDEF_MAXLENGTH, maxLength); |
| extras.putInt(Ndef.EXTRA_NDEF_CARDSTATE, cardState); |
| extras.putInt(Ndef.EXTRA_NDEF_TYPE, getNdefType(libnfcType, javaType)); |
| |
| if (mTechExtras == null) { |
| // This will build the tech extra's for the first time, |
| // including a NULL ref for the NDEF tech we generated above. |
| Bundle[] builtTechExtras = getTechExtras(); |
| builtTechExtras[builtTechExtras.length - 1] = extras; |
| } |
| else { |
| // Tech extras were built before, patch the NDEF one in |
| Bundle[] oldTechExtras = getTechExtras(); |
| Bundle[] newTechExtras = new Bundle[oldTechExtras.length + 1]; |
| System.arraycopy(oldTechExtras, 0, newTechExtras, 0, oldTechExtras.length); |
| newTechExtras[oldTechExtras.length] = extras; |
| mTechExtras = newTechExtras; |
| } |
| |
| |
| } |
| } |
| |
| private int getTechIndex(int tech) { |
| int techIndex = -1; |
| for (int i = 0; i < mTechList.length; i++) { |
| if (mTechList[i] == tech) { |
| techIndex = i; |
| break; |
| } |
| } |
| return techIndex; |
| } |
| |
| private boolean hasTech(int tech) { |
| boolean hasTech = false; |
| for (int i = 0; i < mTechList.length; i++) { |
| if (mTechList[i] == tech) { |
| hasTech = true; |
| break; |
| } |
| } |
| return hasTech; |
| } |
| |
| private boolean hasTechOnHandle(int tech, int handle) { |
| boolean hasTech = false; |
| for (int i = 0; i < mTechList.length; i++) { |
| if (mTechList[i] == tech && mTechHandles[i] == handle) { |
| hasTech = true; |
| break; |
| } |
| } |
| return hasTech; |
| |
| } |
| |
| private boolean isUltralightC() { |
| /* Make a best-effort attempt at classifying ULTRALIGHT |
| * vs ULTRALIGHT-C (based on NXP's public AN1303). |
| * The memory layout is as follows: |
| * Page # BYTE1 BYTE2 BYTE3 BYTE4 |
| * 2 INT1 INT2 LOCK LOCK |
| * 3 OTP OTP OTP OTP (NDEF CC if NDEF-formatted) |
| * 4 DATA DATA DATA DATA (version info if factory-state) |
| * |
| * Read four blocks from page 2, which will get us both |
| * the lock page, the OTP page and the version info. |
| */ |
| boolean isUltralightC = false; |
| byte[] readCmd = { 0x30, 0x02 }; |
| int[] retCode = new int[2]; |
| byte[] respData = transceive(readCmd, false, retCode); |
| if (respData != null && respData.length == 16) { |
| // Check the lock bits (last 2 bytes in page2) |
| // and the OTP bytes (entire page 3) |
| if (respData[2] == 0 && respData[3] == 0 && respData[4] == 0 && |
| respData[5] == 0 && respData[6] == 0 && respData[7] == 0) { |
| // Very likely to be a blank card, look at version info |
| // in page 4. |
| if ((respData[8] == (byte)0x02) && respData[9] == (byte)0x00) { |
| // This is Ultralight-C |
| isUltralightC = true; |
| } else { |
| // 0xFF 0xFF would indicate Ultralight, but we also use Ultralight |
| // as a fallback if it's anything else |
| isUltralightC = false; |
| } |
| } else { |
| // See if we can find the NDEF CC in the OTP page and if it's |
| // smaller than major version two |
| if (respData[4] == (byte)0xE1 && ((respData[5] & 0xff) < 0x20)) { |
| // OK, got NDEF. Technically we'd have to search for the |
| // NDEF TLV as well. However, this would add too much |
| // time for discovery and we can make already make a good guess |
| // with the data we have here. Byte 2 of the OTP page |
| // indicates the size of the tag - 0x06 is UL, anything |
| // above indicates UL-C. |
| if ((respData[6] & 0xff) > 0x06) { |
| isUltralightC = true; |
| } |
| } else { |
| // Fall back to ultralight |
| isUltralightC = false; |
| } |
| } |
| } |
| return isUltralightC; |
| } |
| |
| @Override |
| public Bundle[] getTechExtras() { |
| synchronized (this) { |
| if (mTechExtras != null) return mTechExtras; |
| mTechExtras = new Bundle[mTechList.length]; |
| for (int i = 0; i < mTechList.length; i++) { |
| Bundle extras = new Bundle(); |
| switch (mTechList[i]) { |
| case TagTechnology.NFC_A: { |
| byte[] actBytes = mTechActBytes[i]; |
| if ((actBytes != null) && (actBytes.length > 0)) { |
| extras.putShort(NfcA.EXTRA_SAK, (short) (actBytes[0] & (short) 0xFF)); |
| } else { |
| // Unfortunately Jewel doesn't have act bytes, |
| // ignore this case. |
| } |
| extras.putByteArray(NfcA.EXTRA_ATQA, mTechPollBytes[i]); |
| break; |
| } |
| |
| case TagTechnology.NFC_B: { |
| // What's returned from the PN544 is actually: |
| // 4 bytes app data |
| // 3 bytes prot info |
| byte[] appData = new byte[4]; |
| byte[] protInfo = new byte[3]; |
| if (mTechPollBytes[i].length >= 7) { |
| System.arraycopy(mTechPollBytes[i], 0, appData, 0, 4); |
| System.arraycopy(mTechPollBytes[i], 4, protInfo, 0, 3); |
| |
| extras.putByteArray(NfcB.EXTRA_APPDATA, appData); |
| extras.putByteArray(NfcB.EXTRA_PROTINFO, protInfo); |
| } |
| break; |
| } |
| |
| case TagTechnology.NFC_F: { |
| byte[] pmm = new byte[8]; |
| byte[] sc = new byte[2]; |
| if (mTechPollBytes[i].length >= 8) { |
| // At least pmm is present |
| System.arraycopy(mTechPollBytes[i], 0, pmm, 0, 8); |
| extras.putByteArray(NfcF.EXTRA_PMM, pmm); |
| } |
| if (mTechPollBytes[i].length == 10) { |
| System.arraycopy(mTechPollBytes[i], 8, sc, 0, 2); |
| extras.putByteArray(NfcF.EXTRA_SC, sc); |
| } |
| break; |
| } |
| |
| case TagTechnology.ISO_DEP: { |
| if (hasTech(TagTechnology.NFC_A)) { |
| extras.putByteArray(IsoDep.EXTRA_HIST_BYTES, mTechActBytes[i]); |
| } |
| else { |
| extras.putByteArray(IsoDep.EXTRA_HI_LAYER_RESP, mTechActBytes[i]); |
| } |
| break; |
| } |
| |
| case TagTechnology.NFC_V: { |
| // First byte response flags, second byte DSFID |
| if (mTechPollBytes[i] != null && mTechPollBytes[i].length >= 2) { |
| extras.putByte(NfcV.EXTRA_RESP_FLAGS, mTechPollBytes[i][0]); |
| extras.putByte(NfcV.EXTRA_DSFID, mTechPollBytes[i][1]); |
| } |
| break; |
| } |
| |
| case TagTechnology.MIFARE_ULTRALIGHT: { |
| boolean isUlc = isUltralightC(); |
| extras.putBoolean(MifareUltralight.EXTRA_IS_UL_C, isUlc); |
| break; |
| } |
| |
| case TagTechnology.NFC_BARCODE: { |
| // hard code this for now, this is the only valid type |
| extras.putInt(NfcBarcode.EXTRA_BARCODE_TYPE, NfcBarcode.TYPE_KOVIO); |
| break; |
| } |
| |
| default: { |
| // Leave the entry in the array null |
| continue; |
| } |
| } |
| mTechExtras[i] = extras; |
| } |
| return mTechExtras; |
| } |
| } |
| |
| @Override |
| public NdefMessage findAndReadNdef() { |
| // Try to find NDEF on any of the technologies. |
| int[] technologies = getTechList(); |
| int[] handles = mTechHandles; |
| NdefMessage ndefMsg = null; |
| boolean foundFormattable = false; |
| int formattableHandle = 0; |
| int formattableLibNfcType = 0; |
| int status; |
| |
| for (int techIndex = 0; techIndex < technologies.length; techIndex++) { |
| // have we seen this handle before? |
| for (int i = 0; i < techIndex; i++) { |
| if (handles[i] == handles[techIndex]) { |
| continue; // don't check duplicate handles |
| } |
| } |
| |
| status = connectWithStatus(technologies[techIndex]); |
| if (status != 0) { |
| Log.d(TAG, "Connect Failed - status = "+ status); |
| if (status == STATUS_CODE_TARGET_LOST) { |
| break; |
| } |
| continue; // try next handle |
| } |
| // Check if this type is NDEF formatable |
| if (!foundFormattable) { |
| if (isNdefFormatable()) { |
| foundFormattable = true; |
| formattableHandle = getConnectedHandle(); |
| formattableLibNfcType = getConnectedLibNfcType(); |
| // We'll only add formattable tech if no ndef is |
| // found - this is because libNFC refuses to format |
| // an already NDEF formatted tag. |
| } |
| reconnect(); |
| } |
| |
| int[] ndefinfo = new int[2]; |
| status = checkNdefWithStatus(ndefinfo); |
| if (status != 0) { |
| Log.d(TAG, "Check NDEF Failed - status = " + status); |
| if (status == STATUS_CODE_TARGET_LOST) { |
| break; |
| } |
| continue; // try next handle |
| } |
| |
| // found our NDEF handle |
| boolean generateEmptyNdef = false; |
| |
| int supportedNdefLength = ndefinfo[0]; |
| int cardState = ndefinfo[1]; |
| byte[] buff = readNdef(); |
| if (buff != null) { |
| try { |
| ndefMsg = new NdefMessage(buff); |
| addNdefTechnology(ndefMsg, |
| getConnectedHandle(), |
| getConnectedLibNfcType(), |
| getConnectedTechnology(), |
| supportedNdefLength, cardState); |
| reconnect(); |
| } catch (FormatException e) { |
| // Create an intent anyway, without NDEF messages |
| generateEmptyNdef = true; |
| } |
| } else { |
| generateEmptyNdef = true; |
| } |
| |
| if (generateEmptyNdef) { |
| ndefMsg = null; |
| foundFormattable = false; |
| reconnect(); |
| } |
| break; |
| } |
| |
| if (ndefMsg == null && foundFormattable) { |
| // Tag is not NDEF yet, and found a formattable target, |
| // so add formattable tech to tech list. |
| addNdefFormatableTechnology( |
| formattableHandle, |
| formattableLibNfcType); |
| } |
| |
| return ndefMsg; |
| } |
| } |