本文我們要講的是在用 i.MX8 平臺(tái)開(kāi)發(fā)時(shí),Android APP 如何進(jìn)行訪問(wèn)硬件驅(qū)動(dòng)。
主要內(nèi)容包括:框架代碼編寫(xiě)和注意事項(xiàng)(Linux Driver、C Library、SystemServer、APP 等五個(gè)部分)、Sepolicy 信息配置,話不多說(shuō),我們直接進(jìn)入正題吧~
一、Android 應(yīng)用層訪問(wèn)硬件服務(wù)框架
Android 應(yīng)用層訪問(wèn)硬件服務(wù)框架主要可以分為:① APP;② SystemServer;③ C Library;④ Linux Driver。
① APP:
從 service_manager 里獲取相關(guān)服務(wù),再通過(guò)接口調(diào)用,接口里實(shí)現(xiàn)對(duì)本地方法的調(diào)用。
涉及到的相關(guān)文件:MainActivity.java、activity_main.xml、ILedService.aidl、LedService.java
② SystemServer:
通過(guò)注冊(cè) android 服務(wù)的方式加載 C 庫(kù),再將服務(wù)加入 service_manager 里面。
涉及到的相關(guān)文件:SystemServer.java、SystemServiceRegistry.java、LedManager.java
③ C Library:
HAL 層操作 /dev/myled,JNI 層加載 HAL 文件和向上注冊(cè) java 本地方法。
涉及到的相關(guān)文件:onload.cpp、com_android_server_LedService.cpp、led_hal.c、led_hal.h
④ Linux Driver
和 Linux 驅(qū)動(dòng)完全一樣,編寫(xiě)驅(qū)動(dòng),向上提供 /dev/myled 節(jié)點(diǎn)。
涉及到的相關(guān)文件:myled.c
二、框架代碼編寫(xiě)
2.1 編寫(xiě) Linux Driver
通過(guò) Linux Driver 來(lái)實(shí)現(xiàn)從應(yīng)用層到底層的控制,這里主要實(shí)現(xiàn)對(duì) LED 燈的 ON/OFF 控制及 GPIO 引腳電平 HIGH/LOW 的上報(bào)。
在 vendor/nxp-opensource/kernel_imx/drivers/leds 目錄下創(chuàng)建 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");//第一個(gè)參數(shù),為要申請(qǐng)的引腳,第二個(gè)為你要定義的名字
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);
//創(chuàng)建設(shè)備信息,執(zhí)行后會(huì)出現(xiàn) /sys/class/myled
myled_class = class_create(THIS_MODULE, "myled");
//創(chuàng)建設(shè)備節(jié)點(diǎn),就是根據(jù)上面的設(shè)備信息來(lái)的
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. 構(gòu)建platform_driver 結(jié)構(gòu)體*/
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. 注冊(cè)平臺(tái)驅(qū)動(dòng)*/
platform_driver_register(&myled_driver);
printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
return 0;
}
static void __exit myled_exit(void)
{
/*3. 注銷(xiāo)平臺(tái)驅(qū)動(dòng)*/
platform_driver_unregister(&myled_driver);
}
module_init(myled_init);
module_exit(myled_exit);
/* 描述驅(qū)動(dòng)程序的一些信息,不是必須的 */
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.
修改默認(rèn)配置文件 vendor/nxp-opensource/kernel_imx/arch/arm64/configs/android_defconfig,添加代碼
CONFIG_MYLED=y
由于這里給的值是 y,會(huì)把我們的 driver 編譯進(jìn)內(nèi)核,不會(huì)生成 ko 文件,也不需要我們?nèi)ナ謩?dòng)掛載。注意,必須是給 y,因?yàn)槲覀兒罄m(xù)會(huì)把 led 添加進(jìn) system server 里面,會(huì)在系統(tǒng)啟動(dòng)的時(shí)候就調(diào)用到 open 來(lái)打開(kāi) /dev/myled。
執(zhí)行如下命令,開(kāi)始編譯 kernel 內(nèi)核
source build/envsetup.sh
lunch mek_8q-eng
make bootimage -j16
2.2 編寫(xiě) C Library
2.2.1 修改 onload.cpp
SystemServer.java 會(huì)加載 C 庫(kù),調(diào)用到 onload.cpp,需要在 onload.cpp 注冊(cè) LED 服務(wù)。
修改 frameworks/base/services/core/jni/onload.cpp,添加:
int register_android_server_LedService(JNIEnv* env);
……
register_android_server_LedService(env);
2.2.2 創(chuàng)建 com_android_server_LedService.cpp
前面用到了 register_android_server_LedService(),是在 com_android_server_LedService.cpp 里實(shí)現(xiàn)的。
com_android_server_LedService.cpp 理論上可以直接操作節(jié)點(diǎn) /dev/myled,但一般不這樣做。
通常的做法是,向上提供本地方法 (native_ledOpen),向下加載 HAL 文件 (led_hal.c),并調(diào)用 HAL 的函數(shù)。
這樣操作有兩個(gè)好處:
- 方便修改;
如果需要修改硬件部分的操作,只需要修改 led_hal.c,生成 so 文件,放入系統(tǒng)即可,而不需要編譯整個(gè) Android 系統(tǒng);
- 保密代碼;
因?yàn)?Linux 的 GPL 協(xié)議,一旦使用的內(nèi)核代碼,自己的代碼也得開(kāi)源出去,硬件廠商為了保密硬件的具體細(xì)節(jié),只在內(nèi)核實(shí)現(xiàn)操作寄存器的接口,具體的操作邏輯放在 HAL 文件里,而 Android 采用 Apache 協(xié)議,修改了代碼而無(wú)需開(kāi)源,這樣就實(shí)現(xiàn)了保密代碼;
編寫(xiě)步驟如下:
- 定義 JNINativeMethod,建立 Java 本地方法與 C 庫(kù)函數(shù)名的映射關(guān)系;
- 使用 jniRegisterNativeMethods 注冊(cè)本地方法,將在 onload.cpp 被調(diào)用;
- 在 open() 里:
① 使用 hw_get_module 獲得 hw_module_t 結(jié)構(gòu)體;
② 使用 module->methods->open 獲得 hw_device_t 結(jié)構(gòu)體;
③ 將 hw_device_t 轉(zhuǎn)換為 led_device_t,調(diào)用對(duì)應(yīng) open;
- 完成其它函數(shù) ctrl、close 的調(diào)用;
在 frameworks/base/services/core/jni/ 路徑下創(chuàng)建 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",
執(zhí)行 mmm frameworks/base/services 編譯 com_android_server_LedService.cpp 和 onload.cpp
2.2.3 創(chuàng)建 HAL 文件:led_hal.c 和 led_hal.h
在 hardware/libhardware/modules 目錄下新建一個(gè)目錄 test_led
在 hardware/libhardware/modules/test_led 目錄下創(chuàng)建 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 下創(chuàng)建 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 目錄下創(chuàng)建 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)
執(zhí)行 mmm hardware/libhardware/modules/test_led 編譯 led_hal.c
編譯完成后會(huì)在 out/target/product/mek_8q/obj_arm/SHARED_LIBRARIES/led.default_intermediates 目錄下生成 so 文件 led.default.so
2.3 編寫(xiě) SystemServer
2.3.1 修改 SystemServer.java
SystemServer.java 主要做兩件事,一是加載 C 庫(kù),二是使用 addService 將 LED 服務(wù)加入 service_manager 里面。
加載 C 庫(kù)這個(gè)是調(diào)用 onload.cpp,這個(gè)前面已經(jīng)修改 onload.cpp,這里就不需要改了。
修改frameworks/base/services/java/com/android/server/SystemServer.java,添加:
traceBeginAndSlog("StartLedService");
ServiceManager.addService("led", new LedService());
traceEnd();
2.3.2 創(chuàng)建 LedManager.java
在目錄 frameworks/base/core/java/android/app 下,創(chuàng)建 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 中注冊(cè)我們的 LedManager Service 服務(wù)。
修改 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 編寫(xiě) APP
2.4.1 創(chuàng)建 AIDL
AIDL(Android Interface Definition Language),即 Android 接口定義語(yǔ)言,顧名思義就是定義接口,提供給 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 目錄下執(zhí)行編譯
mmm frameworks/base/
編譯完成后將會(huì)自動(dòng)生成:
out/soong/.intermediates/frameworks/base/framework/android_common/gen/aidl/frameworks/base/core/java/android/os/ILedService.java。
2.4.2 創(chuàng)建 LedService.java
前面生成了 ILedService.java,需要實(shí)現(xiàn) ILedService 接口的成員函數(shù)。
在 frameworks/base/services/core/java/com/android/server/ 路徑下,創(chuàng)建 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,并調(diào)用本地方法實(shí)現(xiàn)了成員函數(shù)。并在構(gòu)造函數(shù)里調(diào)用 native_ledOpen()。
其 Android.mk 自動(dòng)包含了所有 java 文件,不需要修改 Android.mk。
執(zhí)行完以上步驟后就可以執(zhí)行 make -j16 命令開(kāi)始編譯
2.4.3 創(chuàng)建 APP 工程
① 打開(kāi) Android Studio,選擇 “Start a new Android Studio project” 創(chuàng)建一個(gè)新 APP 工程;
② 然后選擇 “Empty Activity” 空主界面,點(diǎn)擊下一步;
③ 最后設(shè)置 APP 信息、保存路徑、選擇語(yǔ)言、兼容API版本、勾選支持安裝 apps,點(diǎn)擊完成。
等待自動(dòng)創(chuàng)建完成后,會(huì)自動(dòng)生成工程文件,生成的工程文件中會(huì)包含MainActivity.java 和 activity_main.xml,appsrcmainreslayoutactivity_main.xml,界面控件布局文件,既可通過(guò)圖形界面設(shè)計(jì)控件,也可直接編輯 xml;
appsrcmainjavacomexampleled_demoMainActivity.java,實(shí)現(xiàn)控件各種具體功能,邏輯關(guān)系。
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 >
由于這里會(huì)調(diào)用到我們前面定義好的 ledctrl 函數(shù)接口,但是我們還沒(méi)有把對(duì)應(yīng)的庫(kù)導(dǎo)進(jìn)來(lái),需要執(zhí)行如下操作:
將 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar 放在 Windows 里。
在 Android Studio 里,選擇 File->Project Structure,此時(shí)彈出配置界面,點(diǎn)擊 Modules 窗口左上角的 +,選擇 Import .JAR/.AAR Package,選擇剛才的 classes.jar。
切換到 Dependencies 選項(xiàng)卡,選擇 app,點(diǎn)擊 Declared Dependencies 窗口左上角的 + 號(hào),選擇 3 Module dependency,在彈出的界面選擇剛才添加的 classes,最后點(diǎn)擊 OK。
以上操作后,會(huì)在 APP 工程根目錄下生成 classes 文件夾,里面就包含了 classes.jar。
完成后可以點(diǎn)擊 Build -> Build Bundle(s) / APK(s) -> Build APK(s) 開(kāi)始編譯
2.4.4 APK 安裝
上面編譯生成的 APK 名稱(chēng)為 :app-debug.apk,我們需要連接好開(kāi)發(fā)板的 USB TypeC 接口,在 Windows 終端輸入如下命令開(kāi)始安裝:
adb install app-debug.apk
安裝完成后,打開(kāi) APK 即可看到如下界面:
三、Sepolicy 權(quán)限配置
3.1 denied error
在完成各層代碼添加后,還需要添加 Sepolicy 權(quán)限,否則系統(tǒng)在啟動(dòng)的時(shí)候回出現(xiàn)類(lèi)似如下報(bào)錯(cuò)
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 };
添加完后編譯,出現(xiàn)了如下報(bào)錯(cuò):
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
雖然前面我們已經(jīng)將 myled 添加到系統(tǒng)啟動(dòng)的進(jìn)程中,并且也可以在 /dev/ 目錄下查找到了我們的 myled 設(shè)備,但是查看啟動(dòng) logcat 仍然出現(xiàn) open error 的現(xiàn)象。出現(xiàn)這樣現(xiàn)象的原因主要是跟 system server 沒(méi)有權(quán)限去操作我們的 myled 設(shè)備有關(guān),解決權(quán)限的問(wèn)題需要分兩步走:
- myled 設(shè)備的權(quán)限
打開(kāi)目錄文件 android_build/system/core/rootdir/ueventd.rc ,添加如下內(nèi)容:
/dev/myled 0666 root root
- system server 訪問(wèn) myled 設(shè)備的權(quán)限
這里主要涉及的是 Android 的 Sepolicy 安全機(jī)制,需要修改 android_build/device/fsl/imx8q/sepolicy 目錄下的三個(gè)文件:file_contexts、device.te、system_server.te
① file_contexts
仿照這個(gè)文件里面的寫(xiě)法,添加一個(gè)自定義的設(shè)備名字,myled_device 為自定義,其他內(nèi)容保持一致:
/dev/myled u:object_r:myled_device:s0
② device.te
仿照這個(gè)文件里的寫(xiě)法,將剛剛上一步寫(xiě)的 myled_device 聲明為 dev_type:
type myled_device, dev_type;
③ system_server.te
加入允許 system_server 對(duì) /dev/myled 的讀寫(xiě)權(quán)限:
allow system_server myled_device:chr_file rw_file_perms;
chr_file表示字符設(shè)備文件,如果是普通文件用file,目錄請(qǐng)用dir
rw_file_perms代表讀寫(xiě)權(quán)限
完成以上步驟后,執(zhí)行 make -j16 開(kāi)始編譯
編譯完成后會(huì)在 out/target/product/mek_8q 目錄下生成鏡像文件
四、注意事項(xiàng)
① 如果把 LED_SERVICE 添加到 system server 成功的話,系統(tǒng)啟動(dòng)完成可以通過(guò) adb shell service list 可以看到 led service,如果沒(méi)有則說(shuō)明沒(méi)有添加成功。
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 文件沒(méi)有正確添加(HAL 文件沒(méi)有編譯),會(huì)出現(xiàn)如下 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
檢查設(shè)備中 /system/lib/led.default.so 文件是否存在,如果不存在則需要在主機(jī)端中將 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 文件,則還要重新執(zhí)行 mmm hardware/libhardware/modules/test_led,來(lái)生成 so 文件。
理論上我們也可以將 so 文件直接 push 到開(kāi)發(fā)板的 /system/lib 目錄中,但是由于權(quán)限問(wèn)題,會(huì)出現(xiàn) Read-only file system 的錯(cuò)誤提示,所以只能通過(guò)重新編譯生成系統(tǒng)文件來(lái)實(shí)現(xiàn)。
-
Android
+關(guān)注
關(guān)注
12文章
3935瀏覽量
127339 -
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209328 -
硬件
+關(guān)注
關(guān)注
11文章
3312瀏覽量
66200 -
代碼
+關(guān)注
關(guān)注
30文章
4779瀏覽量
68521
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論