diff --git a/README.md b/README.md index 15c9752..f54c0ec 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,82 @@ # Android-SerialPort-API -Google官方的Android串口通信Demo,修改成Android Studio项目 +[Fork](https://code.google.com/archive/p/android-serialport-api/)自Google开源的Android串口通信Demo,修改成Android Studio项目 -用这个API写的一个小工具 https://github.com/licheedev/Android-SerialPort-Tool +This lib is a [fork](https://code.google.com/archive/p/android-serialport-api/) of the Android serial port communication Demo open sourced by Google. -[![](https://jitpack.io/v/licheedev/Android-SerialPort-API.svg)](https://jitpack.io/#licheedev/Android-SerialPort-API) +## Installation & Usage +**Gradle** -**Gradle 引用** +添加依赖: -1. 在根build.gradle中添加 +Add the dependency: ``` allprojects { repositories { ... - maven { url 'https://jitpack.io' } + jcenter() + mavenCentral() // since 2.1.3 } } -``` - -2. 子module添加依赖 -``` dependencies { - compile 'com.github.licheedev.Android-SerialPort-API:serialport:1.0.1' + // 传统4KB内存页面版本 + implementation 'com.licheedev:android-serialport:2.1.4' + // 适配16KB页面版本,https://developer.android.google.cn/guide/practices/page-sizes?hl=zh-cn + implementation 'com.licheedev:android-serialport:2.1.5' } ``` -**修改`su`路径** +**Import** + +```java +import android.serialport.SerialPort; +``` + +**`su` path** + +In order to read/write to a serial port in Android you'll need `su` binary installed on device (this can be done by rooting the device). Usually Android devices that has the ability to communicate with serial ports have `su` installed on the default path `"/system/bin/su"`. To change this use: ```java // su默认路径为 "/system/bin/su" +// The default path of su is "/system/bin/su" // 可通过此方法修改 +// If the path is different then change it using this SerialPort.setSuPath("/system/xbin/su"); ``` + +**Usage** + +```java +// 默认8N1(8数据位、无校验位、1停止位) +// Default 8N1 (8 data bits, no parity bit, 1 stop bit) +SerialPort serialPort = new SerialPort(path, baudrate); + +// 可选配置数据位、校验位、停止位 - 7E2(7数据位、偶校验、2停止位) +// or with builder (with optional configurations) - 7E2 (7 data bits, even parity, 2 stop bits) +SerialPort serialPort = SerialPort + .newBuilder(path, baudrate) +// 校验位;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) +// Check bit; 0: no check bit (NONE, default); 1: odd check bit (ODD); 2: even check bit (EVEN) +// .parity(2) +// 数据位,默认8;可选值为5~8 +// Data bit, default 8; optional value is 5~8 +// .dataBits(7) +// 停止位,默认1;1:1位停止位;2:2位停止位 +// Stop bit, default 1; 1:1 stop bit; 2: 2 stop bit +// .stopBits(2) + .build(); + +// read/write to serial port - needs to be in different thread! +InputStream in = serialPort.getInputStream(); +OutputStream out = serialPort.getOutputStream(); + +// close +serialPort.tryClose(); +``` + +实现方式参考 + +Implementation reference +1. Check [sample project](https://github.com/licheedev/Android-SerialPort-API/tree/master/sample) +2. https://juejin.im/post/5c010a19e51d456ac27b40fc diff --git a/build.gradle b/build.gradle index ddeb90e..a47094e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,19 +2,23 @@ buildscript { repositories { + google() jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' + classpath 'com.android.tools.build:gradle:4.2.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files + classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.4.32' } } allprojects { repositories { + google() jcenter() + mavenCentral() } } @@ -23,14 +27,11 @@ task clean(type: Delete) { } ext { - compileSdkVersion = 25 - buildToolsVersion = "26.0.2" + compileSdkVersion = 29 minSdkVersion = 8 - targetSdkVersion = 25 + targetSdkVersion = 29 - versionCode = 2 - versionName = "1.0.1" - - + versionCode = 3 + versionName = "2.1.5" } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index aa62a10..567806c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,6 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true #Mon Sep 18 19:30:46 CST 2017 +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7690ea5..3ed5e54 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Mar 10 14:38:00 CST 2018 +#Fri Sep 06 10:04:58 CST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/jitpack.gradle b/jitpack.gradle deleted file mode 100644 index 3e87c62..0000000 --- a/jitpack.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply plugin: 'com.github.dcendents.android-maven' - -group = 'com.github.licheedev' - -tasks.withType(JavaCompile) { - options.encoding = "UTF-8" -} - -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -task javadoc(type: Javadoc) { - failOnError false - source = android.sourceSets.main.java.sourceFiles - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - classpath += configurations.compile -} - - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} \ No newline at end of file diff --git a/maven_publish.gradle b/maven_publish.gradle new file mode 100644 index 0000000..e8a2281 --- /dev/null +++ b/maven_publish.gradle @@ -0,0 +1,130 @@ +// 依赖信息 +def groupIdDefined = "com.licheedev" +def artifactIdDefined = "android-serialport" +// 如果是测试版,版本号后面加上 -SNAPSHOT +def versionDefined = rootProject.ext.versionName +// 其他信息 +def gitUrl = "https://github.com/licheedev/Android-SerialPort-API" + +// 配置是否上传 +def toUpload = true + +//在根build.gradle中加入,最新版本号参考 https://github.com/Kotlin/dokka#using-dokka +//classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.4.32' + +//在module的build.gradle末位加入 +//apply from: '../maven_publish.gradle' + +if (toUpload) { + apply plugin: 'signing' + apply plugin: 'maven-publish' + apply plugin: 'org.jetbrains.dokka' + + // + // 打包源码 + task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' + } + + task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + options { + encoding = "utf-8" + charSet 'UTF-8' + } + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile + } + + // 打包javadoc + task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir + } + + // 打包包含kotlin源码的javadoc + task kotlinDocJar(type: Jar, dependsOn: dokkaHtml) { + classifier = 'javadoc' + from dokkaHtml.outputDirectory + } + // + + afterEvaluate { + + publishing { + publications { + mavenAndroid(MavenPublication) { + from components.release + + groupId "$groupIdDefined" + artifactId "$artifactIdDefined" + version "$versionDefined" + // 上传source + artifact sourcesJar + // 上传javadoc + if (project.plugins.hasPlugin('kotlin-android')) { + artifact kotlinDocJar + } else { + artifact javadocJar + } + + pom { + name = 'Android-SerialPort-API' + description = 'Android-SerialPort-API' + url = "$gitUrl" + + //licenses { + // license { + // name = 'The MIT License' + // url = 'https://opensource.org/licenses/MIT' + // } + //} + + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + + developers { + developer { + id = 'licheedev' + name = 'John Lee' + email = 'licheedev@foxmail.com' + } + } + scm { + connection = "$gitUrl" + developerConnection = "${gitUrl}.git" + url = "$gitUrl" + } + } + } + } + + repositories { + + maven { + // 依赖发布地址 + def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" + def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + + // 配置账号密码 + println "user=${ossrhUsername},key=${ossrhPassword}" + credentials { + username "${ossrhUsername}" + password "${ossrhPassword}" + } + } + } + } + + signing { + sign publishing.publications.mavenAndroid + } + } +} \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 36c7fc9..00bc09a 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,17 +1,16 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion '26.0.2' + compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { applicationId "android.serialport.sample" minSdkVersion 15 - targetSdkVersion 25 + targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { release { @@ -22,12 +21,12 @@ android { } dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(include: ['*.jar'], dir: 'libs') + androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:25.3.1' - compile 'com.android.support.constraint:constraint-layout:1.0.2' - testCompile 'junit:junit:4.12' - compile project(':serialport') + implementation 'androidx.appcompat:appcompat:1.2.0' + testImplementation 'junit:junit:4.13' + //implementation project(':serialport') + implementation 'com.licheedev:android-serialport:2.1.5' } diff --git a/sample/src/androidTest/java/android/serialport/sample/ExampleInstrumentedTest.java b/sample/src/androidTest/java/android/serialport/sample/ExampleInstrumentedTest.java index b65e8d8..a7501ea 100644 --- a/sample/src/androidTest/java/android/serialport/sample/ExampleInstrumentedTest.java +++ b/sample/src/androidTest/java/android/serialport/sample/ExampleInstrumentedTest.java @@ -1,8 +1,8 @@ package android.serialport.sample; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/sample/src/main/java/android/serialport/sample/Application.java b/sample/src/main/java/android/serialport/sample/Application.java index e58cf46..a5c3ab3 100644 --- a/sample/src/main/java/android/serialport/sample/Application.java +++ b/sample/src/main/java/android/serialport/sample/Application.java @@ -1,17 +1,17 @@ /* * Copyright 2009 Cedric Priscal - * + * * 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. + * limitations under the License. */ package android.serialport.sample; @@ -19,7 +19,6 @@ import android.content.SharedPreferences; import android.serialport.SerialPort; import android.serialport.SerialPortFinder; -import java.io.File; import java.io.IOException; import java.security.InvalidParameterException; @@ -38,13 +37,22 @@ public SerialPort getSerialPort() String path = sp.getString("DEVICE", ""); int baudrate = Integer.decode(sp.getString("BAUDRATE", "-1")); - /* Check parameters */ + /* Check parameters */ if ((path.length() == 0) || (baudrate == -1)) { throw new InvalidParameterException(); } - /* Open the serial port */ - mSerialPort = new SerialPort(new File(path), baudrate, 0); + /* Open the serial port */ + //mSerialPort = new SerialPort(new File(path), baudrate, 0); + + SerialPort serialPort = SerialPort // + .newBuilder(path, baudrate) // 串口地址地址,波特率 + .parity(2) // 校验位;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) + .dataBits(7) // 数据位,默认8;可选值为5~8 + .stopBits(2) // 停止位,默认1;1:1位停止位;2:2位停止位 + .build(); + + mSerialPort = serialPort; } return mSerialPort; } diff --git a/serialport/.gitignore b/serialport/.gitignore index a222170..968c49e 100644 --- a/serialport/.gitignore +++ b/serialport/.gitignore @@ -1,2 +1,3 @@ /build .externalNativeBuild +.cxx diff --git a/serialport/CMakeLists.txt b/serialport/CMakeLists.txt index f16cb32..80def00 100644 --- a/serialport/CMakeLists.txt +++ b/serialport/CMakeLists.txt @@ -2,7 +2,7 @@ # This ensures that a certain set of CMake features is available to # your build. -cmake_minimum_required(VERSION 3.4.1) +cmake_minimum_required(VERSION 3.18.1) # Specifies a library name, specifies whether the library is STATIC or # SHARED, and provides relative paths to the source code. You can @@ -35,4 +35,10 @@ target_link_libraries( # Specifies the target library. # Links the target library to the log library # included in the NDK. - ${log-lib} ) \ No newline at end of file + ${log-lib} ) + +target_link_options( + serial_port + PRIVATE + "-Wl,-z,max-page-size=16384" +) \ No newline at end of file diff --git a/serialport/build.gradle b/serialport/build.gradle index d0943bf..b5a36b4 100644 --- a/serialport/build.gradle +++ b/serialport/build.gradle @@ -3,15 +3,14 @@ apply plugin: 'com.android.library' android { compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode rootProject.ext.versionCode + versionCode 1 versionName rootProject.ext.versionName - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { release { @@ -22,6 +21,12 @@ android { externalNativeBuild { cmake { path 'CMakeLists.txt' + version "3.18.1" + } + } + packagingOptions { + jniLibs { + useLegacyPackaging true } } } @@ -33,7 +38,10 @@ dependencies { // }) // compile 'com.android.support:appcompat-v7:25.3.0' // testCompile 'junit:junit:4.12' + + + api "androidx.annotation:annotation:1.1.0" } -apply from: '../jitpack.gradle' +apply from: '../maven_publish.gradle' diff --git a/serialport/src/main/AndroidManifest.xml b/serialport/src/main/AndroidManifest.xml index 808f2ee..6c1ed51 100644 --- a/serialport/src/main/AndroidManifest.xml +++ b/serialport/src/main/AndroidManifest.xml @@ -1,10 +1,4 @@ - - - - - - + + diff --git a/serialport/src/main/cpp/SerialPort.c b/serialport/src/main/cpp/SerialPort.c index 7211cd9..07049cb 100644 --- a/serialport/src/main/cpp/SerialPort.c +++ b/serialport/src/main/cpp/SerialPort.c @@ -25,47 +25,79 @@ #include "SerialPort.h" #include "android/log.h" -static const char *TAG="serial_port"; + +static const char *TAG = "serial_port"; #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) -static speed_t getBaudrate(jint baudrate) -{ - switch(baudrate) { - case 0: return B0; - case 50: return B50; - case 75: return B75; - case 110: return B110; - case 134: return B134; - case 150: return B150; - case 200: return B200; - case 300: return B300; - case 600: return B600; - case 1200: return B1200; - case 1800: return B1800; - case 2400: return B2400; - case 4800: return B4800; - case 9600: return B9600; - case 19200: return B19200; - case 38400: return B38400; - case 57600: return B57600; - case 115200: return B115200; - case 230400: return B230400; - case 460800: return B460800; - case 500000: return B500000; - case 576000: return B576000; - case 921600: return B921600; - case 1000000: return B1000000; - case 1152000: return B1152000; - case 1500000: return B1500000; - case 2000000: return B2000000; - case 2500000: return B2500000; - case 3000000: return B3000000; - case 3500000: return B3500000; - case 4000000: return B4000000; - default: return -1; - } +static speed_t getBaudrate(jint baudrate) { + switch (baudrate) { + case 0: + return B0; + case 50: + return B50; + case 75: + return B75; + case 110: + return B110; + case 134: + return B134; + case 150: + return B150; + case 200: + return B200; + case 300: + return B300; + case 600: + return B600; + case 1200: + return B1200; + case 1800: + return B1800; + case 2400: + return B2400; + case 4800: + return B4800; + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; + default: + return -1; + } } /* @@ -74,74 +106,123 @@ static speed_t getBaudrate(jint baudrate) * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; */ JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open - (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) -{ - int fd; - speed_t speed; - jobject mFileDescriptor; - - /* Check arguments */ - { - speed = getBaudrate(baudrate); - if (speed == -1) { - /* TODO: throw an exception */ - LOGE("Invalid baudrate"); - return NULL; - } - } - - /* Opening device */ - { - jboolean iscopy; - const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); - LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); - fd = open(path_utf, O_RDWR | flags); - LOGD("open() fd = %d", fd); - (*env)->ReleaseStringUTFChars(env, path, path_utf); - if (fd == -1) - { - /* Throw an exception */ - LOGE("Cannot open port"); - /* TODO: throw an exception */ - return NULL; - } - } - - /* Configure device */ - { - struct termios cfg; - LOGD("Configuring serial port"); - if (tcgetattr(fd, &cfg)) - { - LOGE("tcgetattr() failed"); - close(fd); - /* TODO: throw an exception */ - return NULL; - } - - cfmakeraw(&cfg); - cfsetispeed(&cfg, speed); - cfsetospeed(&cfg, speed); - - if (tcsetattr(fd, TCSANOW, &cfg)) - { - LOGE("tcsetattr() failed"); - close(fd); - /* TODO: throw an exception */ - return NULL; - } - } - - /* Create a corresponding file descriptor */ - { - jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); - jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); - jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); - mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); - (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); - } - - return mFileDescriptor; + (JNIEnv *env, jobject thiz, jstring path, jint baudrate, jint dataBits, jint parity, + jint stopBits, + jint flags) { + + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + + cfg.c_cflag &= ~CSIZE; + switch (dataBits) { + case 5: + cfg.c_cflag |= CS5; //使用5位数据位 + break; + case 6: + cfg.c_cflag |= CS6; //使用6位数据位 + break; + case 7: + cfg.c_cflag |= CS7; //使用7位数据位 + break; + case 8: + cfg.c_cflag |= CS8; //使用8位数据位 + break; + default: + cfg.c_cflag |= CS8; + break; + } + + switch (parity) { + case 0: + cfg.c_cflag &= ~PARENB; //无奇偶校验 + break; + case 1: + cfg.c_cflag |= (PARODD | PARENB); //奇校验 + break; + case 2: + cfg.c_iflag &= ~(IGNPAR | PARMRK); // 偶校验 + cfg.c_iflag |= INPCK; + cfg.c_cflag |= PARENB; + cfg.c_cflag &= ~PARODD; + break; + default: + cfg.c_cflag &= ~PARENB; + break; + } + + switch (stopBits) { + case 1: + cfg.c_cflag &= ~CSTOPB; //1位停止位 + break; + case 2: + cfg.c_cflag |= CSTOPB; //2位停止位 + break; + default: + cfg.c_cflag &= ~CSTOPB; //1位停止位 + break; + } + + if (tcsetattr(fd, TCSANOW, &cfg)) { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint) fd); + } + + return mFileDescriptor; } /* @@ -150,18 +231,17 @@ JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open * Signature: ()V */ JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close - (JNIEnv *env, jobject thiz) -{ - jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); - jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + (JNIEnv *env, jobject thiz) { + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); - jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); - jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); - jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); - jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); - LOGD("close(fd = %d)", descriptor); - close(descriptor); + LOGD("close(fd = %d)", descriptor); + close(descriptor); } diff --git a/serialport/src/main/cpp/SerialPort.h b/serialport/src/main/cpp/SerialPort.h index 76638ab..bed2cc7 100644 --- a/serialport/src/main/cpp/SerialPort.h +++ b/serialport/src/main/cpp/SerialPort.h @@ -10,10 +10,10 @@ extern "C" { /* * Class: android_serialport_SerialPort * Method: open - * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + * Signature: (Ljava/lang/String;IIIII)Ljava/io/FileDescriptor; */ JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open - (JNIEnv *, jclass, jstring, jint, jint); + (JNIEnv *, jobject, jstring, jint, jint, jint, jint, jint); /* * Class: android_serialport_SerialPort @@ -27,3 +27,14 @@ JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close } #endif #endif +/* Header for class android_serialport_SerialPort_Builder */ + +#ifndef _Included_android_serialport_SerialPort_Builder +#define _Included_android_serialport_SerialPort_Builder +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif diff --git a/serialport/src/main/java/android/serialport/SerialPort.java b/serialport/src/main/java/android/serialport/SerialPort.java index cc189d2..a07b705 100644 --- a/serialport/src/main/java/android/serialport/SerialPort.java +++ b/serialport/src/main/java/android/serialport/SerialPort.java @@ -1,22 +1,24 @@ /* * Copyright 2009 Cedric Priscal - * + * * 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. + * limitations under the License. */ package android.serialport; import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -25,36 +27,78 @@ import java.io.InputStream; import java.io.OutputStream; -public class SerialPort { +public final class SerialPort { private static final String TAG = "SerialPort"; - private static final String DEFAULT_SU_PATH = "/system/bin/su"; + public static final String DEFAULT_SU_PATH = "/system/bin/su"; private static String sSuPath = DEFAULT_SU_PATH; + private File device; + private int baudrate; + private int dataBits; + private int parity; + private int stopBits; + private int flags; /** * Set the su binary path, the default su binary path is {@link #DEFAULT_SU_PATH} * * @param suPath su binary path */ - public static void setSuPath(String suPath) { + public static void setSuPath(@Nullable String suPath) { if (suPath == null) { return; } sSuPath = suPath; } + /** + * Get the su binary path + * + * @return + */ + @NonNull + public static String getSuPath() { + return sSuPath; + } + /* - * Do not remove or rename the field mFd: it is used by native method close(); - */ + * Do not remove or rename the field mFd: it is used by native method close(); + */ private FileDescriptor mFd; private FileInputStream mFileInputStream; private FileOutputStream mFileOutputStream; - public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + /** + * 串口 + * + * @param device 串口设备文件 + * @param baudrate 波特率 + * @param dataBits 数据位;默认8,可选值为5~8 + * @param parity 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) + * @param stopBits 停止位;默认1;1:1位停止位;2:2位停止位 + * @param flags 默认0 + * @throws SecurityException + * @throws IOException + */ + public SerialPort( + @NonNull File device, int baudrate, int dataBits, int parity, int stopBits, + int flags + ) throws SecurityException, IOException { + + this.device = device; + this.baudrate = baudrate; + this.dataBits = dataBits; + this.parity = parity; + this.stopBits = stopBits; + this.flags = flags; + + if (!device.exists()) { + throw new IOException("SerialPort(" + device.getAbsolutePath() + ") not exists"); + } - /* Check access permission */ + /* Check access permission */ if (!device.canRead() || !device.canWrite()) { try { /* Missing read/write permission, trying to chmod the file */ @@ -71,43 +115,198 @@ public SerialPort(File device, int baudrate, int flags) throws SecurityException } } - mFd = open(device.getAbsolutePath(), baudrate, flags); + mFd = open(device.getAbsolutePath(), baudrate, dataBits, parity, stopBits, flags); if (mFd == null) { Log.e(TAG, "native open returns null"); - throw new IOException(); + throw new IOException("native open" + + "SerialPort(" + device.getAbsolutePath() + + ") returns null"); } mFileInputStream = new FileInputStream(mFd); mFileOutputStream = new FileOutputStream(mFd); } - public SerialPort(String devicePath, int baudrate, int flags) - throws SecurityException, IOException { - this(new File(devicePath), baudrate, flags); - } - - public SerialPort(File device, int baudrate) throws SecurityException, IOException { - this(device, baudrate, 0); + /** + * 串口,默认的8n1 + * + * @param device 串口设备文件 + * @param baudrate 波特率 + * @throws SecurityException + * @throws IOException + */ + public SerialPort(@NonNull File device, int baudrate) throws SecurityException, IOException { + this(device, baudrate, 8, 0, 1, 0); } - public SerialPort(String devicePath, int baudrate) throws SecurityException, IOException { - this(new File(devicePath), baudrate, 0); + /** + * 串口 + * + * @param device 串口设备文件 + * @param baudrate 波特率 + * @param dataBits 数据位;默认8,可选值为5~8 + * @param parity 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) + * @param stopBits 停止位;默认1;1:1位停止位;2:2位停止位 + * @throws SecurityException + * @throws IOException + */ + public SerialPort(@NonNull File device, int baudrate, int dataBits, int parity, int stopBits) + throws SecurityException, IOException { + this(device, baudrate, dataBits, parity, stopBits, 0); } // Getters and setters + @NonNull public InputStream getInputStream() { return mFileInputStream; } + @NonNull public OutputStream getOutputStream() { return mFileOutputStream; } + /** 串口设备文件 */ + @NonNull + public File getDevice() { + return device; + } + + /** 波特率 */ + public int getBaudrate() { + return baudrate; + } + + /** 数据位;默认8,可选值为5~8 */ + public int getDataBits() { + return dataBits; + } + + /** 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) */ + public int getParity() { + return parity; + } + + /** 停止位;默认1;1:1位停止位;2:2位停止位 */ + public int getStopBits() { + return stopBits; + } + + public int getFlags() { + return flags; + } + // JNI - private native static FileDescriptor open(String path, int baudrate, int flags); + private native FileDescriptor open( + String absolutePath, int baudrate, int dataBits, int parity, + int stopBits, int flags + ); public native void close(); + /** 关闭流和串口,已经try-catch */ + public void tryClose() { + try { + mFileInputStream.close(); + } catch (IOException e) { + //e.printStackTrace(); + } + + try { + mFileOutputStream.close(); + } catch (IOException e) { + //e.printStackTrace(); + } + + try { + close(); + } catch (Exception e) { + //e.printStackTrace(); + } + } + static { System.loadLibrary("serial_port"); } + + public static Builder newBuilder(File device, int baudrate) { + return new Builder(device, baudrate); + } + + public static Builder newBuilder(String devicePath, int baudrate) { + return new Builder(devicePath, baudrate); + } + + public final static class Builder { + + private File device; + private int baudrate; + private int dataBits = 8; + private int parity = 0; + private int stopBits = 1; + private int flags = 0; + + private Builder(File device, int baudrate) { + this.device = device; + this.baudrate = baudrate; + } + + private Builder(String devicePath, int baudrate) { + this(new File(devicePath), baudrate); + } + + /** + * 数据位 + * + * @param dataBits 默认8,可选值为5~8 + * @return + */ + public Builder dataBits(int dataBits) { + this.dataBits = dataBits; + return this; + } + + /** + * 校验位 + * + * @param parity 0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) + * @return + */ + public Builder parity(int parity) { + this.parity = parity; + return this; + } + + /** + * 停止位 + * + * @param stopBits 默认1;1:1位停止位;2:2位停止位 + * @return + */ + public Builder stopBits(int stopBits) { + this.stopBits = stopBits; + return this; + } + + /** + * 标志 + * + * @param flags 默认0 + * @return + */ + public Builder flags(int flags) { + this.flags = flags; + return this; + } + + /** + * 打开并返回串口 + * + * @return + * @throws SecurityException + * @throws IOException + */ + public SerialPort build() throws SecurityException, IOException { + return new SerialPort(device, baudrate, dataBits, parity, stopBits, flags); + } + } } diff --git a/serialport_bin/.gitignore b/serialport_bin/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/serialport_bin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/serialport_bin/build.gradle b/serialport_bin/build.gradle deleted file mode 100644 index 44832c7..0000000 --- a/serialport_bin/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - - defaultConfig { - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode rootProject.ext.versionCode - versionName rootProject.ext.versionName - - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { -// compile fileTree(dir: 'libs', include: ['*.jar']) -// androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { -// exclude group: 'com.android.support', module: 'support-annotations' -// }) -// compile 'com.android.support:appcompat-v7:25.3.0' -// testCompile 'junit:junit:4.12' -} - -apply from: '../jitpack.gradle' \ No newline at end of file diff --git a/serialport_bin/proguard-rules.pro b/serialport_bin/proguard-rules.pro deleted file mode 100644 index 415e11f..0000000 --- a/serialport_bin/proguard-rules.pro +++ /dev/null @@ -1,25 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in D:\DevTools\Android\sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/serialport_bin/src/main/AndroidManifest.xml b/serialport_bin/src/main/AndroidManifest.xml deleted file mode 100644 index 808f2ee..0000000 --- a/serialport_bin/src/main/AndroidManifest.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/serialport_bin/src/main/java/android/serialport/SerialPort.java b/serialport_bin/src/main/java/android/serialport/SerialPort.java deleted file mode 100644 index cc189d2..0000000 --- a/serialport_bin/src/main/java/android/serialport/SerialPort.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2009 Cedric Priscal - * - * 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 android.serialport; - -import android.util.Log; -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class SerialPort { - - private static final String TAG = "SerialPort"; - - private static final String DEFAULT_SU_PATH = "/system/bin/su"; - - private static String sSuPath = DEFAULT_SU_PATH; - - /** - * Set the su binary path, the default su binary path is {@link #DEFAULT_SU_PATH} - * - * @param suPath su binary path - */ - public static void setSuPath(String suPath) { - if (suPath == null) { - return; - } - sSuPath = suPath; - } - - /* - * Do not remove or rename the field mFd: it is used by native method close(); - */ - private FileDescriptor mFd; - private FileInputStream mFileInputStream; - private FileOutputStream mFileOutputStream; - - public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { - - /* Check access permission */ - if (!device.canRead() || !device.canWrite()) { - try { - /* Missing read/write permission, trying to chmod the file */ - Process su; - su = Runtime.getRuntime().exec(sSuPath); - String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + "exit\n"; - su.getOutputStream().write(cmd.getBytes()); - if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { - throw new SecurityException(); - } - } catch (Exception e) { - e.printStackTrace(); - throw new SecurityException(); - } - } - - mFd = open(device.getAbsolutePath(), baudrate, flags); - if (mFd == null) { - Log.e(TAG, "native open returns null"); - throw new IOException(); - } - mFileInputStream = new FileInputStream(mFd); - mFileOutputStream = new FileOutputStream(mFd); - } - - public SerialPort(String devicePath, int baudrate, int flags) - throws SecurityException, IOException { - this(new File(devicePath), baudrate, flags); - } - - public SerialPort(File device, int baudrate) throws SecurityException, IOException { - this(device, baudrate, 0); - } - - public SerialPort(String devicePath, int baudrate) throws SecurityException, IOException { - this(new File(devicePath), baudrate, 0); - } - - // Getters and setters - public InputStream getInputStream() { - return mFileInputStream; - } - - public OutputStream getOutputStream() { - return mFileOutputStream; - } - - // JNI - private native static FileDescriptor open(String path, int baudrate, int flags); - - public native void close(); - - static { - System.loadLibrary("serial_port"); - } -} diff --git a/serialport_bin/src/main/java/android/serialport/SerialPortFinder.java b/serialport_bin/src/main/java/android/serialport/SerialPortFinder.java deleted file mode 100644 index 229a984..0000000 --- a/serialport_bin/src/main/java/android/serialport/SerialPortFinder.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2009 Cedric Priscal - * - * 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 android.serialport; - -import android.util.Log; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.LineNumberReader; -import java.util.Iterator; -import java.util.Vector; - -public class SerialPortFinder { - - public class Driver { - public Driver(String name, String root) { - mDriverName = name; - mDeviceRoot = root; - } - - private String mDriverName; - private String mDeviceRoot; - Vector mDevices = null; - - public Vector getDevices() { - if (mDevices == null) { - mDevices = new Vector(); - File dev = new File("/dev"); - - File[] files = dev.listFiles(); - - if (files != null) { - int i; - for (i = 0; i < files.length; i++) { - if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { - Log.d(TAG, "Found new device: " + files[i]); - mDevices.add(files[i]); - } - } - } - } - return mDevices; - } - - public String getName() { - return mDriverName; - } - } - - private static final String TAG = "SerialPort"; - - private Vector mDrivers = null; - - Vector getDrivers() throws IOException { - if (mDrivers == null) { - mDrivers = new Vector(); - LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); - String l; - while ((l = r.readLine()) != null) { - // Issue 3: - // Since driver name may contain spaces, we do not extract driver name with split() - String drivername = l.substring(0, 0x15).trim(); - String[] w = l.split(" +"); - if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { - Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); - mDrivers.add(new Driver(drivername, w[w.length - 4])); - } - } - r.close(); - } - return mDrivers; - } - - public String[] getAllDevices() { - Vector devices = new Vector(); - // Parse each driver - Iterator itdriv; - try { - itdriv = getDrivers().iterator(); - while (itdriv.hasNext()) { - Driver driver = itdriv.next(); - Iterator itdev = driver.getDevices().iterator(); - while (itdev.hasNext()) { - String device = itdev.next().getName(); - String value = String.format("%s (%s)", device, driver.getName()); - devices.add(value); - } - } - } catch (IOException e) { - e.printStackTrace(); - } - return devices.toArray(new String[devices.size()]); - } - - public String[] getAllDevicesPath() { - Vector devices = new Vector(); - // Parse each driver - Iterator itdriv; - try { - itdriv = getDrivers().iterator(); - while (itdriv.hasNext()) { - Driver driver = itdriv.next(); - Iterator itdev = driver.getDevices().iterator(); - while (itdev.hasNext()) { - String device = itdev.next().getAbsolutePath(); - devices.add(device); - } - } - } catch (IOException e) { - e.printStackTrace(); - } - return devices.toArray(new String[devices.size()]); - } -} diff --git a/serialport_bin/src/main/jniLibs/arm64-v8a/libserial_port.so b/serialport_bin/src/main/jniLibs/arm64-v8a/libserial_port.so deleted file mode 100644 index 29dc82c..0000000 Binary files a/serialport_bin/src/main/jniLibs/arm64-v8a/libserial_port.so and /dev/null differ diff --git a/serialport_bin/src/main/jniLibs/armeabi-v7a/libserial_port.so b/serialport_bin/src/main/jniLibs/armeabi-v7a/libserial_port.so deleted file mode 100644 index 1b4efa0..0000000 Binary files a/serialport_bin/src/main/jniLibs/armeabi-v7a/libserial_port.so and /dev/null differ diff --git a/serialport_bin/src/main/jniLibs/armeabi/libserial_port.so b/serialport_bin/src/main/jniLibs/armeabi/libserial_port.so deleted file mode 100644 index c3b9094..0000000 Binary files a/serialport_bin/src/main/jniLibs/armeabi/libserial_port.so and /dev/null differ diff --git a/serialport_bin/src/main/jniLibs/mips/libserial_port.so b/serialport_bin/src/main/jniLibs/mips/libserial_port.so deleted file mode 100644 index 707a321..0000000 Binary files a/serialport_bin/src/main/jniLibs/mips/libserial_port.so and /dev/null differ diff --git a/serialport_bin/src/main/jniLibs/mips64/libserial_port.so b/serialport_bin/src/main/jniLibs/mips64/libserial_port.so deleted file mode 100644 index 642367f..0000000 Binary files a/serialport_bin/src/main/jniLibs/mips64/libserial_port.so and /dev/null differ diff --git a/serialport_bin/src/main/jniLibs/x86/libserial_port.so b/serialport_bin/src/main/jniLibs/x86/libserial_port.so deleted file mode 100644 index a9ec536..0000000 Binary files a/serialport_bin/src/main/jniLibs/x86/libserial_port.so and /dev/null differ diff --git a/serialport_bin/src/main/jniLibs/x86_64/libserial_port.so b/serialport_bin/src/main/jniLibs/x86_64/libserial_port.so deleted file mode 100644 index a9753d6..0000000 Binary files a/serialport_bin/src/main/jniLibs/x86_64/libserial_port.so and /dev/null differ diff --git a/serialport_bin/src/main/res/values/strings.xml b/serialport_bin/src/main/res/values/strings.xml deleted file mode 100644 index 0cd6c33..0000000 --- a/serialport_bin/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - SerialPort - diff --git a/settings.gradle b/settings.gradle index 3f34c65..30d1be0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':serialport', ':serialport_bin', ':sample' +include ':serialport', ':sample'