• android6.0 SerialPort 服务


        一篇博客描述了一个简单的串口应用程序和驱动程序,了解了应用程序访问串口的基本操作,如
    打开串口,设置串口,写串口,读串口,关闭串口等。和Linux串口驱动的基本框架。这里将了解Android
    下的串口系统框架,仍然使用上一篇博客中的tiny_serial.c作为本例的驱动,本例实现的功能不变,无
    论应用程序写任何数据到串口,都能从该串口中读回。
        关于Android系统服务基本框架,可以参考http://www.cnblogs.com/hackfun/p/7612617.html博客,
    Android串口(serial port)服务框架与该例子十分类似,这里作简单描述。当然也有些不同的地方,后面会分析。

    (A) 串口服务的基本框架
        1. 注册驱动
        2. 注册hal
        3. 注册JNI
        4. 注册和添加SerialService
        5. 串口管理SerialManager

    (B) 打开串口
    (C) 设置权限
    (D) 测试串口


    (A) 串口服务的基本框架
    1. 注册驱动
        通过加载kernel/driver/tty/serial/tiny_serial.c驱动,生成/dev/ttytiny0节点,应用通过访问
    该节点,实现的对串口的open、read、write、close等操作。

    2. 注册hal
        在Android串口服务中,省略了hal层,即通过JNI直接访问驱动。

    3. 注册JNI
        通过frameworks/base/services/core/jni/com_android_server_SerialService.cpp和
    frameworks/base/core/jni/android_hardware_SerialPort.cpp两个JNI文件对驱动访问,
    为JAVA提供底层驱动访问的接口,如:

    private native ParcelFileDescriptor native_open(String path);
    
    private native void native_open(FileDescriptor pfd, int speed) throws IOException;
    private native void native_close();
    private native int native_read_array(byte[] buffer, int length) throws IOException;
    private native int native_read_direct(ByteBuffer buffer, int length) throws IOException;
    private native void native_write_array(byte[] buffer, int length) throws IOException;
    private native void native_write_direct(ByteBuffer buffer, int length) throws IOException;
    private native void native_send_break();

    4. 注册和添加SerialService
        frameworks/base/services/core/java/com/android/server/SerialService.java的SerialService类
    中提供了服务端通过JNI放问驱动的接口,如:

    public String[] getSerialPorts()
    public ParcelFileDescriptor openSerialPort(String path)

    通过向frameworks/base/services//java/com/android/server/SystemServer.java的服务管理器ServiceManager
    添加服务:

    serial = new SerialService(context);
    ServiceManager.addService(Context.SERIAL_SERVICE, serial);

    并且,通过frameworks/base/core/java/android/app/SystemServiceRegistry.java

    1 registerService(Context.SERIAL_SERVICE, SerialManager.class,
    2     new CachedServiceFetcher<SerialManager>() {
    3 @Override
    4 public SerialManager createService(ContextImpl ctx) {
    5     IBinder b = ServiceManager.getService(Context.SERIAL_SERVICE);
    6     return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));
    7 }});

    可以访问串口服务SerialService,客户端通过获得SerialService,就能远程调用getSerialPorts()和
    openSerialPort(String path)接口

    5. 串口管理SerialManager
        frameworks/base/core/java/android/hardware/SerialManager.java中SerialManager对串口操作进一步管理,
    客户端只要实例化一个SerialManager对象,使用该对象的方法访问串口。不过,该对象只对
    openSerialPort(String name, int speed)管理。

    (B) 打开串口
        通过以上简单分析Android串口服务框架之后,这里进一步分析串口的打开open操作流程。
        在http://www.cnblogs.com/hackfun/p/7612617.html
    博客中,只有服务端调用JNI访问底层驱动,而在串口服务中,客户端也调用JNI直接访问底层驱动。服务端
    只负责对串口的open操作,在frameworks/base/services/core/jni/com_android_server_SerialService.cpp中

     1 static jobject android_server_SerialService_open(JNIEnv *env, jobject /* thiz */, jstring path)
     2 {
     3     const char *pathStr = env->GetStringUTFChars(path, NULL);
     4     //打开/dev/tty*节点
     5     int fd = open(pathStr, O_RDWR | O_NOCTTY);
     6     if (fd < 0) {
     7         ALOGE("could not open %s", pathStr);
     8         env->ReleaseStringUTFChars(path, pathStr);
     9         return NULL;
    10     }   
    11     env->ReleaseStringUTFChars(path, pathStr);
    12 
    13     jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
    14     if (fileDescriptor == NULL) {
    15         return NULL;
    16     }
    17     //返回文件描述符,用于跨进程访问文件
    18     return env->NewObject(gParcelFileDescriptorOffsets.mClass,
    19         gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
    20 }

    而read、write、close等相关操作在frameworks/base/core/jni/android_hardware_SerialPort.cpp中,
    其中android_hardware_SerialPort_open只对串口进行相关设置,如波特,数据位等。由此看出
    frameworks/base/services/core/jni/com_android_server_SerialService.cpp和
    frameworks/base/core/jni/android_hardware_SerialPort.cpp可能处于两个不同的线程中,这两个不
    同的线程对同一个文件进行访问,需要对文件描述符进行转换。
    在frameworks/base/core/jni/android_hardware_SerialPort.cpp中

     1 static void android_hardware_SerialPort_open(JNIEnv *env, jobject thiz, jobject fileDescriptor, jint speed)
     2 {
     3     ......
     4     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     5     // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy
     6     fd = dup(fd);
     7     if (fd < 0) {
     8         jniThrowException(env, "java/io/IOException", "Could not open serial port");
     9         return;
    10     }
    11     ......
    12 }

    (C) 设置权限

    device/sprd/scx35l/common/rootdir/root/ueventd.sc8830.rc

    1 ......
    2 /dev/ttytiny0             0660     system  system
    3 ......

    device/sprd/scx35l/common/sepolicy/file_contexts

    1 ......
    2 /dev/ttytiny0        u:object_r:serial_device:s0
    3 ......

    device/sprd/scx35l/common/sepolicy/system_app.te

    ......
    allow system_app serial_device:chr_file { open read write ioctl};
    ......


    (D) 测试串口
        这里引用Android6.0源码目录下的一个串口测试app,还要添加一些权限等设置才能正常使用。
    串口测试APP源码:frameworks/base/tests/SerialChat

    1. 设置权限:

    frameworks/base/tests/SerialChat/Android.mk

    1 ......
    2 LOCAL_CERTIFICATE := platform
    3 ......

    frameworks/base/tests/SerialChat/AndroidManifest.xml

    1 ......
    2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    3     package="com.android.serialchat"
    4     android:sharedUserId="android.uid.system">
    5 ......

    2. 添加串口节点名称
        应用程序根据这个名称来打开对应的串口。

    frameworks/base/core/res/res/values/config.xml

    1 ......
    2 <string-array translatable="false" name="config_serialPorts">
    3            <item>"/dev/ttytiny0"</item>
    4 </string-array>
    5 ......

        用mmm frameworks/base/tests/SerialChat -B 编译出的SerialChat.apk push到机器之后,就可以
    进行测试了。

        a.点击打开SerialChat.apk:

           

        

        b.输入要发送的内容

           

        c.点击确定(打钩的位置)发送,并且接收显示在上方。

           

  • 相关阅读:
    【刷题-LeetCode】165 Compare Version Numbers
    python 22 内置模块2
    python 21 内置模块
    python 20 模块,包,及开发目录规范
    python 19
    python 18 三元,生成,递推
    定时抓取数据并存入数据库
    抓取财报数据
    金币
    交换座位
  • 原文地址:https://www.cnblogs.com/hackfun/p/7683512.html
Copyright © 2020-2023  润新知