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