blob: a90cc7beeba473b2ff0a21f67601ba7f1c178a88 [file] [log] [blame]
/*
* 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;
}
}
}
}