Android11.0封装串口api供app层调用

RK3568 Android11.0串口服务

Android11.0已自带串口服务,官方demo在frameworks/base/tests/SerialChat

自定义服务中封装串口服务

在这里插入图片描述

如上目录,对SerialManager、SerialPort进行封装:

  1. 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);
   }
}
  1. /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();
}
  1. /hardware/serial/SerialCallback.java
public interface SerialCallback {
    
    
    void onRead(byte[] buffer);
}
  1. /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;
    }
}

  1. 修改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);
    }
  1. 修改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;
   //*****省略代码******
}
  1. 自定义服务中添加获取串口接口对象的方法:
public ISerial createSerial() {
    
    
       return mSerialHelper.createSerial();
   }
  1. 修改hiddenapi-greylist-packages.txt,
    /frameworks/base/config/hiddenapi-greylist-packages.txt,
    默认添加的类及方法都是隐藏api,上层应用无法直接调用,需要在文件中添加相应包名:
com.yjz.demo//只包括包下的类文件,子包不包含,需要单独声明,如下
com.yjz.demo.hardware.serial

添加权限

  1. 添加SEAndroid权限,文件路径:
    /device/rockchip/rk356x/sepolicy_vendor/serial.te
allow untrusted_app serial_device:chr_file {
    
     open read write ioctl};
  1. 为串口设备添加权限,文件路径:
    /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中目录如上:

  1. /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;
    }
}

  1. /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();
}
  1. /sdk/api/serial/SerialListener.java
public interface SerialListener {
    
    
    void onRead(byte[] buffer);
}
  1. /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);
    }
}

猜你喜欢

转载自blog.csdn.net/yjz_0314/article/details/131647591