diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2017-07-21 05:01:38 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-07-21 05:01:38 +0000 |
commit | f09a03acb66945e85a8c7ce1b5dc346904080dab (patch) | |
tree | cc745ae50b9a2610c0eb0a25123752d4ec48c660 | |
parent | 3bed8828f067c9f40f245391f574663554263796 (diff) | |
parent | f939e8a61d3904ab30ec59e2762f5c543ebaa672 (diff) | |
download | android_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-x | nci/jni/NativeNfcManager.cpp | 25 | ||||
-rwxr-xr-x | nci/src/com/android/nfc/dhimpl/NativeNfcManager.java | 15 | ||||
-rw-r--r-- | src/com/android/nfc/DeviceHost.java | 4 | ||||
-rw-r--r-- | src/com/android/nfc/DtaServiceConnector.java | 100 | ||||
-rwxr-xr-x | src/com/android/nfc/NfcService.java | 84 | ||||
-rwxr-xr-x | src/com/android/nfc/P2pLinkManager.java | 137 | ||||
-rw-r--r-- | src/com/android/nfc/snep/SnepMessage.java | 74 | ||||
-rw-r--r-- | src/com/android/nfc/snep/SnepMessenger.java | 98 | ||||
-rw-r--r-- | src/com/android/nfc/snep/SnepServer.java | 4 | ||||
-rw-r--r-- | src/com/android/nfc/sneptest/DtaSnepClient.java | 335 | ||||
-rw-r--r-- | src/com/android/nfc/sneptest/ExtDtaSnepServer.java | 259 |
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; + } + } + } +} |