diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/com/android/nfc/NfcService.java | 4 | ||||
-rwxr-xr-x | src/com/android/nfc/nxp/NativeLlcpConnectionlessSocket.java | 78 | ||||
-rwxr-xr-x | src/com/android/nfc/nxp/NativeLlcpServiceSocket.java | 53 | ||||
-rwxr-xr-x | src/com/android/nfc/nxp/NativeLlcpSocket.java | 99 | ||||
-rwxr-xr-x | src/com/android/nfc/nxp/NativeNfcManager.java | 373 | ||||
-rwxr-xr-x | src/com/android/nfc/nxp/NativeNfcSecureElement.java | 67 | ||||
-rwxr-xr-x | src/com/android/nfc/nxp/NativeNfcTag.java | 803 | ||||
-rwxr-xr-x | src/com/android/nfc/nxp/NativeP2pDevice.java | 77 |
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; + } + +} |