Android11.0封装串口api供app层调用
RK3568 Android11.0串口服务
Android11.0已自带串口服务,官方demo在frameworks/base/tests/SerialChat
自定义服务中封装串口服务
如上目录,对SerialManager、SerialPort进行封装:
- hardware/SerialHelper.java
import android.content.Context;
import android.hardware.SerialPort;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.hardware.SerialManager;
import android.hardware.SerialPort;
import android.os.ServiceManager;
import android.util.ArrayMap;
import android.hardware.ISerialManager;
import android.util.Log;
import java.io.IOException;
import com.yjz.demo.hardware.serial.SerialImpl;
import com.yjz.demo.hardware.serial.ISerial;
public final class SerialHelper {
private static final String TAG = SerialHelper.class.getSimpleName();
private Context mContext;
private SerialManager mSerialManager;
public SerialHelper(Context context) {
this.mContext = context;
mSerialManager = (SerialManager) context.getSystemService(Context.SERIAL_SERVICE);
}
public ISerial createSerial() {
return new SerialImpl(mSerialManager);
}
}
- /hardware/serial/ISerial.java;
import java.io.IOException;
public interface ISerial {
void open(String name, int speed, SerialCallback callback) throws IOException;
void write(byte[] buffer);
void close();
void release();
}
- /hardware/serial/SerialCallback.java
public interface SerialCallback {
void onRead(byte[] buffer);
}
- /hardware/serial/SerialImpl.java
import android.hardware.SerialManager;
import android.hardware.SerialPort;
import android.util.Log;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
public final class SerialImpl implements ISerial {
private static final String TAG = SerialImpl.class.getSimpleName();
private String mName;
private int mSpeed;
private SerialCallback mCallback;
private SerialPort mSerialPort;
private SerialManager mSerialManager;
private volatile boolean mIsClose;
public SerialImpl(SerialManager serialManager) {
mSerialManager = serialManager;
}
@Override
public void open(String name, int speed, SerialCallback callback) throws IOException {
mCallback = callback;
mSerialPort = mSerialManager.openSerialPort(name, speed);
if (null == mSerialPort) {
throw new IOException("Could not open serial port " + name);
}
Log.d(TAG, "serial open success");
mIsClose = false;
new Thread() {
@Override
public void run() {
Log.d(TAG, "serial read thread run " + Thread.currentThread().getName());
int ret = 0;
ByteBuffer inputBuffer = ByteBuffer.allocate(1024);
byte[] buffer = new byte[1024];
while (ret >= 0) {
if (mIsClose) {
Log.d(TAG, "serial had close " + Thread.currentThread().getName());
break;
}
try {
// Log.d(TAG, "calling read");
inputBuffer.clear();
ret = mSerialPort.read(inputBuffer);
// Log.d(TAG, "serial read returned " + ret + " " + Thread.currentThread().getName());
inputBuffer.get(buffer, 0, ret);
byte[] rtArray = Arrays.copyOf(buffer, ret);
if (null != mCallback && ret > 0) {
mCallback.onRead(rtArray);
}
} catch (Exception e) {
Log.w(TAG, "serial read failed " + Thread.currentThread().getName(), e);
break;
}
}
Log.d(TAG, "serial read thread out" + Thread.currentThread().getName());
}
}.start();
}
@Override
public void write(byte[] buffer) {
if (null != buffer && buffer.length > 0) {
ByteBuffer outputBuffer = ByteBuffer.allocate(buffer.length);
outputBuffer.put(buffer);
if (null != mSerialPort) {
try {
mSerialPort.write(outputBuffer, buffer.length);
} catch (Exception e) {
Log.e(TAG, "serial write fail ", e);
}
} else {
Log.d(TAG, "serial had close");
}
} else {
Log.d(TAG, "serial no write data");
}
}
@Override
public void close() {
Log.d(TAG, "serial close");
mIsClose = true;
if (mSerialPort != null) {
try {
mSerialPort.close();
} catch (IOException e) {
e.printStackTrace();
}
mSerialPort = null;
}
if (null != mCallback) {
mCallback = null;
}
}
@Override
public void release() {
Log.d(TAG, "serial release");
close();
mSerialManager = null;
}
}
- 修改SerialService /frameworks/base/services/core/java/com/android/server/SerialService.java;
SerialService服务默认只允许系统app调用,第三方应用调用,需要删除权限检查语句;
默认串口设置号是从/frameworks/base/core/res/values/config.xml文件中定义的config_serialPorts数组中获取,这里修改成上层调用者传入;
public String[] getSerialPorts() {
// 注释掉权限检查语句
// mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);
ArrayList<String> ports = new ArrayList<String>();
for (int i = 0; i < mSerialPorts.length; i++) {
String path = mSerialPorts[i];
if (new File(path).exists()) {
ports.add(path);
}
}
String[] result = new String[ports.size()];
ports.toArray(result);
return result;
}
public ParcelFileDescriptor openSerialPort(String path) {
// 注释掉权限检查,设备号使用传入参数
// mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);
// for (int i = 0; i < mSerialPorts.length; i++) {
// if (mSerialPorts[i].equals(path)) {
// return native_open(path);
// }
// }
// throw new IllegalArgumentException("Invalid serial port " + path);
return native_open(path);
}
- 修改android_hardware_SerialPort /frameworks/base/core/jni/android_hardware_SerialPort.cpp;
默认串口的read方法等待读取最少字符模式,此模式下当关闭串口时,读线程无法立即退出,在次获取串口后,读方法会报一次错,需要修改为读取超时模式;
static void
android_hardware_SerialPort_open(JNIEnv *env, jobject thiz, jobject fileDescriptor, jint speed)
//*****省略代码******
/* no timeout but request at least one character per read */
//默认为:
//tio.c_cc[VTIME] = 0;
//tio.c_cc[VMIN] = 1;
//修改为:
tio.c_cc[VTIME] = 1;//1 * 100毫秒
tio.c_cc[VMIN] = 0;
//*****省略代码******
}
- 自定义服务中添加获取串口接口对象的方法:
public ISerial createSerial() {
return mSerialHelper.createSerial();
}
- 修改hiddenapi-greylist-packages.txt,
/frameworks/base/config/hiddenapi-greylist-packages.txt,
默认添加的类及方法都是隐藏api,上层应用无法直接调用,需要在文件中添加相应包名:
com.yjz.demo//只包括包下的类文件,子包不包含,需要单独声明,如下
com.yjz.demo.hardware.serial
添加权限
- 添加SEAndroid权限,文件路径:
/device/rockchip/rk356x/sepolicy_vendor/serial.te
allow untrusted_app serial_device:chr_file {
open read write ioctl};
- 为串口设备添加权限,文件路径:
/device/rockchip/rk356x/init.rk356x.rc
on boot
//****省略代码
chmod 0777 /dev/ttyS3
chmod 0777 /dev/ttyS4
chmod 0777 /dev/ttyS5
chmod 0777 /dev/ttyS7
chmod 0777 /dev/ttyS8
chmod 0777 /dev/ttyS9
SDK中添加上层应用调用API
SDK最终编译成jar包,供应用导入调用相关api。
SDK中目录如上:
- /sdk/api/serial/ImplSerial.java
import com.yjz.demo.DemoManager;
import java.io.IOException;
public final class ImplSerial implements ISerial {
private static final String TAG = ImplSerial.class.getSimpleName();
private String mName;
private int mSpeed;
private com.yjz.demo.hardware.serial.ISerial mISerial;
public ImplSerial(DemoManager manager) {
mISerial = manager.createSerial();
}
@Override
public void open(String name, int speed, SerialListener listener) throws IOException {
mISerial.open(name, speed, new com.yjz.demo.hardware.serial.SerialCallback() {
@Override
public void onRead(byte[] buffer) {
if (null != listener) {
listener.onRead(buffer);
}
}
});
}
@Override
public void write(byte[] buffer) {
mISerial.write(buffer);
}
@Override
public void close() {
mISerial.close();
}
@Override
public void release() {
mISerial.release();
mISerial = null;
}
}
- /sdk/api/serial/Iserial.java
import java.io.IOException;
public interface ISerial {
void open(String name, int speed, SerialListener listener) throws IOException;
void write(byte[] buffer);
void close();
void release();
}
- /sdk/api/serial/SerialListener.java
public interface SerialListener {
void onRead(byte[] buffer);
}
- /sdk/api/ImplIApi.java
import android.content.Context;
import com.yjz.demo.DemoManager;
import com.yjz.sdk.api.gpio.Gpio;
import com.yjz.sdk.api.serial.ISerial;
import com.yjz.sdk.api.serial.ImplSerial;
final class ImplIApi implements IApi {
private DemoManager mDemoManager;
public ImplIApi(Context context) {
mDemoManager = (DemoManager) context.getSystemService(Context.DEMO_SERVICE);
}
@Override
public Gpio createGpio(int gpioNum) {
return new Gpio(mDemoManager, gpioNum);
}
@Override
public boolean setRelay(int type, String value) {
return mDemoManager.writeRelayValue(type, value);
}
@Override
public ISerial createSerial() {
return new ImplSerial(mDemoManager);
}
}