module from github to local files.

This commit is contained in:
dxl
2020-10-26 16:05:49 +08:00
parent 70885f54d0
commit 1cefde1971
92 changed files with 8129 additions and 0 deletions
@@ -0,0 +1,27 @@
package com.dxl.iobridges;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.dxl.iobridges.test", appContext.getPackageName());
}
}
+2
View File
@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dxl.iobridges" />
@@ -0,0 +1,56 @@
package com.iobridges.bulkio;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.io.InputStream;
public class BulkInputStream extends InputStream {
private int timeout;
private UsbDeviceConnection connection;
private UsbEndpoint endpoint;
public BulkInputStream(UsbDeviceConnection connection, UsbEndpoint endpoint) {
this.connection = connection;
this.endpoint = endpoint;
}
@Override
public int read() throws IOException {
byte[] bs = new byte[1];
int len = connection.bulkTransfer(endpoint, bs, 1, timeout);
if (len > 0) {
return bs[0];
}
if (len < 0) {
return -1;
}
return 0;
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return connection.bulkTransfer(endpoint, b, b.length, timeout);
}
@Override
public int read(@NonNull byte[] b, int off, int len) throws IOException {
return connection.bulkTransfer(endpoint, b, off, len, timeout);
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
@Override
public void close() throws IOException {
connection.close();
}
}
@@ -0,0 +1,48 @@
package com.iobridges.bulkio;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.io.OutputStream;
public class BulkOutputStream extends OutputStream {
private int timeout = 2333;
private UsbDeviceConnection connection;
private UsbEndpoint endpoint;
public BulkOutputStream(UsbDeviceConnection connection, UsbEndpoint endpoint) {
this.connection = connection;
this.endpoint = endpoint;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
@Override
public void write(int b) throws IOException {
connection.bulkTransfer(endpoint, new byte[]{(byte) b}, 0, 1, timeout);
}
@Override
public void write(@NonNull byte[] b, int off, int len) throws IOException {
connection.bulkTransfer(endpoint, b, off, len, timeout);
}
@Override
public void write(@NonNull byte[] b) throws IOException {
connection.bulkTransfer(endpoint, b, 0, b.length, timeout);
}
@Override
public void close() throws IOException {
connection.close();
}
}
@@ -0,0 +1,22 @@
package com.iobridges.com;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
/**
* author DXL
*
* @version 1.0
*/
public interface Communication extends Serializable {
/**
* Get OutputStream implement!
*/
OutputStream getOutput();
/**
* Get InputStream implement!
*/
InputStream getInput();
}
@@ -0,0 +1,55 @@
package com.iobridges.com;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
/**
* Abstract device check base class, can judge whether the device and communication are normal!
*
* @author DXL
* @version 1.1
*/
public abstract class DeviceChecker implements Serializable, Closeable {
protected Communication communication;
public DeviceChecker(Communication communication) {
this.communication = communication;
}
/**
* Test a device is can work!
*
* @return if can work return true, if no, return false
*/
public final boolean check() throws IOException {
if (communication == null) {
throw new IOException("The communication must be nonnull.");
}
InputStream is = communication.getInput();
OutputStream os = communication.getOutput();
if (is == null || os == null) {
throw new IOException("The inputStream and outputStream must be nonnull.");
}
// Auto init communication to mapper.
LocalComBridgeAdapter.getInstance().setInputStream(is).setOutputStream(os)
.startServer(LocalComBridgeAdapter.NAMESPACE_DEFAULT);
if (!checkDevice()) {
// if check failed, we must to close client!
close();
// and close client communication!
LocalComBridgeAdapter.getInstance().stopClient();
return false;
}
return true;
}
protected abstract boolean checkDevice() throws IOException;
/**
* Close the device(Should no close communication. only close device!)
*/
public abstract void close() throws IOException;
}
@@ -0,0 +1,436 @@
package com.iobridges.com;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbEndpoint;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.util.Log;
import com.iobridges.utils.HexUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Arrays;
/**
* @author DXL
* Created by DXL on 2017/8/21.
* Communication control mapping implementation,
* the realization of inter process communication!
*/
public final class LocalComBridgeAdapter implements Serializable {
/*
* Warning!!!
* If your implementation does not need to be mapped to the c/c++ (only Java exists), please do not use this tool class!
*
* 警告!!!
* 如果您的实现不需要映射到底层(只存在Java),请不要使用此工具类!
* */
// The namespace of the LocalServerSocket
public static final String NAMESPACE_DEFAULT = "DXL.COM.ASL";
// The tag of the log.
private final String LOG_TAG = "LocalComBridgeAdapter";
// 本地套接字服务!
private LocalServerSocket serverSocket;
// 单例!
private static LocalComBridgeAdapter instance;
// 设备输入流!
private volatile InputStream mInputStreamFromDevice;
// 设备输出流!
private volatile OutputStream mOutputStreamFromDevice;
// 客户端输入流
private volatile InputStream mInputStreamFromSocket;
// 客户端输出流
private volatile OutputStream mOutputStreamFromSocket;
// 是否已经连接!
private volatile boolean isHasClient = false;
// 是否关闭监听!
private volatile boolean listenAccept = false;
// 连接到转发服务的客户端!
private LocalSocket socket;
// 设备数据转发线程是否可以工作
private volatile boolean forwardWork = false;
// connection lock!
private static final Object LOCK = new Object();
// con thread status!
private volatile boolean isConThreadRunning = false;
// device thread status!
private volatile boolean isDeviceDataThreadRunning = false;
// server action timeout, default is 1000ms!
private int timeout = 1000;
// current namespace
private String namespace;
// is server stop
private volatile boolean isServerStopAction = false;
// The buffer max for the device IO, default is 16384
private int deviceBufferMax = 16384;
private LocalComBridgeAdapter() { /* No instantiation is required */ }
/**
* Data forward thread!
* data will from a InputStream to a OutputStream!
*/
private class ConServerThread extends Thread {
@Override
public void run() {
while (listenAccept) {
try {
if (serverSocket != null) {
Log.d(LOG_TAG, "服务套接字堵塞等待连接中!");
isConThreadRunning = true;
LocalSocket socketInternal = serverSocket.accept();
if (isServerStopAction) throw new IOException("Server closed.");
Log.d(LOG_TAG, "服务套接字连接成功,将会开启一个客户端线程!");
mInputStreamFromSocket = socketInternal.getInputStream();
mOutputStreamFromSocket = socketInternal.getOutputStream();
socket = socketInternal;
isHasClient = true;
new SocketDataThread().start();
} else {
return;
}
} catch (IOException e) {
// e.printStackTrace();
isHasClient = false;
listenAccept = false;
Log.w(LOG_TAG, "Connection thread abort!");
break;
}
}
isConThreadRunning = false;
Log.d(LOG_TAG, "ConServerThread结束");
}
}
/**
* Data from socket to device transfer
* need client connect and stable communication!
*/
private class SocketDataThread extends Thread {
private byte[] buffer = new byte[1024 * 500];
SocketDataThread() {
setPriority(MAX_PRIORITY);
}
@Override
public void run() {
Log.d(LOG_TAG, "SocketDataThread执行");
while (true) {
try {
int available = mInputStreamFromSocket.available();
if (available > 0) {
int len = mInputStreamFromSocket.read(buffer);
// Log.d(LOG_TAG, "mInputStreamFromSocket.read()->len: " + len);
if (len > 0) {
mOutputStreamFromDevice.write(Arrays.copyOf(buffer, len));
mOutputStreamFromDevice.flush();
// Log.d(LOG_TAG, "SocketDataThread数据传输: " + HexUtil.toHexString(buffer, 0, len));
}
if (len == -1) {
throw new IOException("Socket already disconnected.");
}
}
} catch (IOException e) {
// e.printStackTrace();
break;
} catch (Exception e) {
// e.printStackTrace();
}
}
Log.d(LOG_TAG, "SocketDataThread结束");
}
}
/**
* Data from device to socket client transfer
* default case, it is always worked at runtime!
*/
private class DeviceDataThread extends Thread {
// 在某些机型上,这个值是无效的,会导致bulkTransfer()卡死
// private byte[] buffer = new byte[1024 * 500];
// 因此我们需要使用Usb规定的最大值
private final byte[] buffer = new byte[deviceBufferMax];
DeviceDataThread() {
setPriority(MAX_PRIORITY);
}
@Override
public void run() {
Log.d(LOG_TAG, "DeviceDataThread执行");
isDeviceDataThreadRunning = true;
while (forwardWork) {
if (isHasClient) { // 有客户端的时候才接收数据!
try {
int len = mInputStreamFromDevice.read(buffer);
if (len > 0) {
// Log.d(LOG_TAG, "mInputStreamFromDevice.read()->len: " + len);
mOutputStreamFromSocket.write(Arrays.copyOf(buffer, len));
mOutputStreamFromSocket.flush();
// Log.d(LOG_TAG, "DeviceDataThread数据传输: " + HexUtil.toHexString(buffer));
}
} catch (Exception e) {
// Empty
// e.printStackTrace();
}
}
}
Log.d(LOG_TAG, "DeviceDataThread结束");
}
}
public static LocalComBridgeAdapter getInstance() {
synchronized (LOCK) {
/*
* It is a single instance tools
* you can't instantiation than for once.
* */
if (instance == null) instance = new LocalComBridgeAdapter();
}
return instance;
}
/**
* get inputStream from external device
*
* @return the inputStream of device
*/
public InputStream getInputStream() {
return mInputStreamFromDevice;
}
/**
* set inputStream from external device
* The adapter will automatically read bytes from this input stream
* and forward them to the client with socket
*
* @param mInputStream the inputStream from external device implement!
* @return this
*/
public LocalComBridgeAdapter setInputStream(InputStream mInputStream) {
this.mInputStreamFromDevice = mInputStream;
return this;
}
/**
* get outputStream from external device
*
* @return the outputStream of device
*/
public OutputStream getOutputStream() {
return mOutputStreamFromDevice;
}
/**
* set outputStream from external device
* The adapter will automatically read bytes from the socket client
* and write them to this output stream
*
* @param mOutputStream the outputStream from external device implement!
* @return this
*/
public LocalComBridgeAdapter setOutputStream(OutputStream mOutputStream) {
this.mOutputStreamFromDevice = mOutputStream;
return this;
}
/**
* Turn on server on a namespace
* default namespace is {@link #NAMESPACE_DEFAULT }
* namespace only once instance at runtime
* namespace is unique
*
* @param namespace the namespace is also used when clients connect
* @return this
*/
public LocalComBridgeAdapter startServer(String namespace) {
synchronized (LOCK) {
if (!listenAccept) {
listenAccept = true;
isConThreadRunning = false;
isServerStopAction = false;
try {
if (serverSocket == null) {
serverSocket = new LocalServerSocket(namespace);
this.namespace = namespace;
}
// 创建一个客户端连接线程
new ConServerThread().start();
} catch (IOException e) {
e.printStackTrace();
Log.e(LOG_TAG, "If you see an error message like \"Address already in use\", check that you call the stopServer function");
}
// wait con thread running...
long startTime = System.currentTimeMillis();
while (!isConThreadRunning) {
// Log.w(LOG_TAG, "Waiting for LocalComBridgeAdapter server start......");
if (System.currentTimeMillis() - startTime > timeout) {
Log.w(LOG_TAG, "LocalComBridgeAdapter server start timeout.");
return this;
}
}
} else {
Log.w(LOG_TAG, "LocalComBridgeAdapter already start!");
}
if (!forwardWork) {
forwardWork = true;
isDeviceDataThreadRunning = false;
// 创建一个数据转发线程
new DeviceDataThread().start();
// wait device data thread running...
long startTime = System.currentTimeMillis();
while (!isDeviceDataThreadRunning) {
// Log.w(LOG_TAG, "Waiting for LocalComBridgeAdapter server start......");
if (System.currentTimeMillis() - startTime > timeout) {
Log.w(LOG_TAG, "LocalComBridgeAdapter server start timeout.");
return this;
}
}
}
// if task is start successfully, we need sleep 100ms, ensure all resource is readied.
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(LOG_TAG, "LocalComBridgeAdapter start!");
}
return this;
}
/**
* Stop server listen!
* After the service is stopped,
* the client will not be able to connect.
* In general, please do not shut down the service.
*
* @return this
*/
public LocalComBridgeAdapter stopServer() {
synchronized (LOCK) {
listenAccept = false;
isServerStopAction = true;
isDeviceDataThreadRunning = false;
if (serverSocket != null) {
try {
simClient2CloseServer();
serverSocket.close();
} catch (IOException ignored) {
}
serverSocket = null;
}
long startTime = System.currentTimeMillis();
while (isConThreadRunning) {
// Log.w(LOG_TAG, "Waiting for LocalComBridgeAdapter server stop......");
if (System.currentTimeMillis() - startTime > timeout) {
Log.w(LOG_TAG, "LocalComBridgeAdapter server stop timeout.");
return this;
}
}
Log.d(LOG_TAG, "LocalComBridgeAdapter server stop!");
}
return this;
}
// Simulate the behavior of requesting a connection to close it
private void simClient2CloseServer() {
LocalSocket localSocket = new LocalSocket();
try {
localSocket.connect(new LocalSocketAddress(namespace));
localSocket.shutdownInput();
localSocket.shutdownOutput();
localSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Stop client connection.
* Calling this function will close the client link from C/C++ or java or any...
* please call when you need to connect to a new client.
*
* @return this
*/
public LocalComBridgeAdapter stopClient() {
synchronized (LOCK) {
isHasClient = false;
try {
if (socket != null) {
// close socket
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
mInputStreamFromSocket.close();
mOutputStreamFromSocket.close();
socket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
Log.d(LOG_TAG, "LocalComBridgeAdapter client stop!");
}
return this;
}
/**
* The timeout fro start and stop server!
*
* @return timeout ms.
*/
public int getTimeout() {
return timeout;
}
/**
* The timeout for start and stop server!
*
* @param timeout timeout ms.
* @return this
*/
public LocalComBridgeAdapter setTimeout(int timeout) {
this.timeout = timeout;
return this;
}
/**
* Get buffer of device
*
* @return buffer size max
*/
public int getDeviceBufferMax() {
return deviceBufferMax;
}
/**
* Set buffer size max of the device.
* if you are UsbDevice, the buffer size recommend use default!
* or less than 16384, see {@link android.hardware.usb.UsbDeviceConnection#bulkTransfer(UsbEndpoint, byte[], int, int)}
*
* @param deviceBufferMax the new size of buffer
* @return this
*/
public LocalComBridgeAdapter setDeviceBufferMax(int deviceBufferMax) {
this.deviceBufferMax = deviceBufferMax;
return this;
}
/**
* Destroy all task and recovery all resources
* Warning!! this action only can run on app exit!
* It will destroy server and client, and adapter single instance!
* so, it can run on application exit only.
*/
public void destroy() {
stopClient();
stopServer();
forwardWork = false;
instance = null;
}
}
@@ -0,0 +1,260 @@
package com.iobridges.utils;
import android.util.Log;
import java.nio.charset.Charset;
import java.util.Arrays;
public class HexUtil {
private final static char[] HEX_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
public static String dumpHexString(byte[] array) {
return dumpHexString(array, 0, array.length);
}
public static String dumpHexString(byte[] array, int offset, int length) {
StringBuilder result = new StringBuilder();
byte[] line = new byte[16];
int lineIndex = 0;
result.append("\n0x");
result.append(toHexString(offset));
for (int i = offset; i < offset + length; i++) {
if (lineIndex == 16) {
result.append(" ");
for (int j = 0; j < 16; j++) {
if (line[j] > ' ' && line[j] < '~') {
result.append(new String(line, j, 1));
} else {
result.append(".");
}
}
result.append("\n0x");
result.append(toHexString(i));
lineIndex = 0;
}
byte b = array[i];
result.append(" ");
result.append(HEX_DIGITS[(b >>> 4) & 0x0F]);
result.append(HEX_DIGITS[b & 0x0F]);
line[lineIndex++] = b;
}
if (lineIndex != 16) {
int count = (16 - lineIndex) * 3;
count++;
for (int i = 0; i < count; i++) {
result.append(" ");
}
for (int i = 0; i < lineIndex; i++) {
if (line[i] > ' ' && line[i] < '~') {
result.append(new String(line, i, 1));
} else {
result.append(".");
}
}
}
return result.toString();
}
public static String toHexString(byte b) {
return toHexString(toByteArray(b));
}
public static String toHexString(byte[] array) {
if (array == null) return null;
return toHexString(array, 0, array.length);
}
public static String toHexString(Byte[] array) {
if (array == null) return null;
byte[] bytes = new byte[array.length];
for (int i = 0; i < array.length; i++) {
bytes[i] = array[i];
}
return toHexString(bytes, 0, array.length);
}
public static String toHexString(byte[] array, int offset, int length) {
char[] buf = new char[length * 2];
int bufIndex = 0;
for (int i = offset; i < offset + length; i++) {
byte b = array[i];
buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
}
return new String(buf);
}
public static String toHexString(int i) {
return toHexString(toByteArray(i));
}
public static String toHexString(short i) {
return toHexString(toByteArray(i));
}
public static String decorateHex(String hexStr) {
StringBuilder sb = new StringBuilder();
char[] charArr = hexStr.toCharArray();
for (int i = 0; i < charArr.length; i += 2) {
sb.append("0x").append(charArr[i])
.append(charArr[i + 1]).append(" ");
}
return sb.toString();
}
public static byte[] toByteArray(byte b) {
byte[] array = new byte[1];
array[0] = b;
return array;
}
public static byte[] toByteArray(int i) {
byte[] array = new byte[4];
array[3] = (byte) (i & 0xFF);
array[2] = (byte) ((i >> 8) & 0xFF);
array[1] = (byte) ((i >> 16) & 0xFF);
array[0] = (byte) ((i >> 24) & 0xFF);
return array;
}
public static byte[] toByteArray(short i) {
byte[] array = new byte[2];
array[1] = (byte) (i & 0xFF);
array[0] = (byte) ((i >> 8) & 0xFF);
return array;
}
private static int toByte(char c) {
if (c >= '0' && c <= '9')
return (c - '0');
if (c >= 'A' && c <= 'F')
return (c - 'A' + 10);
if (c >= 'a' && c <= 'f')
return (c - 'a' + 10);
Log.d("HexUtil", "The byte char is invalid: " + c);
//throw new RuntimeException("Invalid hex char '" + c + "'");
return 0;
}
public static int toInt(byte b) {
return b & 0xFF;
}
public static int toIntFrom2Byte(byte[] b) {
return Integer.parseInt(toHexString(b), 16);
}
/**
* 拆分byte数组
*
* @param bytes 要拆分的数组
* @param size 要按几个组成一份
* @return
*/
public static byte[][] splitBytes(byte[] bytes, int size) {
double splitLength = Double.parseDouble(size + "");
int arrayLength = (int) Math.ceil(bytes.length / splitLength);
byte[][] result = new byte[arrayLength][];
int from, to;
for (int i = 0; i < arrayLength; i++) {
from = (int) (i * splitLength);
to = (int) (from + splitLength);
if (to > bytes.length)
to = bytes.length;
result[i] = Arrays.copyOfRange(bytes, from, to);
}
return result;
}
public static byte[] bytesMerge(byte[]... arrs) {
byte[] ret = new byte[byteArraysLength(arrs)];
int pos = 0;
for (byte[] tmp : arrs) {
if (tmp != null && tmp.length > 0) {
System.arraycopy(tmp, 0, ret, pos, tmp.length);
pos += tmp.length;
}
}
return ret;
}
public static int byteArraysLength(byte[]... arrs) {
int ret = 0;
for (byte[] tmp : arrs) {
if (tmp != null)
ret += tmp.length;
}
return ret;
}
public static int byteArrayToInt(byte[] b) {
return (b[3] & 0xFF) |
(b[2] & 0xFF) << 8 |
(b[1] & 0xFF) << 16 |
(b[0] & 0xFF) << 24;
}
public static int byte2Int(byte[] data, int offset) {
byte[] bs = new byte[4];
for (int i = offset, j = 0; j < bs.length; i++, j++) {
bs[j] = data[i];
}
return HexUtil.byteArrayToInt(bs);
}
public static int hexString2Int(String hexString) {
byte[] hexByte = hexStringToByteArray(hexString);
return byteArrayToInt(hexByte);
}
public static byte[] intToByteArray(int a) {
return new byte[]{
(byte) ((a >> 24) & 0xFF),
(byte) ((a >> 16) & 0xFF),
(byte) ((a >> 8) & 0xFF),
(byte) (a & 0xFF)
};
}
public static byte[] hexStringToByteArray(String hexString) {
if (hexString == null) return null;
if (hexString.length() == 0) return null;
int length = hexString.length();
byte[] buffer = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
buffer[i / 2] = (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString
.charAt(i + 1)));
}
return buffer;
}
public static byte[] getAsciiBytes(String str) {
return str.getBytes(Charset.forName("ascii"));
}
//判断是否是十六进制格式的字符串
public static boolean isHexString(String str) {
if (str == null) return false;
if (str.matches("[0-9a-fA-F]+")) return true;
if (str.matches("0x[0-9a-fA-F]+")) return true;
return str.matches("0x[0-9a-fA-F] +");
}
}
@@ -0,0 +1,17 @@
package com.dxl.iobridges;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}