summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2017-07-21 05:01:38 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2017-07-21 05:01:38 +0000
commitf09a03acb66945e85a8c7ce1b5dc346904080dab (patch)
treecc745ae50b9a2610c0eb0a25123752d4ec48c660
parent3bed8828f067c9f40f245391f574663554263796 (diff)
parentf939e8a61d3904ab30ec59e2762f5c543ebaa672 (diff)
downloadandroid_packages_apps_Nfc-f09a03acb66945e85a8c7ce1b5dc346904080dab.tar.gz
android_packages_apps_Nfc-f09a03acb66945e85a8c7ce1b5dc346904080dab.tar.bz2
android_packages_apps_Nfc-f09a03acb66945e85a8c7ce1b5dc346904080dab.zip
Merge "DTA support and extensions added." into oc-dr1-dev
-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;
+ }
+ }
+ }
+}