summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/com/android/nfc/NfcService.java4
-rwxr-xr-xsrc/com/android/nfc/nxp/NativeLlcpConnectionlessSocket.java78
-rwxr-xr-xsrc/com/android/nfc/nxp/NativeLlcpServiceSocket.java53
-rwxr-xr-xsrc/com/android/nfc/nxp/NativeLlcpSocket.java99
-rwxr-xr-xsrc/com/android/nfc/nxp/NativeNfcManager.java373
-rwxr-xr-xsrc/com/android/nfc/nxp/NativeNfcSecureElement.java67
-rwxr-xr-xsrc/com/android/nfc/nxp/NativeNfcTag.java803
-rwxr-xr-xsrc/com/android/nfc/nxp/NativeP2pDevice.java77
8 files changed, 1552 insertions, 2 deletions
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index 3e7a6b5..602b25d 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -23,8 +23,8 @@ import com.android.nfc.DeviceHost.LlcpSocket;
import com.android.nfc.DeviceHost.NfcDepEndpoint;
import com.android.nfc.DeviceHost.TagEndpoint;
import com.android.nfc.handover.HandoverManager;
-import com.android.nfc.dhimpl.NativeNfcManager;
-import com.android.nfc.dhimpl.NativeNfcSecureElement;
+import com.android.nfc.nxp.NativeNfcManager;
+import com.android.nfc.nxp.NativeNfcSecureElement;
import android.app.Application;
import android.app.KeyguardManager;
diff --git a/src/com/android/nfc/nxp/NativeLlcpConnectionlessSocket.java b/src/com/android/nfc/nxp/NativeLlcpConnectionlessSocket.java
new file mode 100755
index 0000000..c9d3b5d
--- /dev/null
+++ b/src/com/android/nfc/nxp/NativeLlcpConnectionlessSocket.java
@@ -0,0 +1,78 @@
+/*
+ * 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.nxp;
+
+import com.android.nfc.DeviceHost;
+import com.android.nfc.LlcpPacket;
+
+import java.io.IOException;
+
+/**
+ * LlcpConnectionlessSocket represents a LLCP Connectionless object to be used
+ * in a connectionless communication
+ */
+public class NativeLlcpConnectionlessSocket implements DeviceHost.LlcpConnectionlessSocket {
+
+ private int mHandle;
+ private int mSap;
+ private int mLinkMiu;
+
+ public NativeLlcpConnectionlessSocket() { }
+
+ public native boolean doSendTo(int sap, byte[] data);
+
+ public native LlcpPacket doReceiveFrom(int linkMiu);
+
+ public native boolean doClose();
+
+ @Override
+ public int getLinkMiu(){
+ return mLinkMiu;
+ }
+
+ @Override
+ public int getSap(){
+ return mSap;
+ }
+
+ @Override
+ public void send(int sap, byte[] data) throws IOException {
+ if (!doSendTo(sap, data)) {
+ throw new IOException();
+ }
+ }
+
+ @Override
+ public LlcpPacket receive() throws IOException {
+ LlcpPacket packet = doReceiveFrom(mLinkMiu);
+ if (packet == null) {
+ throw new IOException();
+ }
+ return packet;
+ }
+
+ public int getHandle(){
+ return mHandle;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (!doClose()) {
+ throw new IOException();
+ }
+ }
+}
diff --git a/src/com/android/nfc/nxp/NativeLlcpServiceSocket.java b/src/com/android/nfc/nxp/NativeLlcpServiceSocket.java
new file mode 100755
index 0000000..531afd8
--- /dev/null
+++ b/src/com/android/nfc/nxp/NativeLlcpServiceSocket.java
@@ -0,0 +1,53 @@
+/*
+ * 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.nxp;
+
+import com.android.nfc.DeviceHost;
+import com.android.nfc.DeviceHost.LlcpSocket;
+
+import java.io.IOException;
+
+/**
+ * LlcpServiceSocket represents a LLCP Service to be used in a
+ * Connection-oriented communication
+ */
+public class NativeLlcpServiceSocket implements DeviceHost.LlcpServerSocket {
+ private int mHandle;
+ private int mLocalMiu;
+ private int mLocalRw;
+ private int mLocalLinearBufferLength;
+ private int mSap;
+ private String mServiceName;
+
+ public NativeLlcpServiceSocket(){ }
+
+ private native NativeLlcpSocket doAccept(int miu, int rw, int linearBufferLength);
+ @Override
+ public LlcpSocket accept() throws IOException {
+ LlcpSocket socket = doAccept(mLocalMiu, mLocalRw, mLocalLinearBufferLength);
+ if (socket == null) throw new IOException();
+ return socket;
+ }
+
+ private native boolean doClose();
+ @Override
+ public void close() throws IOException {
+ if (!doClose()) {
+ throw new IOException();
+ }
+ }
+}
diff --git a/src/com/android/nfc/nxp/NativeLlcpSocket.java b/src/com/android/nfc/nxp/NativeLlcpSocket.java
new file mode 100755
index 0000000..a337d35
--- /dev/null
+++ b/src/com/android/nfc/nxp/NativeLlcpSocket.java
@@ -0,0 +1,99 @@
+/*
+ * 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.nxp;
+
+import com.android.nfc.DeviceHost;
+
+import java.io.IOException;
+
+/**
+ * LlcpClientSocket represents a LLCP Connection-Oriented client to be used in a
+ * connection-oriented communication
+ */
+public class NativeLlcpSocket implements DeviceHost.LlcpSocket {
+ private int mHandle;
+ private int mSap;
+ private int mLocalMiu;
+ private int mLocalRw;
+
+ public NativeLlcpSocket(){ }
+
+ private native boolean doConnect(int nSap);
+ @Override
+ public void connectToSap(int sap) throws IOException {
+ if (!doConnect(sap)) {
+ throw new IOException();
+ }
+ }
+
+ private native boolean doConnectBy(String sn);
+ @Override
+ public void connectToService(String serviceName) throws IOException {
+ if (!doConnectBy(serviceName)) {
+ throw new IOException();
+ }
+ }
+
+ private native boolean doClose();
+ @Override
+ public void close() throws IOException {
+ if (!doClose()) {
+ throw new IOException();
+ }
+ }
+
+ private native boolean doSend(byte[] data);
+ @Override
+ public void send(byte[] data) throws IOException {
+ if (!doSend(data)) {
+ throw new IOException();
+ }
+ }
+
+ private native int doReceive(byte[] recvBuff);
+ @Override
+ public int receive(byte[] recvBuff) throws IOException {
+ int receiveLength = doReceive(recvBuff);
+ if (receiveLength == -1) {
+ throw new IOException();
+ }
+ return receiveLength;
+ }
+
+ private native int doGetRemoteSocketMiu();
+ @Override
+ public int getRemoteMiu() { return doGetRemoteSocketMiu(); }
+
+ private native int doGetRemoteSocketRw();
+ @Override
+ public int getRemoteRw() { return doGetRemoteSocketRw(); }
+
+ @Override
+ public int getLocalSap(){
+ return mSap;
+ }
+
+ @Override
+ public int getLocalMiu(){
+ return mLocalMiu;
+ }
+
+ @Override
+ public int getLocalRw(){
+ return mLocalRw;
+ }
+}
diff --git a/src/com/android/nfc/nxp/NativeNfcManager.java b/src/com/android/nfc/nxp/NativeNfcManager.java
new file mode 100755
index 0000000..4bd8c24
--- /dev/null
+++ b/src/com/android/nfc/nxp/NativeNfcManager.java
@@ -0,0 +1,373 @@
+/*
+ * 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.nxp;
+
+import com.android.nfc.DeviceHost;
+import com.android.nfc.LlcpException;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.nfc.ErrorCodes;
+import android.nfc.tech.Ndef;
+import android.nfc.tech.TagTechnology;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * Native interface to the NFC Manager functions
+ */
+public class NativeNfcManager implements DeviceHost {
+ private static final String TAG = "NativeNfcManager";
+
+ private static final String NFC_CONTROLLER_FIRMWARE_FILE_NAME = "/vendor/firmware/libpn544_fw.so";
+
+ static final String PREF = "NxpDeviceHost";
+
+ private static final String PREF_FIRMWARE_MODTIME = "firmware_modtime";
+ private static final long FIRMWARE_MODTIME_DEFAULT = -1;
+
+ static {
+ System.loadLibrary("nfc_jni");
+ }
+
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String INTERNAL_TARGET_DESELECTED_ACTION = "com.android.nfc.action.INTERNAL_TARGET_DESELECTED";
+
+ /* Native structure */
+ private int mNative;
+
+ private final DeviceHostListener mListener;
+ private final Context mContext;
+
+ public NativeNfcManager(Context context, DeviceHostListener listener) {
+ mListener = listener;
+ initializeNativeStructure();
+ mContext = context;
+ }
+
+ public native boolean initializeNativeStructure();
+
+ private native boolean doDownload();
+
+ public native int doGetLastError();
+
+ @Override
+ public void checkFirmware() {
+ // Check that the NFC controller firmware is up to date. This
+ // ensures that firmware updates are applied in a timely fashion,
+ // and makes it much less likely that the user will have to wait
+ // for a firmware download when they enable NFC in the settings
+ // app. Firmware download can take some time, so this should be
+ // run in a separate thread.
+
+ // check the timestamp of the firmware file
+ File firmwareFile;
+ int nbRetry = 0;
+ try {
+ firmwareFile = new File(NFC_CONTROLLER_FIRMWARE_FILE_NAME);
+ } catch(NullPointerException npe) {
+ Log.e(TAG,"path to firmware file was null");
+ return;
+ }
+
+ long modtime = firmwareFile.lastModified();
+
+ SharedPreferences prefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE);
+ long prev_fw_modtime = prefs.getLong(PREF_FIRMWARE_MODTIME, FIRMWARE_MODTIME_DEFAULT);
+ Log.d(TAG,"prev modtime: " + prev_fw_modtime);
+ Log.d(TAG,"new modtime: " + modtime);
+ if (prev_fw_modtime == modtime) {
+ return;
+ }
+
+ // FW download.
+ while(nbRetry < 5) {
+ Log.d(TAG,"Perform Download");
+ if(doDownload()) {
+ Log.d(TAG,"Download Success");
+ // Now that we've finished updating the firmware, save the new modtime.
+ prefs.edit().putLong(PREF_FIRMWARE_MODTIME, modtime).apply();
+ break;
+ } else {
+ Log.d(TAG,"Download Failed");
+ nbRetry++;
+ }
+ }
+ }
+
+ private native boolean doInitialize();
+
+ @Override
+ public boolean initialize() {
+ SharedPreferences prefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+
+ if (prefs.getBoolean(NativeNfcSecureElement.PREF_SE_WIRED, false)) {
+ try {
+ Thread.sleep (12000);
+ editor.putBoolean(NativeNfcSecureElement.PREF_SE_WIRED, false);
+ editor.apply();
+ } catch (InterruptedException e) { }
+ }
+
+ return doInitialize();
+ }
+
+ private native boolean doDeinitialize();
+
+ @Override
+ public boolean deinitialize() {
+ SharedPreferences prefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+
+ editor.putBoolean(NativeNfcSecureElement.PREF_SE_WIRED, false);
+ editor.apply();
+
+ return doDeinitialize();
+ }
+
+ @Override
+ public native void enableDiscovery();
+
+ @Override
+ public native void disableDiscovery();
+
+ @Override
+ public native int[] doGetSecureElementList();
+
+ @Override
+ public native void doSelectSecureElement();
+
+ @Override
+ public native void doDeselectSecureElement();
+
+
+ private native NativeLlcpConnectionlessSocket doCreateLlcpConnectionlessSocket(int nSap,
+ String sn);
+
+ @Override
+ public LlcpConnectionlessSocket createLlcpConnectionlessSocket(int nSap, String sn)
+ throws LlcpException {
+ LlcpConnectionlessSocket socket = doCreateLlcpConnectionlessSocket(nSap, sn);
+ if (socket != null) {
+ return socket;
+ } else {
+ /* Get Error Status */
+ int error = doGetLastError();
+
+ Log.d(TAG, "failed to create llcp socket: " + ErrorCodes.asString(error));
+
+ switch (error) {
+ case ErrorCodes.ERROR_BUFFER_TO_SMALL:
+ case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES:
+ throw new LlcpException(error);
+ default:
+ throw new LlcpException(ErrorCodes.ERROR_SOCKET_CREATION);
+ }
+ }
+ }
+
+ private native NativeLlcpServiceSocket doCreateLlcpServiceSocket(int nSap, String sn, int miu,
+ int rw, int linearBufferLength);
+ @Override
+ public LlcpServerSocket createLlcpServerSocket(int nSap, String sn, int miu,
+ int rw, int linearBufferLength) throws LlcpException {
+ LlcpServerSocket socket = doCreateLlcpServiceSocket(nSap, sn, miu, rw, linearBufferLength);
+ if (socket != null) {
+ return socket;
+ } else {
+ /* Get Error Status */
+ int error = doGetLastError();
+
+ Log.d(TAG, "failed to create llcp socket: " + ErrorCodes.asString(error));
+
+ switch (error) {
+ case ErrorCodes.ERROR_BUFFER_TO_SMALL:
+ case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES:
+ throw new LlcpException(error);
+ default:
+ throw new LlcpException(ErrorCodes.ERROR_SOCKET_CREATION);
+ }
+ }
+ }
+
+ private native NativeLlcpSocket doCreateLlcpSocket(int sap, int miu, int rw,
+ int linearBufferLength);
+ @Override
+ public LlcpSocket createLlcpSocket(int sap, int miu, int rw,
+ int linearBufferLength) throws LlcpException {
+ LlcpSocket socket = doCreateLlcpSocket(sap, miu, rw, linearBufferLength);
+ if (socket != null) {
+ return socket;
+ } else {
+ /* Get Error Status */
+ int error = doGetLastError();
+
+ Log.d(TAG, "failed to create llcp socket: " + ErrorCodes.asString(error));
+
+ switch (error) {
+ case ErrorCodes.ERROR_BUFFER_TO_SMALL:
+ case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES:
+ throw new LlcpException(error);
+ default:
+ throw new LlcpException(ErrorCodes.ERROR_SOCKET_CREATION);
+ }
+ }
+ }
+
+ @Override
+ public native boolean doCheckLlcp();
+
+ @Override
+ public native boolean doActivateLlcp();
+
+ private native void doResetTimeouts();
+
+ @Override
+ public void resetTimeouts() {
+ doResetTimeouts();
+ }
+
+ @Override
+ public native void doAbort();
+
+ private native boolean doSetTimeout(int tech, int timeout);
+ @Override
+ public boolean setTimeout(int tech, int timeout) {
+ return doSetTimeout(tech, timeout);
+ }
+
+ private native int doGetTimeout(int tech);
+ @Override
+ public int getTimeout(int tech) {
+ return doGetTimeout(tech);
+ }
+
+
+ @Override
+ public boolean canMakeReadOnly(int ndefType) {
+ return (ndefType == Ndef.TYPE_1 || ndefType == Ndef.TYPE_2 ||
+ ndefType == Ndef.TYPE_MIFARE_CLASSIC);
+ }
+
+ @Override
+ public int getMaxTransceiveLength(int technology) {
+ switch (technology) {
+ case (TagTechnology.NFC_A):
+ case (TagTechnology.MIFARE_CLASSIC):
+ case (TagTechnology.MIFARE_ULTRALIGHT):
+ return 253; // PN544 RF buffer = 255 bytes, subtract two for CRC
+ case (TagTechnology.NFC_B):
+ return 0; // PN544 does not support transceive of raw NfcB
+ case (TagTechnology.NFC_V):
+ return 253; // PN544 RF buffer = 255 bytes, subtract two for CRC
+ case (TagTechnology.ISO_DEP):
+ /* The maximum length of a normal IsoDep frame consists of:
+ * CLA, INS, P1, P2, LC, LE + 255 payload bytes = 261 bytes
+ * such a frame is supported. Extended length frames however
+ * are not supported.
+ */
+ return 261; // Will be automatically split in two frames on the RF layer
+ case (TagTechnology.NFC_F):
+ return 252; // PN544 RF buffer = 255 bytes, subtract one for SoD, two for CRC
+ default:
+ return 0;
+ }
+
+ }
+
+ private native void doSetP2pInitiatorModes(int modes);
+ @Override
+ public void setP2pInitiatorModes(int modes) {
+ doSetP2pInitiatorModes(modes);
+ }
+
+ private native void doSetP2pTargetModes(int modes);
+ @Override
+ public void setP2pTargetModes(int modes) {
+ doSetP2pTargetModes(modes);
+ }
+
+ public boolean getExtendedLengthApdusSupported() {
+ // Not supported on the PN544
+ return false;
+ }
+
+ private native String doDump();
+ @Override
+ public String dump() {
+ return doDump();
+ }
+
+ /**
+ * Notifies Ndef Message (TODO: rename into notifyTargetDiscovered)
+ */
+ private void notifyNdefMessageListeners(NativeNfcTag tag) {
+ mListener.onRemoteEndpointDiscovered(tag);
+ }
+
+ /**
+ * Notifies transaction
+ */
+ private void notifyTargetDeselected() {
+ mListener.onCardEmulationDeselected();
+ }
+
+ /**
+ * Notifies transaction
+ */
+ private void notifyTransactionListeners(byte[] aid) {
+ mListener.onCardEmulationAidSelected(aid);
+ }
+
+ /**
+ * Notifies P2P Device detected, to activate LLCP link
+ */
+ private void notifyLlcpLinkActivation(NativeP2pDevice device) {
+ mListener.onLlcpLinkActivated(device);
+ }
+
+ /**
+ * Notifies P2P Device detected, to activate LLCP link
+ */
+ private void notifyLlcpLinkDeactivated(NativeP2pDevice device) {
+ mListener.onLlcpLinkDeactivated(device);
+ }
+
+ private void notifySeFieldActivated() {
+ mListener.onRemoteFieldActivated();
+ }
+
+ private void notifySeFieldDeactivated() {
+ mListener.onRemoteFieldDeactivated();
+ }
+
+ private void notifySeApduReceived(byte[] apdu) {
+ mListener.onSeApduReceived(apdu);
+ }
+
+ private void notifySeEmvCardRemoval() {
+ mListener.onSeEmvCardRemoval();
+ }
+
+ private void notifySeMifareAccess(byte[] block) {
+ mListener.onSeMifareAccess(block);
+ }
+}
diff --git a/src/com/android/nfc/nxp/NativeNfcSecureElement.java b/src/com/android/nfc/nxp/NativeNfcSecureElement.java
new file mode 100755
index 0000000..88f9b9d
--- /dev/null
+++ b/src/com/android/nfc/nxp/NativeNfcSecureElement.java
@@ -0,0 +1,67 @@
+/*
+ * 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.nxp;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+
+/**
+ * Native interface to the NFC Secure Element functions
+ *
+ * {@hide}
+ */
+public class NativeNfcSecureElement {
+
+ static final String PREF_SE_WIRED = "se_wired";
+
+ private final Context mContext;
+
+ SharedPreferences mPrefs;
+ SharedPreferences.Editor mPrefsEditor;
+
+ public NativeNfcSecureElement(Context context) {
+ mContext = context;
+
+ mPrefs = mContext.getSharedPreferences(NativeNfcManager.PREF, Context.MODE_PRIVATE);
+ mPrefsEditor = mPrefs.edit();
+ }
+
+ private native int doNativeOpenSecureElementConnection();
+
+ public int doOpenSecureElementConnection() {
+ mPrefsEditor.putBoolean(PREF_SE_WIRED, true);
+ mPrefsEditor.apply();
+
+ return doNativeOpenSecureElementConnection();
+ }
+
+ private native boolean doNativeDisconnectSecureElementConnection(int handle);
+
+ public boolean doDisconnect(int handle) {
+ mPrefsEditor.putBoolean(PREF_SE_WIRED, false);
+ mPrefsEditor.apply();
+
+ return doNativeDisconnectSecureElementConnection(handle);
+ }
+
+ public native byte[] doTransceive(int handle, byte[] data);
+
+ public native int[] doGetTechList(int handle);
+
+ public native byte [] doGetUid(int handle);
+}
diff --git a/src/com/android/nfc/nxp/NativeNfcTag.java b/src/com/android/nfc/nxp/NativeNfcTag.java
new file mode 100755
index 0000000..8996dfb
--- /dev/null
+++ b/src/com/android/nfc/nxp/NativeNfcTag.java
@@ -0,0 +1,803 @@
+/*
+ * 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.nxp;
+
+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.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 = false;
+
+ 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 int watchdogTimeout = 125;
+
+ private boolean isPresent = true;
+ private boolean isStopped = false;
+ private boolean isPaused = false;
+ private boolean doCheck = true;
+
+ 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();
+ }
+
+ public synchronized void setTimeout(int timeout) {
+ watchdogTimeout = timeout;
+ doCheck = false; // Do it only after we have waited "timeout" ms again
+ this.notifyAll();
+ }
+
+ @Override
+ public synchronized void run() {
+ 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
+ }
+ }
+ mIsPresent = false;
+ // Restart the polling loop
+
+ Log.d(TAG, "Tag lost, restarting polling loop");
+ doDisconnect();
+ 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]);
+ } else {
+ // Connect to a tech with a different handle
+ status = reconnectWithStatus(mTechHandles[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)) {
+ status = 0;
+ } else {
+ 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.
+ status = -1;
+ } 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() {
+ // 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();
+ 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 synchronized boolean disconnect() {
+ boolean result = false;
+
+ mIsPresent = false;
+ if (mWatchdog != null) {
+ // Watchdog has already disconnected or will do it
+ mWatchdog.end();
+ try {
+ mWatchdog.join();
+ } catch (InterruptedException e) {
+ // Should never happen.
+ }
+ 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() {
+ if (hasTech(TagTechnology.MIFARE_CLASSIC) || hasTech(TagTechnology.MIFARE_ULTRALIGHT)) {
+ // These are always formatable
+ return true;
+ }
+ if (hasTech(TagTechnology.NFC_V)) {
+ // Currently libnfc only formats NXP NFC-V tags
+ if (mUid[5] >= 1 && mUid[5] <= 3 && mUid[6] == 0x04) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // For ISO-DEP, call native code to determine at lower level if format
+ // is possible. It will need NFC-A poll/activation time bytes for this.
+ if (hasTech(TagTechnology.ISO_DEP)) {
+ int nfcaTechIndex = getTechIndex(TagTechnology.NFC_A);
+ if (nfcaTechIndex != -1) {
+ return doIsIsoDepNdefFormatable(mTechPollBytes[nfcaTechIndex],
+ mTechActBytes[nfcaTechIndex]);
+ } else {
+ return false;
+ }
+ } else {
+ // Formatting not supported by libNFC
+ return false;
+ }
+ }
+
+ @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;
+ }
+ }
+ }
+
+ 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;
+ }
+
+ 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;
+ addNdefTechnology(null,
+ getConnectedHandle(),
+ getConnectedLibNfcType(),
+ getConnectedTechnology(),
+ supportedNdefLength, cardState);
+ 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;
+ }
+}
diff --git a/src/com/android/nfc/nxp/NativeP2pDevice.java b/src/com/android/nfc/nxp/NativeP2pDevice.java
new file mode 100755
index 0000000..7c7db41
--- /dev/null
+++ b/src/com/android/nfc/nxp/NativeP2pDevice.java
@@ -0,0 +1,77 @@
+/*
+ * 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.nxp;
+
+import com.android.nfc.DeviceHost.NfcDepEndpoint;
+
+/**
+ * Native interface to the P2P Initiator functions
+ */
+public class NativeP2pDevice implements NfcDepEndpoint {
+
+ private int mHandle;
+
+ private int mMode;
+
+ private byte[] mGeneralBytes;
+
+ private native byte[] doReceive();
+ @Override
+ public byte[] receive() {
+ return doReceive();
+ }
+
+ private native boolean doSend(byte[] data);
+ @Override
+ public boolean send(byte[] data) {
+ return doSend(data);
+ }
+
+ private native boolean doConnect();
+ @Override
+ public boolean connect() {
+ return doConnect();
+ }
+
+ private native boolean doDisconnect();
+ @Override
+ public boolean disconnect() {
+ return doDisconnect();
+ }
+
+ public native byte[] doTransceive(byte[] data);
+ @Override
+ public byte[] transceive(byte[] data) {
+ return doTransceive(data);
+ }
+
+ @Override
+ public int getHandle() {
+ return mHandle;
+ }
+
+ @Override
+ public int getMode() {
+ return mMode;
+ }
+
+ @Override
+ public byte[] getGeneralBytes() {
+ return mGeneralBytes;
+ }
+
+}