Android11.0 encapsulates the serial port API for app layer calling

RK3568 Android11.0 serial port service

Android11.0 has its own serial port service. The official demo is in frameworks/base/tests/SerialChat

Encapsulate serial port service in custom service

Insert image description here

As shown in the above directory, encapsulate SerialManager and 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. Modify SerialService /frameworks/base/services/core/java/com/android/server/SerialService.java;
    The SerialService service only allows system app calls by default, and third-party application calls , you need to delete the permission check statement;
    The default serial port setting number is obtained from the config_serialPorts array defined in the /frameworks/base/core/res/values/config.xml file, and is modified here to be called by the upper layer or passed in;

    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. Modify android_hardware_SerialPort /frameworks/base/core/jni/android_hardware_SerialPort.cpp;
    The default serial port read method waits for reading the minimum character mode. In this mode, when the serial port is closed, the read method The thread cannot exit immediately. After obtaining the serial port for the first time, the read method will report an error and needs to be changed to read timeout mode;
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. Add a method to obtain the serial port interface object in the custom service:
public ISerial createSerial() {
    
    
       return mSerialHelper.createSerial();
   }
  1. Modify hiddenapi-greylist-packages.txt,
    /frameworks/base/config/hiddenapi-greylist-packages.txt,
    Added by default Classes and methods are hidden APIs and cannot be called directly by upper-layer applications. You need to add the corresponding package name to the file:
com.yjz.demo//只包括包下的类文件,子包不包含,需要单独声明,如下
com.yjz.demo.hardware.serial

Add permissions

  1. Add SEAndroid permissions, file path:
    /device/rockchip/rk356x/sepolicy_vendor/serial.te
allow untrusted_app serial_device:chr_file {
    
     open read write ioctl};
  1. Add permissions for the serial device, file path:
    /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

Add upper-layer application calling API in SDK

The SDK is finally compiled into a jar package, which can be imported by the application and called related APIs.
Insert image description here

The directories in the SDK are as above:

  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);
    }
}

Guess you like

Origin blog.csdn.net/yjz_0314/article/details/131647591