0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Android APP如何进行访问硬件驱动

CHANBAEK 来源:飞车侠 作者: Pual Lin 2023-12-04 13:50 次阅读

本文我们要讲的是在用 i.MX8 平台开发时,Android APP 如何进行访问硬件驱动。

主要内容包括:框架代码编写和注意事项(Linux Driver、C Library、SystemServer、APP 等五个部分)、Sepolicy 信息配置,话不多说,我们直接进入正题吧~

一、Android 应用层访问硬件服务框架

图片

Android 应用层访问硬件服务框架主要可以分为:① APP;② SystemServer;③ C Library;④ Linux Driver。

① APP:

从 service_manager 里获取相关服务,再通过接口调用,接口里实现对本地方法的调用。

涉及到的相关文件:MainActivity.java、activity_main.xml、ILedService.aidl、LedService.java

② SystemServer:

通过注册 android 服务的方式加载 C 库,再将服务加入 service_manager 里面。

涉及到的相关文件:SystemServer.java、SystemServiceRegistry.java、LedManager.java

③ C Library:

HAL 层操作 /dev/myled,JNI 层加载 HAL 文件和向上注册 java 本地方法。

涉及到的相关文件:onload.cpp、com_android_server_LedService.cpp、led_hal.c、led_hal.h

④ Linux Driver

和 Linux 驱动完全一样,编写驱动,向上提供 /dev/myled 节点。

涉及到的相关文件:myled.c

二、框架代码编写

2.1 编写 Linux Driver

通过 Linux Driver 来实现从应用层到底层的控制,这里主要实现对 LED 灯的 ON/OFF 控制及 GPIO 引脚电平 HIGH/LOW 的上报。

在 vendor/nxp-opensource/kernel_imx/drivers/leds 目录下创建 myled.c

#include < linux/module.h >
#include < linux/kernel.h >
#include < linux/fs.h >
#include < linux/init.h >
#include < linux/delay.h >
#include < asm/uaccess.h >
#include < asm/irq.h >
#include < asm/io.h >
#include < linux/device.h >
#include < linux/uaccess.h >


#include < linux/platform_device.h >


#include < linux/cdev.h >
#include < linux/slab.h >                          /*kmalloc*/
#include < linux/vmalloc.h >                      /*vmalloc*/
#include < linux/types.h >                         /*ssize_t*/
#include < linux/gpio_keys.h >
#include < linux/gpio.h >
#include < linux/interrupt.h >
#include < linux/sched.h >
#include < asm-generic/ioctl.h >
#include < asm-generic/errno-base.h >


#define USER_LED          374    /*GPIO LED  GPIO4_22*/
#define USER_BUTTON      357    /*GPIO LED  GPIO4_05*/
int major;
static struct class *myled_class;
static struct class_device  *myled_class_devs;


static int myled_drv_open(struct inode *inode, struct file *file)
{
  printk("[pual] Enter %s - %d -- n",__func__,__LINE__);


  return 0;
}


static long myled_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{  
  int read_val;
  printk("gpio_ioctln");
  switch(cmd) {


        case 0:
      if(arg == 0)
      {
        gpio_set_value(USER_LED, 0);
        printk("led is offn");
        break;
      }else
        {
                   gpio_set_value(USER_LED, 1);
                    printk("led is onn");
                    break;
        }
  case 1:      
       read_val = gpio_get_value(USER_BUTTON);
       if(copy_to_user((int __user*)arg,&read_val,sizeof(read_val)))
       return ENOTTY;

       printk("gpio input: %dn", read_val);

              break;
        default:
                return -EINVAL;
  }
  return 0;
}


static struct file_operations myled_drv_fops = {
    .owner = THIS_MODULE,
    .open = myled_drv_open,
    .unlocked_ioctl = myled_drv_ioctl,
};


static  int myled_probe(struct platform_device *pdev)
{
  int ret;
  printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
  ret = gpio_request(USER_LED, "LED");//第一个参数,为要申请的引脚,第二个为你要定义的名字
  if (ret) 
  {
            return ret;
  }
  printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
  gpio_direction_output(USER_LED, 1);
  printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
  gpio_set_value(USER_LED, 1);
  printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
  major = register_chrdev(0,"myled",&myled_drv_fops);

      //创建设备信息,执行后会出现 /sys/class/myled
      myled_class = class_create(THIS_MODULE, "myled");


  //创建设备节点,就是根据上面的设备信息来的
      myled_class_devs = device_create(myled_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */
  return 0;  
}


static int myled_remove(struct platform_device *pdev)
{
  gpio_free(USER_LED);
  unregister_chrdev(major,"myled");
  device_unregister(myled_class_devs);
  class_destroy(myled_class);
  return 0;
}


static const struct of_device_id myled_ids[] = {

  { .compatible = "fsl,myled", },
  { },
};


MODULE_DEVICE_TABLE(of, myled_ids);


/*1. 构建platform_driver 结构体*/
static struct platform_driver myled_driver={
  .probe  = myled_probe,
  .remove = myled_remove,
  .driver = {
    .name  = "myled",
    .of_match_table = myled_ids,
  }
};


static int __init myled_init(void)
{
  /*2. 注册平台驱动*/
  platform_driver_register(&myled_driver);
  printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
  return 0;
}


static void __exit myled_exit(void)
{
  /*3. 注销平台驱动*/
  platform_driver_unregister(&myled_driver);
}
module_init(myled_init);
module_exit(myled_exit);


/* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("WPI Pual Lin");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("i.MX8 LED Driver");
MODULE_LICENSE("GPL");

修改 vendor/nxp-opensource/kernel_imx/drivers/leds 目录下 Makefile 及 Kconfig,添加代码

Makefile

obj-$(CONFIG_MYLED)                     += myled.o

Kconfig

config MYLED
         tristate "MYLED support"
         depends on LEDS_CLASS
         help
           This option enables support for userspace LEDs. Say 'y' to enable this
           support in kernel. To compile this driver as a module, choose 'm' here:
           the module will be called uleds.

修改默认配置文件 vendor/nxp-opensource/kernel_imx/arch/arm64/configs/android_defconfig,添加代码

CONFIG_MYLED=y

由于这里给的值是 y,会把我们的 driver 编译进内核,不会生成 ko 文件,也不需要我们去手动挂载。注意,必须是给 y,因为我们后续会把 led 添加进 system server 里面,会在系统启动的时候就调用到 open 来打开 /dev/myled。

执行如下命令,开始编译 kernel 内核

source build/envsetup.sh 
lunch mek_8q-eng
make bootimage -j16

2.2 编写 C Library

2.2.1 修改 onload.cpp

SystemServer.java 会加载 C 库,调用到 onload.cpp,需要在 onload.cpp 注册 LED 服务。
修改 frameworks/base/services/core/jni/onload.cpp,添加:

int register_android_server_LedService(JNIEnv* env);
……
register_android_server_LedService(env);

2.2.2 创建 com_android_server_LedService.cpp

前面用到了 register_android_server_LedService(),是在 com_android_server_LedService.cpp 里实现的。

com_android_server_LedService.cpp 理论上可以直接操作节点 /dev/myled,但一般不这样做。

通常的做法是,向上提供本地方法 (native_ledOpen),向下加载 HAL 文件 (led_hal.c),并调用 HAL 的函数。

这样操作有两个好处:

  1. 方便修改;

如果需要修改硬件部分的操作,只需要修改 led_hal.c,生成 so 文件,放入系统即可,而不需要编译整个 Android 系统;

  1. 保密代码;

因为 Linux 的 GPL 协议,一旦使用的内核代码,自己的代码也得开源出去,硬件厂商为了保密硬件的具体细节,只在内核实现操作寄存器的接口,具体的操作逻辑放在 HAL 文件里,而 Android 采用 Apache 协议,修改了代码而无需开源,这样就实现了保密代码;

编写步骤如下:

  1. 定义 JNINativeMethod,建立 Java 本地方法与 C 库函数名的映射关系;
  2. 使用 jniRegisterNativeMethods 注册本地方法,将在 onload.cpp 被调用;
  3. 在 open() 里:

① 使用 hw_get_module 获得 hw_module_t 结构体;

② 使用 module->methods->open 获得 hw_device_t 结构体;

③ 将 hw_device_t 转换为 led_device_t,调用对应 open;

  1. 完成其它函数 ctrl、close 的调用;

在 frameworks/base/services/core/jni/ 路径下创建 com_android_server_LedService.cpp

/*/frameworks/base/services/core/jni/*/
#define LOG_TAG "LedService"


#include "jni.h"
#include < nativehelper/JNIHelp.h >
#include "android_runtime/AndroidRuntime.h"
#include < utils/misc.h >
#include < utils/Log.h >
#include < stdio.h >
#include < sys/types.h >
#include < sys/stat.h >
#include < fcntl.h >
#include < sys/ioctl.h >
#include < hardware/led_hal.h >


namespace android
{
    static led_device_t* led_device;


    jint ledOpen(JNIEnv *env, jobject cls)
{
        jint err;
        hw_module_t* module;
        hw_device_t* device;


        ALOGI("[pual] native ledOpen");


        //1. hw_get_module for get module
        err = hw_get_module("led", (hw_module_t const**)&module);
        ALOGE("[pual] native ledOpen");
        if (err == 0) {
            //2. module- >methods- >open for get device 
            err = module- >methods- >open(module, NULL, &device);
            if (err == 0) {
                //3. conversion, call led_open
                led_device = (led_device_t *)device;
                return led_device- >led_open(led_device);
            } else {
                return -1;
            }
        }
        return -1;
    }


    void ledClose(JNIEnv *env, jobject cls)
{
        ALOGI("[pual] nativeled Close");

        return;
    }


    jint ledCtrl(JNIEnv *env, jobject cls, jint number, jint status)
{
        ALOGI("[pual] native ledCtrl %d, %d", number, status);
        return led_device- >led_ctrl(led_device, number, status);
    }


    static const JNINativeMethod method_table[] = {
        {"native_ledOpen",  "()I",   (void *)ledOpen}, 
        {"native_ledClose", "()V",   (void *)ledClose}, 
        {"native_ledCtrl",  "(II)I", (void *)ledCtrl}, 
    };


    int register_android_server_LedService(JNIEnv *env)
{
        return jniRegisterNativeMethods(env, "com/android/server/LedService",
                method_table, NELEM(method_table));
    }
};

修改 frameworks/base/services/core/jni/Android.bp,添加

"com_android_server_LedService.cpp",

执行 mmm frameworks/base/services 编译 com_android_server_LedService.cpp 和 onload.cpp

2.2.3 创建 HAL 文件:led_hal.c 和 led_hal.h

在 hardware/libhardware/modules 目录下新建一个目录 test_led

在 hardware/libhardware/modules/test_led 目录下创建 led_hal.c

#include < hardware/hardware.h >
#include < cutils/log.h >
#include < stdio.h >
#include < unistd.h >
#include < fcntl.h >
#include < errno.h >
#include < stdlib.h >
#include < sys/types.h >
#include < sys/stat.h >
#include < fcntl.h >
#include < sys/ioctl.h >
#include < utils/Log.h >
#include < hardware/led_hal.h >


//#define LOG_TAG "LED_HAL"


static int fd;


static int led_open(struct led_device_t* dev __unused)
{
    fd = open("/dev/myled", O_RDWR);
    ALOGI("led_open : %d", fd);
    if (fd >= 0)
        return 0;
    else
        return -1;
}


static int led_ctrl(struct led_device_t* dev __unused, int number, int status)
{
    int read_val,ret = 0;  
    switch(number)
    {
        case 0:        
            ret = ioctl(fd, 0, status);   
           ALOGI("[pual] led_write : status = %d, ret = %d", status, ret);  
            return ret;
    case 1:
        ioctl(fd,1,&read_val);  
        ALOGI("[pual] led_read : read_val = %d", read_val);  
        return read_val;
    default:
        return -1;
    }
}


static int led_close(struct hw_device_t* device __unused)
{
    close(fd);
    ALOGI("led_close : %d", fd);
    return 0;
}


static struct led_device_t myled_dev = {
    .common = {
        .tag   = HARDWARE_DEVICE_TAG,
        .close = led_close,
    },
    .led_open  = led_open,
    .led_ctrl  = led_ctrl,
};


static int led_device_open(const struct hw_module_t* module __unused, const char* id __unused,
        struct hw_device_t** device)
{
    //return by id
    *device = (hw_device_t*) &myled_dev; 
    return 0;
}


static struct hw_module_methods_t led_module_methods = {
    .open = led_device_open,
};


struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .id = "led",
    .methods = &led_module_methods,
};

在目录 hardware/libhardware/include/hardware 下创建 led_hal.h

#ifndef ANDROID_LED_INTERFACE_H    
#define ANDROID_LED_INTERFACE_H  
#include < stdint.h >   
#include < sys/cdefs.h >   
#include < sys/types.h > 
#include < hardware/hardware.h >   


__BEGIN_DECLS   


struct led_device_t  {  
    struct hw_device_t common;  
    int (*led_open) (struct led_device_t* dev);
    int (*led_ctrl) (struct led_device_t* dev, int number, int status);
};  

__END_DECLS  

#endif

在 hardware/libhardware/modules/test_led 目录下创建 Android.mk

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)


LOCAL_MODULE := led.default


LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := led_hal.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := eng


include $(BUILD_SHARED_LIBRARY)

执行 mmm hardware/libhardware/modules/test_led 编译 led_hal.c

编译完成后会在 out/target/product/mek_8q/obj_arm/SHARED_LIBRARIES/led.default_intermediates 目录下生成 so 文件 led.default.so

2.3 编写 SystemServer

2.3.1 修改 SystemServer.java

SystemServer.java 主要做两件事,一是加载 C 库,二是使用 addService 将 LED 服务加入 service_manager 里面。
加载 C 库这个是调用 onload.cpp,这个前面已经修改 onload.cpp,这里就不需要改了。
修改frameworks/base/services/java/com/android/server/SystemServer.java,添加:

traceBeginAndSlog("StartLedService");
    ServiceManager.addService("led", new LedService());
    traceEnd();

2.3.2 创建 LedManager.java

在目录 frameworks/base/core/java/android/app 下,创建 LedManager.java

package android.app;


import android.content.Context;
import android.os.ILedService;
import android.os.RemoteException;


public class LedManager {
    ILedService mService;
    public LedManager(Context ctx,ILedService service){
        mService = service;
    }


    public void ledCtrl(int number, int status){
        try {
            mService.ledCtrl(number, status);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
      }
}

2.3.3 修改 SystemServiceRegistry.java

在 SystemServiceRegistry.java 中注册我们的 LedManager Service 服务。

修改 frameworks/base/core/java/android/app/ SystemServiceRegistry.java,在 static 块中添加

registerService(Context.LED_SERVICE, LedManager.class,
              new CachedServiceFetcher() {
            @Override
            public LedManager createService(ContextImpl ctx) {
                IBinder b = ServiceManager.getService(Context.LED_SERVICE);
                ILedService service = ILedService.Stub.asInterface(b);
                return new LedManager(ctx, service);
            }});

这里面的 LED_SERVIC,需要在frameworks/base/core/java/android/content/Context.java 中声明

@StringDef(suffix = { "_SERVICE" }, value = {
    LED_SERVICE,
    …
})
…
public static final String LED_SERVICE="led";

2.4 编写 APP

2.4.1 创建 AIDL

AIDL(Android Interface Definition Language),即 Android 接口定义语言,顾名思义就是定义接口,提供给 APP 使用。

frameworks/base/core/java/android/os/ 目录下新建:ILedService.aidl :

package android.os;


interface ILedService {
    int ledCtrl(int number, int status);
}

frameworks/base 目录下修改:Android.bp,添加:

"core/java/android/os/ILedService.aidl",

在 android_build 目录下执行编译

mmm frameworks/base/

编译完成后将会自动生成:

out/soong/.intermediates/frameworks/base/framework/android_common/gen/aidl/frameworks/base/core/java/android/os/ILedService.java。

2.4.2 创建 LedService.java

前面生成了 ILedService.java,需要实现 ILedService 接口的成员函数。

在 frameworks/base/services/core/java/com/android/server/ 路径下,创建 LedService.java

/*frameworks/base/services/core/java/com/android/server/*/
package com.android.server;
import android.os.ILedService;
import android.util.Slog;


public class LedService extends ILedService.Stub {
private static final String TAG = "ILedService";

  //Call native c function to access hardware
  public int ledCtrl(int number, int status) throws android.os.RemoteException 
{
    Slog.i(TAG, "[pual] enter ledCtrl.");
    return native_ledCtrl(number, status);
  }

  public LedService() {
    Slog.i(TAG, "[pual] enter native_ledOpen.");
    native_ledOpen();
  }

  //Function declaration
  public static native int native_ledCtrl(int number, int status);
  public static native int native_ledOpen();
  public static native void native_ledClose();
}

可以看到 LedService.java 继承于 ILedService,并调用本地方法实现了成员函数。并在构造函数里调用 native_ledOpen()。

其 Android.mk 自动包含了所有 java 文件,不需要修改 Android.mk。

执行完以上步骤后就可以执行 make -j16 命令开始编译

2.4.3 创建 APP 工程

① 打开 Android Studio,选择 “Start a new Android Studio project” 创建一个新 APP 工程;

图片

② 然后选择 “Empty Activity” 空主界面,点击下一步;

图片

③ 最后设置 APP 信息、保存路径、选择语言、兼容API版本、勾选支持安装 apps,点击完成。

图片

等待自动创建完成后,会自动生成工程文件,生成的工程文件中会包含MainActivity.java 和 activity_main.xml,appsrcmainreslayoutactivity_main.xml,界面控件布局文件,既可通过图形界面设计控件,也可直接编辑 xml;

appsrcmainjavacomexampleled_demoMainActivity.java,实现控件各种具体功能,逻辑关系。

MainActivity.java

package com.example.led_demo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.view.View;
import android.os.ILedService;
import android.os.ServiceManager;
//import static android.os.ServiceManager.getService;


public class MainActivity extends AppCompatActivity {


    private boolean ledStatus = false;
    private int gpiostatus;
    private final String TAG="MainActivity";
    private Button button1,button2 = null;
    private ILedService iLedService = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG,"[pual] AppService onCreate");
        initView();
    }


    private void initView() {
        iLedService =ILedService.Stub.asInterface(ServiceManager.getService("led"));
        if(iLedService==null)
            Log.e(TAG,"iLedService = null");
        else
            Log.e(TAG,"iLedService not null");


        button1 = (Button) findViewById(R.id.BUTTON1);
        button1.setOnClickListener(new MyButtonListener1());


        button2 = (Button) findViewById(R.id.BUTTON2);
        button2.setOnClickListener(new MyButtonListener2());
    }


    class MyButtonListener1 implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            ledStatus = !ledStatus;
            if (ledStatus) {
                button1.setText("LED OFF");
                try {
                        iLedService.ledCtrl(0, 0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            } else {
                button1.setText("LED  ON");
                try {
                        iLedService.ledCtrl(0, 1);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    class MyButtonListener2 implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            try {
                gpiostatus = iLedService.ledCtrl(1, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            if (gpiostatus == 1) {
                button2.setText("HIGH");
            } else {
                button2.setText("LOW");
                }
            }
        }
    }

activity_main.xml

< ?xml version="1.0" encoding="utf-8"? >
< androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >


    < LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >


        < TextView
            android:id="@+id/TEXT"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:layout_marginTop="30dp"
            android:text="Android access hardware"
            android:textColor="#008577"
            android:textSize="25sp"
            android:textStyle="bold" / >


        < Button
            android:id="@+id/BUTTON1"
            android:layout_width="50pt"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:layout_gravity="center"
            android:text="LED ON" / >


        < Button
            android:id="@+id/BUTTON2"
            android:layout_width="50pt"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:layout_gravity="center"
            android:text="HIGH" / >


    < /LinearLayout >


< /androidx.constraintlayout.widget.ConstraintLayout >

由于这里会调用到我们前面定义好的 ledctrl 函数接口,但是我们还没有把对应的库导进来,需要执行如下操作:

将 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar 放在 Windows 里。
在 Android Studio 里,选择 File->Project Structure,此时弹出配置界面,点击 Modules 窗口左上角的 +,选择 Import .JAR/.AAR Package,选择刚才的 classes.jar。

切换到 Dependencies 选项卡,选择 app,点击 Declared Dependencies 窗口左上角的 + 号,选择 3 Module dependency,在弹出的界面选择刚才添加的 classes,最后点击 OK。

图片

以上操作后,会在 APP 工程根目录下生成 classes 文件夹,里面就包含了 classes.jar。

完成后可以点击 Build -> Build Bundle(s) / APK(s) -> Build APK(s) 开始编译

图片

2.4.4 APK 安装

上面编译生成的 APK 名称为 :app-debug.apk,我们需要连接好开发板的 USB TypeC 接口,在 Windows 终端输入如下命令开始安装:

adb install app-debug.apk

安装完成后,打开 APK 即可看到如下界面:

图片

三、Sepolicy 权限配置

3.1 denied error

在完成各层代码添加后,还需要添加 Sepolicy 权限,否则系统在启动的时候回出现类似如下报错

avc: denied { find } for service=led pid=4363 uid=10067 scontext=u:r:untrusted_app:s0:c67,c256,c512,c768 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=0


avc: denied { module_load } for pid=4566 comm="insmod" path="/data/pual/myled.ko" dev="mmcblk0p12" ino=221 scontext=u:r:su:s0 tcontext=u:object_r:system_data_file:s0 tclass=system permissive=1

我们需要在 device/fsl/imx8q/sepolicy/system_server.te 中添加:

allow system_server default_android_service:service_manager { add }; 
allow untrusted_app default_android_service:service_manager { find };
allow su system_data_file:system { module_load };

添加完后编译,出现了如下报错:

libsepol.report_failure: neverallow on line 1314 of system/sepolicy/public/domain.te (or line 10945 of policy.conf) violated by allow su system_data_file:system { module_load };

修改以下目录中的 domain.te,

system/sepolicy/public/domain.te

system/sepolicy/prebuilts/api/28.0/public/domain.te

将 1314 行代码注释如下:

#neverallow * ~{ system_file vendor_file rootfs }:system module_load;

3.2 open error

虽然前面我们已经将 myled 添加到系统启动的进程中,并且也可以在 /dev/ 目录下查找到了我们的 myled 设备,但是查看启动 logcat 仍然出现 open error 的现象。出现这样现象的原因主要是跟 system server 没有权限去操作我们的 myled 设备有关,解决权限的问题需要分两步走:

  1. myled 设备的权限

打开目录文件 android_build/system/core/rootdir/ueventd.rc ,添加如下内容:

/dev/myled 0666 root root
  1. system server 访问 myled 设备的权限

这里主要涉及的是 Android 的 Sepolicy 安全机制,需要修改 android_build/device/fsl/imx8q/sepolicy 目录下的三个文件:file_contexts、device.te、system_server.te

① file_contexts

仿照这个文件里面的写法,添加一个自定义的设备名字,myled_device 为自定义,其他内容保持一致:

/dev/myled u:object_r:myled_device:s0

② device.te

仿照这个文件里的写法,将刚刚上一步写的 myled_device 声明为 dev_type:

type myled_device, dev_type;

③ system_server.te

加入允许 system_server 对 /dev/myled 的读写权限:

allow system_server myled_device:chr_file rw_file_perms;

chr_file表示字符设备文件,如果是普通文件用file,目录请用dir

rw_file_perms代表读写权限

完成以上步骤后,执行 make -j16 开始编译

编译完成后会在 out/target/product/mek_8q 目录下生成镜像文件

四、注意事项

① 如果把 LED_SERVICE 添加到 system server 成功的话,系统启动完成可以通过 adb shell service list 可以看到 led service,如果没有则说明没有添加成功。

60      window: [android.view.IWindowManager]
61      alarm: [android.app.IAlarmManager]
62      consumer_ir: [android.hardware.IConsumerIrService]
63      led: [android.os.ILedService]
64      vibrator: [android.os.IVibratorService]
65      content: [android.content.IContentService]

② 如果 so 文件没有正确添加(HAL 文件没有编译),会出现如下 log

--------- beginning of crash 
09-04 08:42:57.134 3404 3792 F libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x80 in tid 3792 (Binder:3404_7), pid 3404 (system_server) 
09-04 08:42:57.247 4652 4652 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstone 
09-04 08:42:57.248 3299 3299 I /system/bin/tombstoned: received crash request for pid 3792 
09-04 08:42:57.249 4652 4652 I crash_dump64: performing dump of process 3404 (target tid = 3792) 
09-04 08:42:57.265 4652 4652 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 
09-04 08:42:57.265 4652 4652 F DEBUG : Build fingerprint: 'Android/mek_8q/mek_8q:9/2.0.0-ga-rc4/pual09030609:eng/dev-keys' 
09-04 08:42:57.265 4652 4652 F DEBUG : Revision: '0' 
09-04 08:42:57.265 4652 4652 F DEBUG : ABI: 'arm64' 
09-04 08:42:57.265 4652 4652 F DEBUG : pid: 3404, tid: 3792, name: Binder:3404_7  >> > system_server  < <  09-04 08:42:57.265 4652 4652 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x80 
09-04 08:42:57.265 4652 4652 F DEBUG : Cause: null pointer dereference 
09-04 08:42:57.265 4652 4652 F DEBUG : x0 0000000000000000 x1 0000000000000001 x2 0000000000000001 x3 0000000000000003 
09-04 08:42:57.265 4652 4652 F DEBUG : x4 0000000000000067 x5 0080000000000000 x6 0aff646268757164 x7 7f7f7f7f7f7f7f7f 
09-04 08:42:57.265 4652 4652 F DEBUG : x8 0000f12def02b000 x9 601f5be1668242d3 x10 0000f12de3a7f580 x11 0000000000000014 
09-04 08:42:57.265 4652 4652 F DEBUG : x12 0000f12de3a7f6b8 x13 ffffffffffffffff x14 ffffffffff000000 x15 ffffffffffffffff 
09-04 08:42:57.265 4652 4652 F DEBUG : x16 0000f12e0a4fbcc0 x17 0000f12e0cb317e0 x18 0000f12de3a7ee7a x19 0000000000000001 
09-04 08:42:57.265 4652 4652 F DEBUG : x20 0000000000000001 x21 0000f12e09dc6c00 x22 0000f12de3a7fdc0 x23 0000000072e471d2 
09-04 08:42:57.265 4652 4652 F DEBUG : x24 0000000000000008 x25 0000f12de3a81588 x26 0000f12e09dc6ca0 x27 0000000000000002 
09-04 08:42:57.265 4652 4652 F DEBUG : x28 0000000000000002 x29 0000f12de3a7fbe8 
09-04 08:42:57.265 4652 4652 F DEBUG : sp 0000f12de3a7faf0 lr 000000007195e9a4 pc 0000f12deeff5e30

检查设备中 /system/lib/led.default.so 文件是否存在,如果不存在则需要在主机端中将 out/target/product/mek_8q/obj/SHARED_LIBRARIES/led.default_intermediates/led.default.so 拷贝到 out/target/product/mek_8q/system/lib 目录中,再重新编译烧录。如果 led.default_intermediates 目录中也不存在 so 文件,则还要重新执行 mmm hardware/libhardware/modules/test_led,来生成 so 文件。

理论上我们也可以将 so 文件直接 push 到开发板的 /system/lib 目录中,但是由于权限问题,会出现 Read-only file system 的错误提示,所以只能通过重新编译生成系统文件来实现。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Android
    +关注

    关注

    12

    文章

    3851

    浏览量

    125639
  • Linux
    +关注

    关注

    87

    文章

    10990

    浏览量

    206734
  • 硬件
    +关注

    关注

    11

    文章

    2922

    浏览量

    64773
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66766
收藏 人收藏

    评论

    相关推荐

    DMK如何进行硬件调试???

    有哪位大侠,能讲解一下DMK如何进行硬件调试???(不是软件设计,而是打开一个程序,要看看它运行到哪里了?该如何做?先前51单片机用的少,我查了一些资料,网上也搜了一下,都不是很具体。)
    发表于 05-03 16:06

    Android手机操控ARM开发板外围硬件设备【创科之龙】(2016年最给力!原创)

    。 主要讲解嵌入式linux驱动中,如何进行驱动开发与Android进行控制(工作中必须要用到的)以及教你如何理解linux设备模型的
    发表于 01-12 22:23

    Android手机操控ARM开发板外围硬件设备【创科之龙】原创

    ,如何进行驱动开发与Android进行控制(工作中必须要用到的)以及教你如何理解linux设备模型的驱动架构【目前企业必须用到的
    发表于 01-12 22:31

    Android程序中进行串口访问

    Android程序中进行串口访问首先下载我们提供的开源示例Demo:git clone https://github.com/friendlyarm
    发表于 11-14 15:04

    Android手机的USB转串口开发硬件以及软件

    以通过Android手机的USB配合相应的APP进行数据采集。硬件如下图: 电脑的数据采集软件网上很多。笔者在此不再做过多的阐述,主要对Androi
    发表于 12-06 13:16

    浅谈Android与Linux系统之间的差异

    程序APP—>硬件访问服务—>Android内核驱动程序(2)Linux应用程序访问linux
    发表于 07-05 06:06

    手把手演示示波器是如何进行远程访问的?

    手把手演示示波器是如何进行远程访问的?
    发表于 05-08 07:23

    EtherCAT总线运动控制器硬件是如何进行接线的?

    EtherCAT总线运动控制器硬件是如何进行接线的?EtherCAT总线运动控制器有哪些参数设置?
    发表于 07-15 13:02

    PLC与单片机硬件是如何进行通信的

    单片机和PLC的应用分别有哪些?PLC与单片机硬件通信的原理是什么?PLC与单片机硬件是如何进行通信的?
    发表于 08-12 07:34

    请问如何从Android应用层APP对u盘进行读写操作?

    开发板:i.MX6Q系统:Android6.0Linux4.1.15从Android应用层APP对u盘进行读写操作,没有权限。怎么解决。。。求教。。。
    发表于 01-11 06:06

    Android5.1系统下的uart串口是如何进行调试的

    Android5.1系统下的uart串口是如何进行调试的?有哪些调试步骤?
    发表于 03-03 09:48

    求助apk要怎么使用基于rk3288 Android硬件访问框架呢

    上一篇写了一个oled驱动,那么现在有一个问题:apk要怎么使用这个硬件,这里就需要提供一个硬件服务,apk通过这个服务就可以操作到硬件了。基于rk3288
    发表于 05-11 10:33

    Android手机的轻量级访问控制

    以A ndroid 手机为平台, 提出了一种适用于Android 手机的访问控制方法, 该方法在Android手机的内核中增加访问控制模块, 并根据手机用户定制的
    发表于 04-17 15:06 44次下载
    <b class='flag-5'>Android</b>手机的轻量级<b class='flag-5'>访问</b>控制

    何进行硬件连接,启动GUI并运行马达

    启动马达吧!快速,简单,有趣!本视频将向用户展示如何进行硬件连接,启动GUI并运行马达。
    的头像 发表于 11-28 06:04 2828次阅读

    何进行OPCDCOM配置

    何进行OPCDCOM配置(四会理士电源技术有限公司招聘)-如何进行OPCDCOM配置                      
    发表于 09-18 14:23 11次下载
    如<b class='flag-5'>何进行</b>OPCDCOM配置