diff --git a/src/main/cpp/Android/jni/Android.mk b/src/main/cpp/Android/jni/Android.mk
new file mode 100644
index 0000000..2899929
--- /dev/null
+++ b/src/main/cpp/Android/jni/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := jSerialComm
+LOCAL_SRC_FILES := SerialPort_Android.cpp AndroidHelperFunctions.cpp
+
+include $(BUILD_SHARED_LIBRARY)
+
+all:
+ cp -rf libs/* ../../resources/Android/
+ rm -rf libs obj/* obj
diff --git a/src/main/cpp/Android/jni/AndroidHelperFunctions.cpp b/src/main/cpp/Android/jni/AndroidHelperFunctions.cpp
new file mode 100644
index 0000000..e77247d
--- /dev/null
+++ b/src/main/cpp/Android/jni/AndroidHelperFunctions.cpp
@@ -0,0 +1,92 @@
+/*
+ * AndroidHelperFunctions.cpp
+ *
+ * Created on: Mar 10, 2015
+ * Last Updated on: Mar 10, 2015
+ * Author: Will Hedgecock
+ *
+ * Copyright (C) 2012-2015 Fazecast, Inc.
+ *
+ * This file is part of jSerialComm.
+ *
+ * jSerialComm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSerialComm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with jSerialComm. If not, see .
+ */
+
+#ifdef __linux__
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "AndroidHelperFunctions.h"
+
+std::string getFriendlyName(const std::string& fullPathToSearch)
+{
+ std::string friendlyName;
+
+ std::ifstream input((fullPathToSearch + std::string("product")).c_str(), std::ios::in | std::ios::binary);
+ if (input.is_open())
+ {
+ std::getline(input, friendlyName);
+ input.close();
+ }
+
+ return friendlyName;
+}
+
+std::vector< std::pair > recursiveSearchForComPorts(const std::string& fullPathToSearch)
+{
+ // Open the directory
+ std::vector< std::pair > comPorts;
+ DIR *directoryIterator = opendir(fullPathToSearch.c_str());
+ if (directoryIterator == NULL)
+ return comPorts;
+
+ // Read all sub-directories in the current directory
+ struct dirent *directoryEntry = readdir(directoryIterator);
+ while (directoryEntry != NULL)
+ {
+ // Check if entry is a sub-directory
+ if (directoryEntry->d_type == DT_DIR)
+ {
+ // Only process non-dot, non-virtual directories
+ if ((directoryEntry->d_name[0] != '.') && (strcmp(directoryEntry->d_name, "virtual") != 0))
+ {
+ // See if the directory names a potential serial port
+ if ((strlen(directoryEntry->d_name) > 3) && (directoryEntry->d_name[0] == 't') && (directoryEntry->d_name[1] == 't') && (directoryEntry->d_name[2] == 'y'))
+ {
+ // See if device has a registered friendly name
+ std::string friendlyName = getFriendlyName(fullPathToSearch + std::string(directoryEntry->d_name) + std::string("/device/../"));
+ if (!friendlyName.empty())
+ comPorts.push_back(std::make_pair(std::string("/dev/") + std::string(directoryEntry->d_name), friendlyName));
+ }
+ else
+ {
+ // Search for more serial ports within the directory
+ std::vector< std::pair > newComPorts = recursiveSearchForComPorts(fullPathToSearch + std::string(directoryEntry->d_name) + std::string("/"));
+ comPorts.insert(comPorts.end(), newComPorts.begin(), newComPorts.end());
+ }
+ }
+ }
+ directoryEntry = readdir(directoryIterator);
+ }
+
+ // Close the directory
+ closedir(directoryIterator);
+ return comPorts;
+}
+
+#endif
diff --git a/src/main/cpp/Android/jni/AndroidHelperFunctions.h b/src/main/cpp/Android/jni/AndroidHelperFunctions.h
new file mode 100644
index 0000000..85e2e01
--- /dev/null
+++ b/src/main/cpp/Android/jni/AndroidHelperFunctions.h
@@ -0,0 +1,36 @@
+/*
+ * AndroidHelperFunctions.h
+ *
+ * Created on: Mar 10, 2015
+ * Last Updated on: Mar 10, 2015
+ * Author: Will Hedgecock
+ *
+ * Copyright (C) 2012-2015 Fazecast, Inc.
+ *
+ * This file is part of jSerialComm.
+ *
+ * jSerialComm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSerialComm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with jSerialComm. If not, see .
+ */
+
+#ifndef __ANDROID_HELPER_FUNCTIONS_HEADER_H__
+#define __ANDROID_HELPER_FUNCTIONS_HEADER_H__
+
+#include
+#include
+#include
+
+std::vector< std::pair > recursiveSearchForComPorts(const std::string& fullPathToSearch);
+std::string getFriendlyName(const std::string& fullPathToSearch);
+
+#endif // #ifndef __ANDROID_HELPER_FUNCTIONS_HEADER_H__
diff --git a/src/main/cpp/Android/jni/Application.mk b/src/main/cpp/Android/jni/Application.mk
new file mode 100644
index 0000000..1cf91a0
--- /dev/null
+++ b/src/main/cpp/Android/jni/Application.mk
@@ -0,0 +1,2 @@
+APP_ABI := all
+APP_STL := stlport_static
\ No newline at end of file
diff --git a/src/main/cpp/Android/jni/SerialPort_Android.cpp b/src/main/cpp/Android/jni/SerialPort_Android.cpp
new file mode 100644
index 0000000..27e648d
--- /dev/null
+++ b/src/main/cpp/Android/jni/SerialPort_Android.cpp
@@ -0,0 +1,396 @@
+/*
+ * SerialPort_Android.cpp
+ *
+ * Created on: Mar 13, 2015
+ * Last Updated on: Mar 13, 2015
+ * Author: Will Hedgecock
+ *
+ * Copyright (C) 2012-2015 Fazecast, Inc.
+ *
+ * This file is part of jSerialComm.
+ *
+ * jSerialComm is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSerialComm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with jSerialComm. If not, see .
+ */
+
+#ifdef __linux__
+#ifndef CMSPAR
+#define CMSPAR 010000000000
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "com_fazecast_jSerialComm_SerialPort.h"
+#include "AndroidHelperFunctions.h"
+
+JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommPorts(JNIEnv *env, jclass serialCommClass)
+{
+ // Get relevant SerialComm methods and IDs
+ jmethodID serialCommConstructor = env->GetMethodID(serialCommClass, "", "()V");
+ jfieldID portStringID = env->GetFieldID(serialCommClass, "portString", "Ljava/lang/String;");
+ jfieldID comPortID = env->GetFieldID(serialCommClass, "comPort", "Ljava/lang/String;");
+
+ // Enumerate serial ports on machine
+ std::vector< std::pair > serialPorts = recursiveSearchForComPorts("/sys/devices/");
+ jobjectArray arrayObject = env->NewObjectArray(serialPorts.size(), serialCommClass, 0);
+ int index = 0;
+ for (int i = 0; i < serialPorts.size(); ++i)
+ {
+ // Create new SerialComm object containing the enumerated values
+ jobject serialCommObject = env->NewObject(serialCommClass, serialCommConstructor);
+ env->SetObjectField(serialCommObject, portStringID, env->NewStringUTF(serialPorts[i].second.c_str()));
+ env->SetObjectField(serialCommObject, comPortID, env->NewStringUTF(serialPorts[i].first.c_str()));
+
+ // Add new SerialComm object to array
+ env->SetObjectArrayElement(arrayObject, index++, serialCommObject);
+ }
+
+ return arrayObject;
+}
+
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(JNIEnv *env, jobject obj)
+{
+ int fdSerial;
+ jstring portNameJString = (jstring)env->GetObjectField(obj, env->GetFieldID(env->GetObjectClass(obj), "comPort", "Ljava/lang/String;"));
+ const char *portName = env->GetStringUTFChars(portNameJString, NULL);
+
+ // Try to open existing serial port with read/write access
+ if ((fdSerial = open(portName, O_RDWR | O_NOCTTY | O_NONBLOCK)) > 0)
+ {
+ // Set port handle in Java structure
+ env->SetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"), fdSerial);
+
+ // Configure the port parameters and timeouts
+ if (Java_com_fazecast_jSerialComm_SerialPort_configPort(env, obj) && Java_com_fazecast_jSerialComm_SerialPort_configFlowControl(env, obj) &&
+ Java_com_fazecast_jSerialComm_SerialPort_configEventFlags(env, obj))
+ env->SetBooleanField(obj, env->GetFieldID(env->GetObjectClass(obj), "isOpened", "Z"), JNI_TRUE);
+ else
+ {
+ // Close the port if there was a problem setting the parameters
+ close(fdSerial);
+ fdSerial = -1;
+ env->SetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"), -1l);
+ env->SetBooleanField(obj, env->GetFieldID(env->GetObjectClass(obj), "isOpened", "Z"), JNI_FALSE);
+ }
+ }
+
+ env->ReleaseStringUTFChars(portNameJString, portName);
+ return (fdSerial == -1) ? JNI_FALSE : JNI_TRUE;
+}
+
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configPort(JNIEnv *env, jobject obj)
+{
+ struct termios options;
+ jclass serialCommClass = env->GetObjectClass(obj);
+ int portFD = (int)env->GetLongField(obj, env->GetFieldID(serialCommClass, "portHandle", "J"));
+ if (portFD <= 0)
+ return JNI_FALSE;
+
+ // Set raw-mode to allow the use of tcsetattr() and ioctl()
+ fcntl(portFD, F_SETFL, 0);
+ cfmakeraw(&options);
+
+ // Get port parameters from Java class
+ int baudRate = env->GetIntField(obj, env->GetFieldID(serialCommClass, "baudRate", "I"));
+ int byteSizeInt = env->GetIntField(obj, env->GetFieldID(serialCommClass, "dataBits", "I"));
+ int stopBitsInt = env->GetIntField(obj, env->GetFieldID(serialCommClass, "stopBits", "I"));
+ int parityInt = env->GetIntField(obj, env->GetFieldID(serialCommClass, "parity", "I"));
+ tcflag_t byteSize = (byteSizeInt == 5) ? CS5 : (byteSizeInt == 6) ? CS6 : (byteSizeInt == 7) ? CS7 : CS8;
+ tcflag_t stopBits = ((stopBitsInt == com_fazecast_jSerialComm_SerialPort_ONE_STOP_BIT) || (stopBitsInt == com_fazecast_jSerialComm_SerialPort_ONE_POINT_FIVE_STOP_BITS)) ? 0 : CSTOPB;
+ tcflag_t parity = (parityInt == com_fazecast_jSerialComm_SerialPort_NO_PARITY) ? 0 : (parityInt == com_fazecast_jSerialComm_SerialPort_ODD_PARITY) ? (PARENB | PARODD) : (parityInt == com_fazecast_jSerialComm_SerialPort_EVEN_PARITY) ? PARENB : (parityInt == com_fazecast_jSerialComm_SerialPort_MARK_PARITY) ? (PARENB | CMSPAR | PARODD) : (PARENB | CMSPAR);
+
+ // Retrieve existing port configuration
+ tcgetattr(portFD, &options);
+
+ // Set updated port parameters
+ options.c_cflag = (B38400 | byteSize | stopBits | parity | CLOCAL | CREAD);
+ if (parityInt == com_fazecast_jSerialComm_SerialPort_SPACE_PARITY)
+ options.c_cflag &= ~PARODD;
+ options.c_iflag = ((parityInt > 0) ? (INPCK | ISTRIP) : IGNPAR);
+ options.c_oflag = 0;
+ options.c_lflag = 0;
+
+ // Apply changes
+ tcsetattr(portFD, TCSAFLUSH, &options);
+ ioctl(portFD, TIOCEXCL); // Block non-root users from using this port
+ return JNI_TRUE;
+}
+
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configFlowControl(JNIEnv *env, jobject obj)
+{
+ struct termios options;
+ jclass serialCommClass = env->GetObjectClass(obj);
+ int portFD = (int)env->GetLongField(obj, env->GetFieldID(serialCommClass, "portHandle", "J"));
+ if (portFD <= 0)
+ return JNI_FALSE;
+
+ // Get port parameters from Java class
+ int flowControl = env->GetIntField(obj, env->GetFieldID(serialCommClass, "flowControl", "I"));
+ tcflag_t CTSRTSEnabled = (((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_CTS_ENABLED) > 0) ||
+ ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_RTS_ENABLED) > 0)) ? CRTSCTS : 0;
+ tcflag_t XonXoffInEnabled = ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_IN_ENABLED) > 0) ? IXOFF : 0;
+ tcflag_t XonXoffOutEnabled = ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_OUT_ENABLED) > 0) ? IXON : 0;
+
+ // Retrieve existing port configuration
+ tcgetattr(portFD, &options);
+
+ // Set updated port parameters
+ options.c_cflag |= CTSRTSEnabled;
+ options.c_iflag |= XonXoffInEnabled | XonXoffOutEnabled;
+ options.c_oflag = 0;
+ options.c_lflag = 0;
+
+ // Apply changes
+ tcsetattr(portFD, TCSAFLUSH, &options);
+ return JNI_TRUE;
+}
+
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configTimeouts(JNIEnv *env, jobject obj)
+{
+ // Get port timeouts from Java class
+ jclass serialCommClass = env->GetObjectClass(obj);
+ int serialFD = (int)env->GetLongField(obj, env->GetFieldID(serialCommClass, "portHandle", "J"));
+ int timeoutMode = env->GetIntField(obj, env->GetFieldID(serialCommClass, "timeoutMode", "I"));
+ int readTimeout = env->GetIntField(obj, env->GetFieldID(serialCommClass, "readTimeout", "I"));
+ if (serialFD <= 0)
+ return JNI_FALSE;
+
+ // Retrieve existing port configuration
+ struct termios options;
+ tcgetattr(serialFD, &options);
+ int flags = fcntl(serialFD, F_GETFL);
+
+ // Set updated port timeouts
+ if (((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING) > 0) && (readTimeout > 0)) // Read Semi-blocking with timeout
+ {
+ flags &= ~O_NONBLOCK;
+ options.c_cc[VMIN] = 0;
+ options.c_cc[VTIME] = readTimeout / 100;
+ }
+ else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING) > 0) // Read Semi-blocking without timeout
+ {
+ flags &= ~O_NONBLOCK;
+ options.c_cc[VMIN] = 1;
+ options.c_cc[VTIME] = 0;
+ }
+ else if (((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) && (readTimeout > 0)) // Read Blocking with timeout
+ {
+ flags &= ~O_NONBLOCK;
+ options.c_cc[VMIN] = 0;
+ options.c_cc[VTIME] = readTimeout / 100;
+ }
+ else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) // Read Blocking without timeout
+ {
+ flags &= ~O_NONBLOCK;
+ options.c_cc[VMIN] = 1;
+ options.c_cc[VTIME] = 0;
+ }
+ else // Non-blocking
+ {
+ flags |= O_NONBLOCK;
+ options.c_cc[VMIN] = 0;
+ options.c_cc[VTIME] = 0;
+ }
+
+ // Apply changes
+ fcntl(serialFD, F_SETFL, flags);
+ return (tcsetattr(serialFD, TCSAFLUSH, &options) == 0) ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configEventFlags(JNIEnv *env, jobject obj)
+{
+ jclass serialCommClass = env->GetObjectClass(obj);
+ int serialFD = (int)env->GetLongField(obj, env->GetFieldID(serialCommClass, "portHandle", "J"));
+ if (serialFD <= 0)
+ return JNI_FALSE;
+
+ // Get event flags from Java class
+ int eventsToMonitor = env->GetIntField(obj, env->GetFieldID(serialCommClass, "eventFlags", "I"));
+
+ // Change read timeouts if we are monitoring data received
+ if ((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_RECEIVED) > 0)
+ {
+ struct termios options;
+ tcgetattr(serialFD, &options);
+ int flags = fcntl(serialFD, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ options.c_cc[VMIN] = 0;
+ options.c_cc[VTIME] = 10;
+ fcntl(serialFD, F_SETFL, flags);
+ tcsetattr(serialFD, TCSAFLUSH, &options);
+ }
+ else
+ Java_com_fazecast_jSerialComm_SerialPort_configTimeouts(env, obj);
+
+ // Apply changes
+ return JNI_TRUE;
+}
+
+JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_waitForEvent(JNIEnv *env, jobject obj)
+{
+ jclass serialCommClass = env->GetObjectClass(obj);
+ int serialFD = (int)env->GetLongField(obj, env->GetFieldID(serialCommClass, "portHandle", "J"));
+ if (serialFD <= 0)
+ return 0;
+
+ // Initialize the waiting set and the timeouts
+ struct timeval timeout = { 1, 0 };
+ fd_set waitingSet;
+ FD_ZERO(&waitingSet);
+ FD_SET(serialFD, &waitingSet);
+
+ // Wait for a serial port event
+ int retVal = select(serialFD + 1, &waitingSet, NULL, NULL, &timeout);
+ if (retVal <= 0)
+ return 0;
+ return (FD_ISSET(serialFD, &waitingSet)) ? com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_AVAILABLE : 0;
+}
+
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_closePortNative(JNIEnv *env, jobject obj)
+{
+ // Close port
+ int portFD = (int)env->GetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"));
+ if (portFD <= 0)
+ return JNI_TRUE;
+ close(portFD);
+ env->SetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"), -1l);
+ env->SetBooleanField(obj, env->GetFieldID(env->GetObjectClass(obj), "isOpened", "Z"), JNI_FALSE);
+
+ return JNI_TRUE;
+}
+
+JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_bytesAvailable(JNIEnv *env, jobject obj)
+{
+ int serialPortFD = (int)env->GetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"));
+ int numBytesAvailable = -1;
+
+ if (serialPortFD > 0)
+ ioctl(serialPortFD, FIONREAD, &numBytesAvailable);
+
+ return numBytesAvailable;
+}
+
+JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_readBytes(JNIEnv *env, jobject obj, jbyteArray buffer, jlong bytesToRead)
+{
+ // Get port handle and read timeout from Java class
+ jclass serialCommClass = env->GetObjectClass(obj);
+ int timeoutMode = env->GetIntField(obj, env->GetFieldID(serialCommClass, "timeoutMode", "I"));
+ int readTimeout = env->GetIntField(obj, env->GetFieldID(serialCommClass, "readTimeout", "I"));
+ int serialPortFD = (int)env->GetLongField(obj, env->GetFieldID(serialCommClass, "portHandle", "J"));
+ if (serialPortFD == -1)
+ return -1;
+ int numBytesRead, numBytesReadTotal = 0, bytesRemaining = bytesToRead;
+ char* readBuffer = (char*)malloc(bytesToRead);
+
+ // Infinite blocking mode specified, don't return until we have completely finished the read
+ if (((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) && (readTimeout == 0))
+ {
+ // While there are more bytes we are supposed to read
+ while (bytesRemaining > 0)
+ {
+ if ((numBytesRead = read(serialPortFD, readBuffer+numBytesReadTotal, bytesRemaining)) == -1)
+ {
+ // Problem reading, close port
+ close(serialPortFD);
+ env->SetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"), -1l);
+ env->SetBooleanField(obj, env->GetFieldID(env->GetObjectClass(obj), "isOpened", "Z"), JNI_FALSE);
+ break;
+ }
+
+ // Fix index variables
+ numBytesReadTotal += numBytesRead;
+ bytesRemaining -= numBytesRead;
+ }
+ }
+ else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) // Blocking mode, but not indefinitely
+ {
+ // Get current system time
+ struct timeval expireTime, currTime;
+ gettimeofday(&expireTime, NULL);
+ expireTime.tv_usec += (readTimeout * 1000);
+ if (expireTime.tv_usec > 1000000)
+ {
+ expireTime.tv_sec += (expireTime.tv_usec * 0.000001);
+ expireTime.tv_usec = (expireTime.tv_usec % 1000000);
+ }
+
+ // While there are more bytes we are supposed to read and the timeout has not elapsed
+ do
+ {
+ if ((numBytesRead = read(serialPortFD, readBuffer+numBytesReadTotal, bytesRemaining)) == -1)
+ {
+ // Problem reading, close port
+ close(serialPortFD);
+ env->SetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"), -1l);
+ env->SetBooleanField(obj, env->GetFieldID(env->GetObjectClass(obj), "isOpened", "Z"), JNI_FALSE);
+ break;
+ }
+
+ // Fix index variables
+ numBytesReadTotal += numBytesRead;
+ bytesRemaining -= numBytesRead;
+
+ // Get current system time
+ gettimeofday(&currTime, NULL);
+ } while ((bytesRemaining > 0) && ((expireTime.tv_sec > currTime.tv_sec) ||
+ ((expireTime.tv_sec == currTime.tv_sec) && (expireTime.tv_usec > currTime.tv_usec))));
+ }
+ else // Semi- or non-blocking specified
+ {
+ // Read from port
+ if ((numBytesRead = read(serialPortFD, readBuffer, bytesToRead)) == -1)
+ {
+ // Problem reading, close port
+ close(serialPortFD);
+ env->SetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"), -1l);
+ env->SetBooleanField(obj, env->GetFieldID(env->GetObjectClass(obj), "isOpened", "Z"), JNI_FALSE);
+ }
+ else
+ numBytesReadTotal = numBytesRead;
+ }
+
+ // Return number of bytes read if successful
+ env->SetByteArrayRegion(buffer, 0, numBytesReadTotal, (jbyte*)readBuffer);
+ free(readBuffer);
+ return (numBytesRead == -1) ? -1 : numBytesReadTotal;
+}
+
+JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_writeBytes(JNIEnv *env, jobject obj, jbyteArray buffer, jlong bytesToWrite)
+{
+ int serialPortFD = (int)env->GetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"));
+ if (serialPortFD == -1)
+ return -1;
+ jbyte *writeBuffer = env->GetByteArrayElements(buffer, 0);
+ int numBytesWritten;
+
+ // Write to port
+ if ((numBytesWritten = write(serialPortFD, writeBuffer, bytesToWrite)) == -1)
+ {
+ // Problem writing, close port
+ close(serialPortFD);
+ env->SetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "portHandle", "J"), -1l);
+ env->SetBooleanField(obj, env->GetFieldID(env->GetObjectClass(obj), "isOpened", "Z"), JNI_FALSE);
+ }
+
+ // Return number of bytes written if successful
+ env->ReleaseByteArrayElements(buffer, writeBuffer, JNI_ABORT);
+ return numBytesWritten;
+}
+
+#endif
diff --git a/src/main/cpp/Android/jni/com_fazecast_jSerialComm_SerialPort.h b/src/main/cpp/Android/jni/com_fazecast_jSerialComm_SerialPort.h
new file mode 100644
index 0000000..9f5aa4e
--- /dev/null
+++ b/src/main/cpp/Android/jni/com_fazecast_jSerialComm_SerialPort.h
@@ -0,0 +1,147 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+/* Header for class com_fazecast_jSerialComm_SerialPort */
+
+#ifndef _Included_com_fazecast_jSerialComm_SerialPort
+#define _Included_com_fazecast_jSerialComm_SerialPort
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_fazecast_jSerialComm_SerialPort_NO_PARITY
+#define com_fazecast_jSerialComm_SerialPort_NO_PARITY 0L
+#undef com_fazecast_jSerialComm_SerialPort_ODD_PARITY
+#define com_fazecast_jSerialComm_SerialPort_ODD_PARITY 1L
+#undef com_fazecast_jSerialComm_SerialPort_EVEN_PARITY
+#define com_fazecast_jSerialComm_SerialPort_EVEN_PARITY 2L
+#undef com_fazecast_jSerialComm_SerialPort_MARK_PARITY
+#define com_fazecast_jSerialComm_SerialPort_MARK_PARITY 3L
+#undef com_fazecast_jSerialComm_SerialPort_SPACE_PARITY
+#define com_fazecast_jSerialComm_SerialPort_SPACE_PARITY 4L
+#undef com_fazecast_jSerialComm_SerialPort_ONE_STOP_BIT
+#define com_fazecast_jSerialComm_SerialPort_ONE_STOP_BIT 1L
+#undef com_fazecast_jSerialComm_SerialPort_ONE_POINT_FIVE_STOP_BITS
+#define com_fazecast_jSerialComm_SerialPort_ONE_POINT_FIVE_STOP_BITS 2L
+#undef com_fazecast_jSerialComm_SerialPort_TWO_STOP_BITS
+#define com_fazecast_jSerialComm_SerialPort_TWO_STOP_BITS 3L
+#undef com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_DISABLED
+#define com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_DISABLED 0L
+#undef com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_RTS_ENABLED
+#define com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_RTS_ENABLED 1L
+#undef com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_CTS_ENABLED
+#define com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_CTS_ENABLED 16L
+#undef com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_DSR_ENABLED
+#define com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_DSR_ENABLED 256L
+#undef com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_DTR_ENABLED
+#define com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_DTR_ENABLED 4096L
+#undef com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_IN_ENABLED
+#define com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_IN_ENABLED 65536L
+#undef com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_OUT_ENABLED
+#define com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_OUT_ENABLED 1048576L
+#undef com_fazecast_jSerialComm_SerialPort_TIMEOUT_NONBLOCKING
+#define com_fazecast_jSerialComm_SerialPort_TIMEOUT_NONBLOCKING 0L
+#undef com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING
+#define com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING 1L
+#undef com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_SEMI_BLOCKING
+#define com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_SEMI_BLOCKING 16L
+#undef com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING
+#define com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING 256L
+#undef com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_BLOCKING
+#define com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_BLOCKING 4096L
+#undef com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_AVAILABLE
+#define com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_AVAILABLE 1L
+#undef com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_RECEIVED
+#define com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_RECEIVED 16L
+#undef com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_WRITTEN
+#define com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_WRITTEN 256L
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: getCommPorts
+ * Signature: ()[Lcom/fazecast/jSerialComm/SerialPort;
+ */
+JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommPorts
+ (JNIEnv *, jclass);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: openPortNative
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: closePortNative
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_closePortNative
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: configPort
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configPort
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: configFlowControl
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configFlowControl
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: configTimeouts
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configTimeouts
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: configEventFlags
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configEventFlags
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: waitForEvent
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_waitForEvent
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: bytesAvailable
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_bytesAvailable
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: readBytes
+ * Signature: ([BJ)I
+ */
+JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_readBytes
+ (JNIEnv *, jobject, jbyteArray, jlong);
+
+/*
+ * Class: com_fazecast_jSerialComm_SerialPort
+ * Method: writeBytes
+ * Signature: ([BJ)I
+ */
+JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_writeBytes
+ (JNIEnv *, jobject, jbyteArray, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/main/java/com/fazecast/jSerialComm/SerialPort.java b/src/main/java/com/fazecast/jSerialComm/SerialPort.java
index de5db84..97c6ea3 100644
--- a/src/main/java/com/fazecast/jSerialComm/SerialPort.java
+++ b/src/main/java/com/fazecast/jSerialComm/SerialPort.java
@@ -25,8 +25,10 @@
package com.fazecast.jSerialComm;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -52,7 +54,31 @@ public final class SerialPort
tempFileDirectory += "/";
// Determine Operating System and architecture
- if (OS.indexOf("win") >= 0)
+ if (System.getProperty("java.vm.vendor").toLowerCase().contains("android"))
+ {
+ try
+ {
+ BufferedReader buildPropertiesFile = new BufferedReader(new FileReader("/system/build.prop"));
+ String line;
+ while ((line = buildPropertiesFile.readLine()) != null)
+ {
+ if (line.contains("ro.product.cpu.abi") || line.contains("ro.product.cpu.abi2") || line.contains("ro.product.cpu.abilist") ||
+ line.contains("ro.product.cpu.abilist64") || line.contains("ro.product.cpu.abilist32"))
+ {
+ libraryPath = (line.indexOf(',') == -1) ? "Android/" + line.substring(line.indexOf('=')+1) :
+ "Android/" + line.substring(line.indexOf('=')+1, line.indexOf(','));
+ break;
+ }
+ }
+ buildPropertiesFile.close();
+ }
+ catch (Exception e) { e.printStackTrace(); }
+
+ if (libraryPath.isEmpty())
+ libraryPath = "Android/armeabi";
+ fileName = "libjSerialComm.so";
+ }
+ else if (OS.indexOf("win") >= 0)
{
if (System.getProperty("os.arch").indexOf("64") >= 0)
libraryPath = "Windows/x86_64";
diff --git a/src/main/resources/Android/arm64-v8a/libjSerialComm.so b/src/main/resources/Android/arm64-v8a/libjSerialComm.so
new file mode 100644
index 0000000..54dd702
Binary files /dev/null and b/src/main/resources/Android/arm64-v8a/libjSerialComm.so differ
diff --git a/src/main/resources/Android/armeabi-v7a/libjSerialComm.so b/src/main/resources/Android/armeabi-v7a/libjSerialComm.so
new file mode 100644
index 0000000..40a5434
Binary files /dev/null and b/src/main/resources/Android/armeabi-v7a/libjSerialComm.so differ
diff --git a/src/main/resources/Android/armeabi/libjSerialComm.so b/src/main/resources/Android/armeabi/libjSerialComm.so
new file mode 100644
index 0000000..06468ce
Binary files /dev/null and b/src/main/resources/Android/armeabi/libjSerialComm.so differ
diff --git a/src/main/resources/Android/mips/libjSerialComm.so b/src/main/resources/Android/mips/libjSerialComm.so
new file mode 100644
index 0000000..46f4a94
Binary files /dev/null and b/src/main/resources/Android/mips/libjSerialComm.so differ
diff --git a/src/main/resources/Android/mips64/libjSerialComm.so b/src/main/resources/Android/mips64/libjSerialComm.so
new file mode 100644
index 0000000..05c4b81
Binary files /dev/null and b/src/main/resources/Android/mips64/libjSerialComm.so differ
diff --git a/src/main/resources/Android/x86/libjSerialComm.so b/src/main/resources/Android/x86/libjSerialComm.so
new file mode 100644
index 0000000..07ce008
Binary files /dev/null and b/src/main/resources/Android/x86/libjSerialComm.so differ
diff --git a/src/main/resources/Android/x86_64/libjSerialComm.so b/src/main/resources/Android/x86_64/libjSerialComm.so
new file mode 100644
index 0000000..ef07082
Binary files /dev/null and b/src/main/resources/Android/x86_64/libjSerialComm.so differ