summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuchi Kandoi <kandoiruchi@google.com>2017-07-17 11:37:57 -0700
committerandroid-build-team Robot <android-build-team-robot@google.com>2017-07-21 22:17:56 +0000
commitcc0d7a87f7f9fb3556a5903074b0dcaf6bb90ba2 (patch)
treecc745ae50b9a2610c0eb0a25123752d4ec48c660
parent7e468a9b4b50be34f2ddba8d71571622cb38a4ff (diff)
downloadandroid_packages_apps_Nfc-cc0d7a87f7f9fb3556a5903074b0dcaf6bb90ba2.tar.gz
android_packages_apps_Nfc-cc0d7a87f7f9fb3556a5903074b0dcaf6bb90ba2.tar.bz2
android_packages_apps_Nfc-cc0d7a87f7f9fb3556a5903074b0dcaf6bb90ba2.zip
DTA support and extensions added.
Added SNEP server extension required by NFC Forum test cases. Added support to configure the MW as per specifc configuration required by NFC Forum test cases. Bug: 63903843 Test: compiles. Change-Id: I4df165b20388d87c25a07c8048eff7997fd21070 (cherry picked from commit f939e8a61d3904ab30ec59e2762f5c543ebaa672)
-rwxr-xr-xnci/jni/NativeNfcManager.cpp25
-rwxr-xr-xnci/src/com/android/nfc/dhimpl/NativeNfcManager.java15
-rw-r--r--src/com/android/nfc/DeviceHost.java4
-rw-r--r--src/com/android/nfc/DtaServiceConnector.java100
-rwxr-xr-xsrc/com/android/nfc/NfcService.java84
-rwxr-xr-xsrc/com/android/nfc/P2pLinkManager.java137
-rw-r--r--src/com/android/nfc/snep/SnepMessage.java74
-rw-r--r--src/com/android/nfc/snep/SnepMessenger.java98
-rw-r--r--src/com/android/nfc/snep/SnepServer.java4
-rw-r--r--src/com/android/nfc/sneptest/DtaSnepClient.java335
-rw-r--r--src/com/android/nfc/sneptest/ExtDtaSnepServer.java259
11 files changed, 1127 insertions, 8 deletions
diff --git a/nci/jni/NativeNfcManager.cpp b/nci/jni/NativeNfcManager.cpp
index 6d7d8114..6d35982b 100755
--- a/nci/jni/NativeNfcManager.cpp
+++ b/nci/jni/NativeNfcManager.cpp
@@ -152,6 +152,7 @@ static uint16_t sCurrentConfigLen;
static uint8_t sConfig[256];
static int prevScreenState = NFA_SCREEN_STATE_OFF_LOCKED;
static int NFA_SCREEN_POLLING_TAG_MASK = 0x10;
+static bool gIsDtaEnabled = false;
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
@@ -980,6 +981,17 @@ static jboolean nfcManager_doInitialize (JNIEnv* e, jobject o)
/////////////////////////////////////////////////////////////////////////////////
// Add extra configuration here (work-arounds, etc.)
+ if (gIsDtaEnabled == true)
+ {
+ uint8_t configData = 0;
+ configData = 0x01; /* Poll NFC-DEP : Highest Available Bit Rates */
+ NFA_SetConfig(NFC_PMID_BITR_NFC_DEP, sizeof(uint8_t), &configData);
+ configData = 0x0B; /* Listen NFC-DEP : Waiting Time */
+ NFA_SetConfig(NFC_PMID_WT, sizeof(uint8_t), &configData);
+ configData = 0x0F; /* Specific Parameters for NFC-DEP RF Interface */
+ NFA_SetConfig(NFC_PMID_NFC_DEP_OP, sizeof(uint8_t), &configData);
+ }
+
struct nfc_jni_native_data *nat = getNative(e, o);
if ( nat )
@@ -1040,7 +1052,15 @@ TheEnd:
return sIsNfaEnabled ? JNI_TRUE : JNI_FALSE;
}
+static void nfcManager_doEnableDtaMode (JNIEnv*, jobject)
+{
+ gIsDtaEnabled = true;
+}
+static void nfcManager_doDisableDtaMode(JNIEnv*, jobject)
+{
+ gIsDtaEnabled = false;
+}
/*******************************************************************************
**
** Function: nfcManager_enableDiscovery
@@ -1893,6 +1913,11 @@ static JNINativeMethod gMethods[] =
{"getNciVersion","()I",
(void *)nfcManager_doGetNciVersion},
+ {"doEnableDtaMode", "()V",
+ (void*) nfcManager_doEnableDtaMode},
+ {"doDisableDtaMode", "()V",
+ (void*) nfcManager_doDisableDtaMode}
+
};
diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
index 777ff93a..488801ff 100755
--- a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
+++ b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
@@ -80,6 +80,21 @@ public class NativeNfcManager implements DeviceHost {
return doInitialize();
}
+ private native void doEnableDtaMode();
+
+ @Override
+ public void enableDtaMode() {
+ doEnableDtaMode();
+ }
+
+ private native void doDisableDtaMode();
+
+ @Override
+ public void disableDtaMode() {
+ Log.d(TAG,"disableDtaMode : entry");
+ doDisableDtaMode();
+ }
+
private native boolean doDeinitialize();
@Override
diff --git a/src/com/android/nfc/DeviceHost.java b/src/com/android/nfc/DeviceHost.java
index db41c736..7fb08337 100644
--- a/src/com/android/nfc/DeviceHost.java
+++ b/src/com/android/nfc/DeviceHost.java
@@ -241,4 +241,8 @@ public interface DeviceHost {
public void doSetScreenState(int screen_state_mask);
public int getNciVersion();
+
+ public void enableDtaMode();
+
+ public void disableDtaMode();
}
diff --git a/src/com/android/nfc/DtaServiceConnector.java b/src/com/android/nfc/DtaServiceConnector.java
new file mode 100644
index 00000000..147084b7
--- /dev/null
+++ b/src/com/android/nfc/DtaServiceConnector.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 NXP Semiconductors
+ *
+ * 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;
+
+import java.util.List;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+
+public class DtaServiceConnector {
+
+
+ private static String sMessageService;
+ Context mContext;
+ Messenger dtaMessenger = null;
+ boolean isBound;
+
+
+ public DtaServiceConnector(Context mContext) {
+ this.mContext = mContext;
+ }
+
+ public void bindService() {
+ if (!isBound) {
+ Intent intent = new Intent(sMessageService);
+ mContext.bindService(createExplicitFromImplicitIntent(mContext,intent),
+ myConnection,Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ private ServiceConnection myConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ dtaMessenger = new Messenger(service);
+ isBound = true;
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ dtaMessenger = null;
+ isBound = false;
+ }
+ };
+
+ public void sendMessage(String ndefMessage) {
+ if (!isBound) return;
+ Message msg = Message.obtain();
+ Bundle bundle = new Bundle();
+ bundle.putString("NDEF_MESSAGE", ndefMessage);
+ msg.setData(bundle);
+ try {
+ dtaMessenger.send(msg);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
+ PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
+ if (resolveInfo == null || resolveInfo.size() != 1) {
+ return null;
+ }
+ ResolveInfo serviceInfo = resolveInfo.get(0);
+ String packageName = serviceInfo.serviceInfo.packageName;
+ String className = serviceInfo.serviceInfo.name;
+ ComponentName component = new ComponentName(packageName, className);
+ Intent explicitIntent = new Intent(implicitIntent);
+ explicitIntent.setComponent(component);
+ return explicitIntent;
+ }
+
+ public static void setMessageService(String service) {
+ sMessageService = service;
+ }
+
+}
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index 7b61686f..87c75ac6 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -43,6 +43,7 @@ import android.nfc.IAppCallback;
import android.nfc.INfcAdapter;
import android.nfc.INfcAdapterExtras;
import android.nfc.INfcCardEmulation;
+import android.nfc.INfcDta;
import android.nfc.INfcFCardEmulation;
import android.nfc.INfcTag;
import android.nfc.INfcUnlockHandler;
@@ -174,6 +175,8 @@ public class NfcService implements DeviceHostListener {
public static final String ACTION_RF_FIELD_OFF_DETECTED =
"com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED";
+ public static boolean sIsShortRecordLayout = false;
+
// for use with playSound()
public static final int SOUND_START = 0;
public static final int SOUND_END = 1;
@@ -250,6 +253,7 @@ public class NfcService implements DeviceHostListener {
P2pLinkManager mP2pLinkManager;
TagService mNfcTagService;
NfcAdapterService mNfcAdapter;
+ NfcDtaService mNfcDtaService;
boolean mIsDebugBuild;
boolean mIsHceCapable;
boolean mIsHceFCapable;
@@ -265,6 +269,7 @@ public class NfcService implements DeviceHostListener {
private ForegroundUtils mForegroundUtils;
private static NfcService sService;
+ public static boolean sIsDtaMode = false;
boolean mIsLiveCaseEnabled; // whether live cases are enabled
int mLiveCaseTechnology; // Technology mask of accepted NFC tags
@@ -627,6 +632,7 @@ public class NfcService implements DeviceHostListener {
}
nci_version = getNciVersion();
+ Log.d(TAG, "NCI_Version: " + nci_version);
synchronized (NfcService.this) {
mObjectMap.clear();
@@ -1106,6 +1112,15 @@ public class NfcService implements DeviceHostListener {
}
@Override
+ public INfcDta getNfcDtaInterface(String pkg) throws RemoteException {
+ NfcPermissions.enforceAdminPermissions(mContext);
+ if (mNfcDtaService == null) {
+ mNfcDtaService = new NfcDtaService();
+ }
+ return mNfcDtaService;
+ }
+
+ @Override
public void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, int[] techList) {
NfcPermissions.enforceAdminPermissions(mContext);
@@ -1487,6 +1502,75 @@ public class NfcService implements DeviceHostListener {
}
}
+ final class NfcDtaService extends INfcDta.Stub {
+ public void enableDta() throws RemoteException {
+ NfcPermissions.enforceAdminPermissions(mContext);
+ if(!sIsDtaMode) {
+ mDeviceHost.enableDtaMode();
+ sIsDtaMode = true;
+ Log.d(TAG, "DTA Mode is Enabled ");
+ }
+ }
+
+ public void disableDta() throws RemoteException {
+ NfcPermissions.enforceAdminPermissions(mContext);
+ if(sIsDtaMode) {
+ mDeviceHost.disableDtaMode();
+ sIsDtaMode = false;
+ }
+ }
+
+ public boolean enableServer(String serviceName, int serviceSap, int miu,
+ int rwSize,int testCaseId) throws RemoteException {
+ NfcPermissions.enforceAdminPermissions(mContext);
+
+ if(serviceName.equals(null))
+ return false;
+
+ mP2pLinkManager.enableExtDtaSnepServer(serviceName, serviceSap, miu, rwSize,testCaseId);
+ return true;
+ }
+
+ public void disableServer() throws RemoteException {
+ NfcPermissions.enforceAdminPermissions(mContext);
+ mP2pLinkManager.disableExtDtaSnepServer();
+ }
+
+ public boolean enableClient(String serviceName, int miu, int rwSize,
+ int testCaseId) throws RemoteException {
+ NfcPermissions.enforceAdminPermissions(mContext);
+
+ if(testCaseId == 0)
+ return false;
+
+ if (testCaseId>20){
+ sIsShortRecordLayout=true;
+ testCaseId=testCaseId-20;
+ } else {
+ sIsShortRecordLayout=false;
+ }
+ Log.d("testCaseId", ""+testCaseId);
+ mP2pLinkManager.enableDtaSnepClient(serviceName, miu, rwSize, testCaseId);
+ return true;
+ }
+
+ public void disableClient() throws RemoteException {
+ NfcPermissions.enforceAdminPermissions(mContext);
+ mP2pLinkManager.disableDtaSnepClient();
+ }
+
+ public boolean registerMessageService(String msgServiceName)
+ throws RemoteException {
+ NfcPermissions.enforceAdminPermissions(mContext);
+ if(msgServiceName.equals(null))
+ return false;
+
+ DtaServiceConnector.setMessageService(msgServiceName);
+ return true;
+ }
+
+ };
+
boolean isNfcEnabledOrShuttingDown() {
synchronized (this) {
return (mState == NfcAdapter.STATE_ON || mState == NfcAdapter.STATE_TURNING_OFF);
diff --git a/src/com/android/nfc/P2pLinkManager.java b/src/com/android/nfc/P2pLinkManager.java
index 58779599..3dd601b2 100755
--- a/src/com/android/nfc/P2pLinkManager.java
+++ b/src/com/android/nfc/P2pLinkManager.java
@@ -24,6 +24,8 @@ import com.android.nfc.beam.BeamSendService;
import com.android.nfc.beam.BeamTransferRecord;
import android.os.UserManager;
+import com.android.nfc.sneptest.ExtDtaSnepServer;
+import com.android.nfc.sneptest.DtaSnepClient;
import com.android.nfc.echoserver.EchoServer;
import com.android.nfc.handover.HandoverClient;
import com.android.nfc.handover.HandoverDataParser;
@@ -57,6 +59,7 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
+import java.io.UnsupportedEncodingException;
/**
* Interface to listen for P2P events.
@@ -241,6 +244,18 @@ class P2pLinkManager implements Handler.Callback, P2pEventListener.Callback {
boolean mLlcpServicesConnected;
long mLastLlcpActivationTime;
byte mPeerLlcpVersion;
+ // for DTA Mode
+ private int mDtaMiu;
+ private int mDtaRwSize;
+ private int mServiceSap;
+ private int mTestCaseID;
+ private String mServiceName;
+ private ExtDtaSnepServer mExtDtaSnepServer = null;
+ private DtaSnepClient mDtaSnepClient = null;
+ private boolean mClientEnabled = false;
+ private boolean mServerEnabled = false;
+ private boolean mExtDtaSnepServerRunning = false;
+ private boolean mPutBeforeGet = false;
public P2pLinkManager(Context context, HandoverDataParser handoverDataParser, int defaultMiu,
int defaultRwSize) {
@@ -292,6 +307,8 @@ class P2pLinkManager implements Handler.Callback, P2pEventListener.Callback {
if (mEchoServer != null) {
mHandler.sendEmptyMessage(MSG_STOP_ECHOSERVER);
}
+ if (mExtDtaSnepServerRunning)
+ disableExtDtaSnepServer();
}
mIsSendEnabled = sendEnable;
mIsReceiveEnabled = receiveEnable;
@@ -299,6 +316,64 @@ class P2pLinkManager implements Handler.Callback, P2pEventListener.Callback {
}
/**
+ * To Enable DTA SNEP Server for NFC Forum testing
+ */
+ public void enableExtDtaSnepServer(String serviceName, int serviceSap, int miu, int rwSize,int testCaseId) {
+ if (DBG) Log.d(TAG, "Enabling Extended DTA Server");
+ mServiceName = serviceName;
+ mServiceSap = serviceSap;
+ mDtaMiu = miu;
+ mDtaRwSize = rwSize;
+ mTestCaseID = testCaseId;
+ synchronized (this) {
+ if(mExtDtaSnepServer == null)
+ mExtDtaSnepServer = new ExtDtaSnepServer(mServiceName, mServiceSap, mDtaMiu, mDtaRwSize,
+ mExtDtaSnepServerCallback,mContext, mTestCaseID);
+ mExtDtaSnepServer.start();
+ mExtDtaSnepServerRunning = true;
+ }
+ mServerEnabled = true;
+ }
+
+ /**
+ * To Disable DTA SNEP Server for NFC Forum testing
+ */
+ public void disableExtDtaSnepServer() {
+ if (DBG) Log.d(TAG, "Disabling Extended DTA Server");
+ if (!mExtDtaSnepServerRunning)
+ return;
+ synchronized (this) {
+ mExtDtaSnepServer.stop();
+ mExtDtaSnepServer = null;
+ mExtDtaSnepServerRunning = false;
+ }
+ mServerEnabled = false;
+ }
+
+ /**
+ * To Enable DTA SNEP Client for NFC Forum testing
+ */
+ public void enableDtaSnepClient(String serviceName, int miu, int rwSize, int testCaseId) {
+ if (DBG) Log.d(TAG, "enableDtaSnepClient");
+ mClientEnabled = true;
+ mServiceName = serviceName;
+ mServiceSap = -1;
+ mDtaMiu = miu;
+ mDtaRwSize = rwSize;
+ mTestCaseID = testCaseId;
+ }
+
+ /**
+ * To Disable DTA SNEP Client for NFC Forum testing
+ */
+ public void disableDtaSnepClient() {
+ if (DBG) Log.d(TAG, "disableDtaSnepClient");
+ mDtaSnepClient = null;
+ mClientEnabled = false;
+ }
+
+
+ /**
* May be called from any thread.
* @return whether the LLCP link is in an active or debounce state
*/
@@ -691,12 +766,25 @@ class P2pLinkManager implements Handler.Callback, P2pEventListener.Callback {
handoverClient = null;
}
}
-
if (needsNdef || (needsHandover && handoverClient == null)) {
- snepClient = new SnepClient();
+ if (NfcService.sIsDtaMode) {
+ if (mClientEnabled) {
+ if (mDtaSnepClient == null) {
+ if (DBG) Log.d(TAG, "Creating DTA Snep Client");
+ mDtaSnepClient = new DtaSnepClient(mServiceName, mDtaMiu, mDtaRwSize, mTestCaseID);
+ }
+ }
+ } else
+ snepClient = new SnepClient();
try {
- snepClient.connect();
+ if (NfcService.sIsDtaMode) {
+ if (mDtaSnepClient != null)
+ mDtaSnepClient.DtaClientOperations(mContext);
+ }
+ else
+ snepClient.connect();
success = true;
+ mDtaSnepClient = null;
} catch (IOException e) {
snepClient = null;
}
@@ -724,6 +812,9 @@ class P2pLinkManager implements Handler.Callback, P2pEventListener.Callback {
if (nppClient != null) {
nppClient.close();
}
+ if (mDtaSnepClient != null) {
+ mDtaSnepClient.close();
+ }
return false;
} else {
// Once assigned, these are the responsibility of
@@ -892,6 +983,9 @@ class P2pLinkManager implements Handler.Callback, P2pEventListener.Callback {
final SnepServer.Callback mDefaultSnepCallback = new SnepServer.Callback() {
@Override
public SnepMessage doPut(NdefMessage msg) {
+ if(NfcService.sIsDtaMode)
+ Log.d(TAG, "DTA mode enabled, dont dispatch the tag");
+ else
onReceiveComplete(msg);
return SnepMessage.getMessage(SnepMessage.RESPONSE_SUCCESS);
}
@@ -903,7 +997,14 @@ class P2pLinkManager implements Handler.Callback, P2pEventListener.Callback {
// since Android 4.1 used the NFC Forum default server to
// implement connection handover, we will support this
// until we can deprecate it.
- NdefMessage response = mHandoverDataParser.getIncomingHandoverData(msg).handoverSelect;
+ NdefMessage response = null;
+ if (NfcService.sIsDtaMode){
+ if(msg != null && mHandoverDataParser.getIncomingHandoverData(msg) != null) {
+ response = mHandoverDataParser.getIncomingHandoverData(msg).handoverSelect;
+ }
+ } else {
+ response = mHandoverDataParser.getIncomingHandoverData(msg).handoverSelect;
+ }
if (response != null) {
onReceiveHandover();
return SnepMessage.getSuccessResponse(response);
@@ -912,6 +1013,34 @@ class P2pLinkManager implements Handler.Callback, P2pEventListener.Callback {
}
}
};
+ final ExtDtaSnepServer.Callback mExtDtaSnepServerCallback = new ExtDtaSnepServer.Callback() {
+ @Override
+ public SnepMessage doPut(NdefMessage msg) {
+ mPutBeforeGet = true;
+ return SnepMessage.getMessage(SnepMessage.RESPONSE_SUCCESS);
+ }
+
+ @Override
+ public SnepMessage doGet(int acceptableLength, NdefMessage msg) {
+ if ((!mPutBeforeGet)) {
+ return SnepMessage.getMessage(SnepMessage.RESPONSE_NOT_FOUND);
+ } else if (acceptableLength == 501) {
+ mPutBeforeGet = false;
+ return SnepMessage.getMessage(SnepMessage.RESPONSE_EXCESS_DATA);
+ } else if (mPutBeforeGet&&(acceptableLength == 1024)) {
+ try {
+ mPutBeforeGet = false;
+ return SnepMessage.getSuccessResponse(SnepMessage.getLargeNdef());
+ } catch (UnsupportedEncodingException e) {
+ mPutBeforeGet = false;
+ return null;
+ }
+ } else {
+ mPutBeforeGet = false;
+ return SnepMessage.getMessage(SnepMessage.RESPONSE_NOT_IMPLEMENTED);
+ }
+ }
+ };
void onReceiveHandover() {
mHandler.obtainMessage(MSG_RECEIVE_HANDOVER).sendToTarget();
diff --git a/src/com/android/nfc/snep/SnepMessage.java b/src/com/android/nfc/snep/SnepMessage.java
index 50996d00..4c3ad995 100644
--- a/src/com/android/nfc/snep/SnepMessage.java
+++ b/src/com/android/nfc/snep/SnepMessage.java
@@ -18,10 +18,14 @@ package com.android.nfc.snep;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import com.android.nfc.NfcService;
+import com.android.nfc.sneptest.DtaSnepClient;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
public final class SnepMessage {
@@ -32,6 +36,7 @@ public final class SnepMessage {
public static final byte REQUEST_CONTINUE = (byte) 0x00;
public static final byte REQUEST_GET = (byte) 0x01;
public static final byte REQUEST_PUT = (byte) 0x02;
+ public static final byte REQUEST_RFU = (byte) 0x03;
public static final byte REQUEST_REJECT = (byte) 0x7F;
public static final byte RESPONSE_CONTINUE = (byte) 0x80;
@@ -43,7 +48,19 @@ public final class SnepMessage {
public static final byte RESPONSE_UNSUPPORTED_VERSION = (byte) 0xE1;
public static final byte RESPONSE_REJECT = (byte) 0xFF;
+ private static final byte[] NDEF_SHORT_TEST_RECORD = new byte[]{(byte)0xD1,(byte)0x01,(byte)0x1E,(byte)0x54,(byte)0x02,(byte)0x6C,(byte)0x61, // NDEF Header
+ (byte)0x4C,(byte)0x6F,(byte)0x72,(byte)0x65,(byte)0x6D,(byte)0x20,(byte)0x69,(byte)0x70,(byte)0x73,(byte)0x75, // Payload
+ (byte)0x6D,(byte)0x20,(byte)0x64,(byte)0x6F,(byte)0x6C,(byte)0x6F,(byte)0x72,(byte)0x20,(byte)0x73,(byte)0x69,
+ (byte)0x74,(byte)0x20,(byte)0x61,(byte)0x6D,(byte)0x65,(byte)0x74,(byte)0x2E};
+
+ private static final byte[] NDEF_TEST_RECORD = new byte[]{(byte)0xC1,(byte)0x01,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x1E,(byte)0x54,(byte)0x02,(byte)0x6C,(byte)0x61, // NDEF Header
+ (byte)0x4C,(byte)0x6F,(byte)0x72,(byte)0x65,(byte)0x6D,(byte)0x20,(byte)0x69,(byte)0x70,(byte)0x73,(byte)0x75, // Payload
+ (byte)0x6D,(byte)0x20,(byte)0x64,(byte)0x6F,(byte)0x6C,(byte)0x6F,(byte)0x72,(byte)0x20,(byte)0x73,(byte)0x69,
+ (byte)0x74,(byte)0x20,(byte)0x61,(byte)0x6D,(byte)0x65,(byte)0x74,(byte)0x2E};
+
private static final int HEADER_LENGTH = 6;
+ public static final int MAL_IUT = 0x0400;
+ public static final int MAL = 0xFFFFFFFF;
private final byte mVersion;
private final byte mField;
private final int mLength;
@@ -75,6 +92,49 @@ public final class SnepMessage {
return new SnepMessage(data);
}
+ public static NdefMessage getLargeNdef() throws UnsupportedEncodingException {
+ String snepTestData2 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at"
+ +" lorem nunc, ut venenatis quam. Etiam id dolor quam, at viverra dolor."
+ +" Phasellus eu lacus ligula, quis euismod erat. Sed feugiat, ligula at"
+ +" mollis aliquet, justo lacus condimentum eros, non tincidunt neque"
+ +" ipsum eu risus. Sed adipiscing dui euismod tellus ullamcorper ornare."
+ +" Phasellus mattis risus et lectus euismod eu fermentum sem cursus."
+ +" Phasellus tristique consectetur mauris eu porttitor. Sed lobortis"
+ +" porttitor orci.";
+ String lang = "la";
+ byte[] textBytes = snepTestData2.getBytes();
+ byte[] langBytes = lang.getBytes("US-ASCII");
+ int langLength = langBytes.length;
+ int textLength = textBytes.length;
+
+ byte[] payload = new byte[1 + langLength + textLength];
+ payload[0] = (byte) langLength;
+
+ System.arraycopy(langBytes, 0, payload, 1, langLength);
+ System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength);
+
+ NdefRecord data2 = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload);
+ return new NdefMessage(new NdefRecord[]{data2});
+ }
+
+ public static NdefMessage getSmallNdef() throws UnsupportedEncodingException {
+ String snepTestData1 = "Lorem ipsum dolor sit amet.";
+ String lang = "la";
+ byte[] textBytes = snepTestData1.getBytes();
+ byte[] langBytes = lang.getBytes("US-ASCII");
+ int langLength = langBytes.length;
+ int textLength = textBytes.length;
+
+ byte[] payload = new byte[1 + langLength + textLength];
+ payload[0] = (byte) langLength;
+
+ System.arraycopy(langBytes, 0, payload, 1, langLength);
+ System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength);
+
+ NdefRecord data1 = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload);
+ return new NdefMessage(new NdefRecord[]{data1});
+ }
+
private SnepMessage(byte[] data) throws FormatException {
ByteBuffer input = ByteBuffer.wrap(data);
int ndefOffset;
@@ -114,7 +174,19 @@ public final class SnepMessage {
public byte[] toByteArray() {
byte[] bytes;
if (mNdefMessage != null) {
- bytes = mNdefMessage.toByteArray();
+ if (NfcService.sIsDtaMode && DtaSnepClient.mTestCaseId != 0) {
+ if (DtaSnepClient.mTestCaseId == 5 || DtaSnepClient.mTestCaseId == 6) {
+ bytes = mNdefMessage.toByteArray();
+ } else {
+ if (NfcService.sIsShortRecordLayout) {
+ bytes = NDEF_SHORT_TEST_RECORD;
+ } else {
+ bytes = NDEF_TEST_RECORD;
+ }
+ }
+ } else {
+ bytes = mNdefMessage.toByteArray();
+ }
} else {
bytes = new byte[0];
}
diff --git a/src/com/android/nfc/snep/SnepMessenger.java b/src/com/android/nfc/snep/SnepMessenger.java
index 97ac1f0e..5078fe8c 100644
--- a/src/com/android/nfc/snep/SnepMessenger.java
+++ b/src/com/android/nfc/snep/SnepMessenger.java
@@ -17,6 +17,9 @@
package com.android.nfc.snep;
import com.android.nfc.DeviceHost.LlcpSocket;
+import com.android.nfc.NfcService;
+import com.android.nfc.sneptest.DtaSnepClient;
+import com.android.nfc.sneptest.ExtDtaSnepServer;
import android.nfc.FormatException;
import android.util.Log;
@@ -77,6 +80,29 @@ public class SnepMessenger {
throw new IOException("Invalid response from server (" +
snepResponse.getField() + ")");
}
+ // Look for wrong/invalid request or response from peer
+ if (NfcService.sIsDtaMode) {
+ if (mIsClient && (DtaSnepClient.mTestCaseId == 6)) {
+ length = Math.min(buffer.length - offset, mFragmentLength);
+ tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
+ if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
+ mSocket.send(tmpBuffer);
+ offset += length;
+
+ mSocket.receive(responseBytes);
+
+ try {
+ snepResponse = SnepMessage.fromByteArray(responseBytes);
+ } catch (FormatException e) {
+ throw new IOException("Invalid SNEP message", e);
+ }
+ if (DBG) Log.d(TAG, "Got response from second fragment: " + snepResponse.getField());
+ if (snepResponse.getField() == remoteContinue) {
+ close();
+ return;
+ }
+ }
+ }
// Send remaining fragments.
while (offset < buffer.length) {
@@ -84,6 +110,23 @@ public class SnepMessenger {
tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
mSocket.send(tmpBuffer);
+
+ if (NfcService.sIsDtaMode) {
+ if (!mIsClient && ExtDtaSnepServer.mTestCaseId == 0x01) {
+ mSocket.receive(responseBytes);
+ try {
+ snepResponse = SnepMessage.fromByteArray(responseBytes);
+ } catch (FormatException e) {
+ throw new IOException("Invalid SNEP message", e);
+ }
+ if (DBG) Log.d(TAG, "Got continue response after second fragment: and now disconnecting..." + snepResponse.getField());
+ if (snepResponse.getField() == remoteContinue) {
+ close();
+ return;
+ }
+ }
+ }
+
offset += length;
}
}
@@ -95,6 +138,7 @@ public class SnepMessenger {
int requestSize = 0;
int readSize = 0;
byte requestVersion = 0;
+ byte requestField = 0; // for DTA Mode
boolean doneReading = false;
byte fieldContinue;
byte fieldReject;
@@ -117,6 +161,13 @@ public class SnepMessenger {
throw new IOException("Error reading SNEP message.");
} else if (size < HEADER_LENGTH) {
try {
+ if (NfcService.sIsDtaMode && mIsClient) {
+ if (DBG) Log.d(TAG, "Invalid header length");
+ close();
+ } else {
+ mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
+
+ }
mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
} catch (IOException e) {
// Ignore
@@ -129,14 +180,55 @@ public class SnepMessenger {
DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial));
requestVersion = dataIn.readByte();
- byte requestField = dataIn.readByte();
+ requestField = dataIn.readByte();
requestSize = dataIn.readInt();
if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize);
if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
- // Invalid protocol version; treat message as complete.
- return new SnepMessage(requestVersion, requestField, 0, 0, null);
+ if (NfcService.sIsDtaMode) {
+ sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
+ close();
+ } else {
+ if (NfcService.sIsDtaMode) {
+ sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
+ close();
+ } else {
+ // Invalid protocol version; treat message as complete.
+ return new SnepMessage(requestVersion, requestField, 0, 0, null);
+ }
+ }
+
+ }
+
+ if (NfcService.sIsDtaMode) {
+ if (!mIsClient && (requestField == SnepMessage.RESPONSE_CONTINUE)|| // added for TC_S_BIT_B1_01_X
+ requestField == SnepMessage.RESPONSE_SUCCESS ||
+ requestField == SnepMessage.RESPONSE_NOT_FOUND) {
+ if (DBG) Log.d(TAG, "errorneous response received, disconnecting client");
+ close();
+ }
+ if (!mIsClient && requestField == SnepMessage.REQUEST_RFU) {
+ if (DBG) Log.d(TAG, "unknown request received, disconnecting client");
+ sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_BAD_REQUEST));
+ close();
+ }
+ // added for TC_C_BIT_BI_01_0
+ if (mIsClient && requestField == SnepMessage.REQUEST_PUT) {
+ if (DBG) Log.d(TAG, "errorneous PUT request received, disconnecting from server");
+ close();
+ }
+ // added for TC_C_GET_BV_03
+ if (mIsClient && (requestSize > SnepMessage.MAL_IUT)) {
+ if (DBG) Log.d(TAG, "responding reject");
+ return new SnepMessage(requestVersion, requestField, requestSize, 0, null);
+ }
+ //added for TC_S_ACC_BV_05_0&1 and TC_S_ACC_BV_06_0&1
+ if (!mIsClient && ((requestSize > SnepMessage.MAL_IUT) ||
+ requestSize == SnepMessage.MAL)) {
+ if (DBG) Log.d(TAG, "responding reject");
+ return new SnepMessage(requestVersion, requestField, requestSize, 0, null);
+ }
}
if (requestSize > readSize) {
diff --git a/src/com/android/nfc/snep/SnepServer.java b/src/com/android/nfc/snep/SnepServer.java
index 866ed32c..f2e9c0cc 100644
--- a/src/com/android/nfc/snep/SnepServer.java
+++ b/src/com/android/nfc/snep/SnepServer.java
@@ -155,6 +155,10 @@ public final class SnepServer {
if (((request.getVersion() & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
messenger.sendMessage(SnepMessage.getMessage(
SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
+ } else if (NfcService.sIsDtaMode && ((request.getLength() > SnepMessage.MAL_IUT) ||
+ request.getLength() == SnepMessage.MAL)) {
+ if (DBG) Log.d(TAG, "Bad requested length");
+ messenger.sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_REJECT));
} else if (request.getField() == SnepMessage.REQUEST_GET) {
messenger.sendMessage(callback.doGet(request.getAcceptableLength(),
request.getNdefMessage()));
diff --git a/src/com/android/nfc/sneptest/DtaSnepClient.java b/src/com/android/nfc/sneptest/DtaSnepClient.java
new file mode 100644
index 00000000..6087b6e7
--- /dev/null
+++ b/src/com/android/nfc/sneptest/DtaSnepClient.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2017 NXP Semiconductors
+ *
+ * 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.sneptest;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.nfc.NdefMessage;
+import android.os.IBinder;
+import android.os.Messenger;
+import android.util.Log;
+
+import com.android.nfc.DeviceHost.LlcpSocket;
+import com.android.nfc.LlcpException;
+import com.android.nfc.NfcService;
+import com.android.nfc.DtaServiceConnector;
+import com.android.nfc.snep.SnepException;
+import com.android.nfc.snep.SnepMessage;
+import com.android.nfc.snep.SnepMessenger;
+
+public final class DtaSnepClient {
+ private static final String TAG = "DtaSnepClient";
+ private static final boolean DBG = true;
+ private static final int DEFAULT_ACCEPTABLE_LENGTH = 1024;
+ private static final int DEFAULT_MIU = 128;
+ private static final int DEFAULT_RWSIZE = 1;
+ private static final int DEFAULT_PORT = 63;
+ private static final String SNEP_SERVICE_NAME = "urn:nfc:sn:snep";
+ private static final String DEFAULT_SERVICE_NAME = SNEP_SERVICE_NAME;
+ private final Object mTransmissionLock = new Object();
+
+ private int mState = DISCONNECTED;
+ private final int mAcceptableLength;
+ private final int mFragmentLength;
+ private final int mMiu;
+ private final int mPort;
+ private final int mRwSize;
+ private final String mServiceName;
+ public static int mTestCaseId;
+
+ private static final int DISCONNECTED = 0;
+ private static final int CONNECTING = 1;
+ private static final int CONNECTED = 2;
+
+ SnepMessenger mMessenger = null;
+
+ public DtaSnepClient() {
+ mServiceName = DEFAULT_SERVICE_NAME;
+ mPort = DEFAULT_PORT;
+ mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
+ mFragmentLength = -1;
+ mMiu = DEFAULT_MIU;
+ mRwSize = DEFAULT_RWSIZE;
+ }
+
+ public DtaSnepClient(String serviceName, int miu, int rwSize, int testCaseId) {
+ mServiceName = serviceName;
+ mPort = -1;
+ mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
+ mFragmentLength = -1;
+ mMiu = miu;
+ mRwSize = rwSize;
+ mTestCaseId = testCaseId;
+ }
+
+ public void DtaClientOperations(Context mContext) {
+ DtaServiceConnector dtaServiceConnector=new DtaServiceConnector(mContext);
+ dtaServiceConnector.bindService();
+ if (DBG) Log.d(TAG, "Connecting remote server");
+ try {
+ connect();
+ } catch(IOException e) {
+ Log.e(TAG, "Error connecting remote server");
+ }
+ switch(mTestCaseId) {
+ //TC_C_BIT_BV_01
+ case 1:
+ {
+ try {
+ if (DBG) Log.d(TAG, "PUT Small Ndef Data");
+ put(SnepMessage.getSmallNdef());
+ dtaServiceConnector.sendMessage(SnepMessage.getSmallNdef().toString());
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ close();
+ }
+ break;
+ //TC_C_BIT_BI_01_0
+ case 2:
+ {
+ try {
+ if (DBG) Log.d(TAG, "PUT Small Ndef Data");
+ put(SnepMessage.getSmallNdef());
+ dtaServiceConnector.sendMessage(SnepMessage.getSmallNdef().toString());
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ close();
+ }
+ break;
+ //TC_C_BIT_BI_01_1
+ case 3:
+ {
+ try {
+ if (DBG) Log.d(TAG, "PUT Small Ndef Data");
+ put(SnepMessage.getSmallNdef());
+ dtaServiceConnector.sendMessage(SnepMessage.getSmallNdef().toString());
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ close();
+ }
+ break;
+ //TC_C_PUT_BV_01
+ case 4:
+ {
+ try {
+ if (DBG) Log.d(TAG, "PUT Small Ndef Data");
+ put(SnepMessage.getSmallNdef());
+ dtaServiceConnector.sendMessage(SnepMessage.getSmallNdef().toString());
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ close();
+ }
+ break;
+ //TC_C_PUT_BV_02
+ case 5:
+ {
+ try {
+ if (DBG) Log.d(TAG, "PUT Large Ndef Data");
+ put(SnepMessage.getLargeNdef());
+ dtaServiceConnector.sendMessage(SnepMessage.getLargeNdef().toString());
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ close();
+ }
+ break;
+ //TC_C_PUT_BI_01
+ case 6:
+ {
+ try {
+ if (DBG) Log.d(TAG, "PUT Large Ndef Data");
+ put(SnepMessage.getLargeNdef());
+ dtaServiceConnector.sendMessage(SnepMessage.getLargeNdef().toString());
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ close();
+ }
+ break;
+ //TC_C_GET_BV_01
+ case 7:
+ {
+ try {
+ if (DBG) Log.d(TAG, "GET Ndef Message");
+ get(SnepMessage.getSmallNdef());
+ dtaServiceConnector.sendMessage(SnepMessage.getSmallNdef().toString());
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ close();
+ }
+ break;
+ //TC_C_GET_BV_02
+ case 8:
+ {
+ try {
+ if (DBG) Log.d(TAG, "GET Ndef Message");
+ get(SnepMessage.getSmallNdef());
+ dtaServiceConnector.sendMessage(SnepMessage.getSmallNdef().toString());
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ close();
+ }
+ break;
+ //TC_C_GET_BV_03
+ case 9:
+ {
+ try {
+ if (DBG) Log.d(TAG, "GET Ndef Message");
+ get(SnepMessage.getSmallNdef());
+ dtaServiceConnector.sendMessage(SnepMessage.getSmallNdef().toString());
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ close();
+ }
+ break;
+ default:
+ if (DBG) Log.d(TAG, "Unknown test case");
+ }
+ }
+
+ public void put(NdefMessage msg) throws IOException {
+ SnepMessenger messenger;
+ synchronized (this) {
+ if (mState != CONNECTED) {
+ throw new IOException("Socket not connected.");
+ }
+ messenger = mMessenger;
+ }
+
+ synchronized (mTransmissionLock) {
+ try {
+ messenger.sendMessage(SnepMessage.getPutRequest(msg));
+ messenger.getMessage();
+ } catch (SnepException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+
+ public SnepMessage get(NdefMessage msg) throws IOException {
+ SnepMessenger messenger;
+ synchronized (this) {
+ if (mState != CONNECTED) {
+ throw new IOException("Socket not connected.");
+ }
+ messenger = mMessenger;
+ }
+
+ synchronized (mTransmissionLock) {
+ try {
+ messenger.sendMessage(SnepMessage.getGetRequest(mAcceptableLength, msg));
+ return messenger.getMessage();
+ } catch (SnepException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+
+ public void connect() throws IOException {
+ synchronized (this) {
+ if (mState != DISCONNECTED) {
+ throw new IOException("Socket already in use.");
+ }
+ mState = CONNECTING;
+ }
+
+ LlcpSocket socket = null;
+ SnepMessenger messenger;
+ try {
+ if (DBG) Log.d(TAG, "about to create socket");
+ // Connect to the snep server on the remote side
+ socket = NfcService.getInstance().createLlcpSocket(0, mMiu, mRwSize, 1024);
+ if (socket == null) {
+ throw new IOException("Could not connect to socket.");
+ }
+ if (mPort == -1) {
+ if (DBG) Log.d(TAG, "about to connect to service " + mServiceName);
+ socket.connectToService(mServiceName);
+ } else {
+ if (DBG) Log.d(TAG, "about to connect to port " + mPort);
+ socket.connectToSap(mPort);
+ }
+ int miu = socket.getRemoteMiu();
+ int fragmentLength = (mFragmentLength == -1) ? miu : Math.min(miu, mFragmentLength);
+ messenger = new SnepMessenger(true, socket, fragmentLength);
+ } catch (LlcpException e) {
+ synchronized (this) {
+ mState = DISCONNECTED;
+ }
+ throw new IOException("Could not connect to socket");
+ } catch (IOException e) {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e2) {}
+ }
+ synchronized (this) {
+ mState = DISCONNECTED;
+ }
+ throw new IOException("Failed to connect to socket");
+ }
+
+ synchronized (this) {
+ mMessenger = messenger;
+ mState = CONNECTED;
+ }
+ }
+
+ public void close() {
+ synchronized (this) {
+ if (mMessenger != null) {
+ try {
+ mMessenger.close();
+ } catch (IOException e) {
+ // ignore
+ } finally {
+ mMessenger = null;
+ mState = DISCONNECTED;
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/nfc/sneptest/ExtDtaSnepServer.java b/src/com/android/nfc/sneptest/ExtDtaSnepServer.java
new file mode 100644
index 00000000..a90cc7be
--- /dev/null
+++ b/src/com/android/nfc/sneptest/ExtDtaSnepServer.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2017 NXP Semiconductors
+ *
+ * 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.sneptest;
+
+import java.io.IOException;
+
+import android.content.Context;
+import android.nfc.NdefMessage;
+import android.util.Log;
+
+import com.android.nfc.DtaServiceConnector;
+import com.android.nfc.DeviceHost.LlcpServerSocket;
+import com.android.nfc.DeviceHost.LlcpSocket;
+import com.android.nfc.LlcpException;
+import com.android.nfc.NfcService;
+import com.android.nfc.snep.SnepException;
+import com.android.nfc.snep.SnepMessage;
+import com.android.nfc.snep.SnepMessenger;
+
+public final class ExtDtaSnepServer {
+ private static final String TAG = "ExtDtaSnepServer";
+ private static final boolean DBG = true;
+ public static final int DEFAULT_PORT = 5;
+ public static final String EXTENDED_SNEP_DTA_SERVICE_NAME = "urn:nfc:sn:sneptest";
+ public static final String DEFAULT_SERVICE_NAME = EXTENDED_SNEP_DTA_SERVICE_NAME;
+
+ final Callback mExtDtaSnepServerCallback;
+ final String mDtaServiceName;
+ final int mDtaServiceSap;
+ final int mDtaFragmentLength;
+ final int mDtaMiu;
+ final int mDtaRwSize;
+ public static Context mContext;
+ public static int mTestCaseId;
+
+ /** Protected by 'this', null when stopped, non-null when running */
+ ServerThread mServerThread = null;
+ boolean mServerRunning = false;
+ static DtaServiceConnector dtaServiceConnector;
+
+ public interface Callback {
+ public SnepMessage doPut(NdefMessage msg);
+ public SnepMessage doGet(int acceptableLength, NdefMessage msg);
+ }
+
+ // for NFC Forum SNEP DTA
+ public ExtDtaSnepServer(String serviceName, int serviceSap, int miu, int rwSize,
+ Callback callback,Context mContext,int testCaseId) {
+ mExtDtaSnepServerCallback = callback;
+ mDtaServiceName = serviceName;
+ mDtaServiceSap = serviceSap;
+ mDtaFragmentLength = -1; // to get remote MIU
+ mDtaMiu = miu;
+ mDtaRwSize = rwSize;
+ mTestCaseId = testCaseId;
+ dtaServiceConnector=new DtaServiceConnector(mContext);
+ dtaServiceConnector.bindService();
+ }
+
+ /** Connection class, used to handle incoming connections */
+ private class ConnectionThread extends Thread {
+ private final LlcpSocket mSock;
+ private final SnepMessenger mMessager;
+
+ ConnectionThread(LlcpSocket socket, int fragmentLength) {
+ super(TAG);
+ mSock = socket;
+ mMessager = new SnepMessenger(false, socket, fragmentLength);
+ }
+
+ @Override
+ public void run() {
+ if (DBG) Log.d(TAG, "starting connection thread");
+ try {
+ boolean running;
+ synchronized (ExtDtaSnepServer.this) {
+ running = mServerRunning;
+ }
+
+ while (running) {
+ if (!handleRequest(mMessager, mExtDtaSnepServerCallback))
+ break;
+
+ synchronized (ExtDtaSnepServer.this) {
+ running = mServerRunning;
+ }
+ }
+ } catch (IOException e) {
+ if (DBG) Log.e(TAG, "Closing from IOException");
+ } finally {
+ try {
+ if (DBG) Log.d(TAG, "about to close");
+ mSock.close();
+ } catch (IOException e) {}
+ }
+ if (DBG) Log.d(TAG, "finished connection thread");
+ }
+ }
+
+ static boolean handleRequest(SnepMessenger messenger, Callback callback) throws IOException {
+ SnepMessage request;
+ try {
+ request = messenger.getMessage();
+ } catch (SnepException e) {
+ if (DBG) Log.w(TAG, "Bad snep message", e);
+ try {
+ messenger.sendMessage(SnepMessage.getMessage(
+ SnepMessage.RESPONSE_BAD_REQUEST));
+ } catch (IOException e2) {}
+ return false;
+ }
+
+ if (((request.getVersion() & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
+ messenger.sendMessage(SnepMessage.getMessage(
+ SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
+ } else if ((request.getLength() > SnepMessage.MAL_IUT) || request.getLength() == SnepMessage.MAL) {
+ if (DBG) Log.d(TAG, "Bad requested length");
+ messenger.sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_REJECT));
+ } else if (request.getField() == SnepMessage.REQUEST_GET) {
+ if (DBG) Log.d(TAG, "getting message " + request.toString());
+ messenger.sendMessage(callback.doGet(request.getAcceptableLength(), request.getNdefMessage()));
+ if (request.getNdefMessage() != null)
+ dtaServiceConnector.sendMessage(request.getNdefMessage().toString());
+ } else if (request.getField() == SnepMessage.REQUEST_PUT) {
+ if (DBG) Log.d(TAG, "putting message " + request.toString());
+ messenger.sendMessage(callback.doPut(request.getNdefMessage()));
+ if (request.getNdefMessage() != null)
+ dtaServiceConnector.sendMessage(request.getNdefMessage().toString());
+ } else {
+ if (DBG) Log.d(TAG, "Unknown request (" + request.getField() +")");
+ messenger.sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_BAD_REQUEST));
+ }
+ return true;
+ }
+
+ /** Server class, used to listen for incoming connection request */
+ class ServerThread extends Thread {
+ private boolean mThreadRunning = true;
+ LlcpServerSocket mServerSocket;
+
+ @Override
+ public void run() {
+ boolean threadRunning;
+ synchronized (ExtDtaSnepServer.this) {
+ threadRunning = mThreadRunning;
+ }
+
+ while (threadRunning) {
+ if (DBG) Log.d(TAG, "about create LLCP service socket");
+ try {
+ synchronized (ExtDtaSnepServer.this) {
+ mServerSocket = NfcService.getInstance().createLlcpServerSocket(mDtaServiceSap,
+ mDtaServiceName, mDtaMiu, mDtaRwSize, 1024);
+ }
+ if (mServerSocket == null) {
+ if (DBG) Log.d(TAG, "failed to create LLCP service socket");
+ return;
+ }
+ if (DBG) Log.d(TAG, "created LLCP service socket");
+ synchronized (ExtDtaSnepServer.this) {
+ threadRunning = mThreadRunning;
+ }
+
+ while (threadRunning) {
+ LlcpServerSocket serverSocket;
+ synchronized (ExtDtaSnepServer.this) {
+ serverSocket = mServerSocket;
+ }
+
+ if (serverSocket == null) {
+ if (DBG) Log.d(TAG, "Server socket shut down.");
+ return;
+ }
+ if (DBG) Log.d(TAG, "about to accept");
+ LlcpSocket communicationSocket = serverSocket.accept();
+ if (DBG) Log.d(TAG, "accept returned " + communicationSocket);
+ if (communicationSocket != null) {
+ int miu = communicationSocket.getRemoteMiu();
+ int fragmentLength = (mDtaFragmentLength == -1) ? miu : Math.min(miu, mDtaFragmentLength);
+ new ConnectionThread(communicationSocket, fragmentLength).start();
+ }
+
+ synchronized (ExtDtaSnepServer.this) {
+ threadRunning = mThreadRunning;
+ }
+ }
+ if (DBG) Log.d(TAG, "stop running");
+ } catch (LlcpException e) {
+ Log.e(TAG, "llcp error", e);
+ } catch (IOException e) {
+ Log.e(TAG, "IO error", e);
+ } finally {
+ synchronized (ExtDtaSnepServer.this) {
+ if (mServerSocket != null) {
+ if (DBG) Log.d(TAG, "about to close");
+ try {
+ mServerSocket.close();
+ } catch (IOException e) {}
+ mServerSocket = null;
+ }
+ }
+ }
+
+ synchronized (ExtDtaSnepServer.this) {
+ threadRunning = mThreadRunning;
+ }
+ }
+ }
+
+ public void shutdown() {
+ synchronized (ExtDtaSnepServer.this) {
+ mThreadRunning = false;
+ if (mServerSocket != null) {
+ try {
+ mServerSocket.close();
+ } catch (IOException e) {}
+ mServerSocket = null;
+ }
+ }
+ }
+ }
+
+ public void start() {
+ synchronized (ExtDtaSnepServer.this) {
+ if (DBG) Log.d(TAG, "start, thread = " + mServerThread);
+ if (mServerThread == null) {
+ if (DBG) Log.d(TAG, "starting new server thread");
+ mServerThread = new ServerThread();
+ mServerThread.start();
+ mServerRunning = true;
+ }
+ }
+ }
+
+ public void stop() {
+ synchronized (ExtDtaSnepServer.this) {
+ if (DBG) Log.d(TAG, "stop, thread = " + mServerThread);
+ if (mServerThread != null) {
+ if (DBG) Log.d(TAG, "shuting down server thread");
+ mServerThread.shutdown();
+ mServerThread = null;
+ mServerRunning = false;
+ }
+ }
+ }
+}