IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android HIDL —— HelloWorld 实战 -> 正文阅读

[移动开发]Android HIDL —— HelloWorld 实战

??了解了 HIDL 是什么之后,以一个简单的 HelloWorld 来做一个小实战。

??HAL 层本来的工作应该包括向下实现控制硬件的代码,并向上层提供接口。但是这里我们只是做一个简单的 HelloWorld,省略了控制硬件的部分,只是完成了向上提供一个接口,上层调用的时候返回一个 HelloWorld 字符串。

??学习资料主要是官网文档:https://source.android.com/devices/architecture/hidl-cpp

准备工作

  • Android 代码
  • Android BSP 编译环境
  • 测试设备

??HIDL 的步骤:
???1. 定义接口文件(.hal)
???2. 工具生成相应文件
???3. 编写文件并编译
???4. 测试验证

定义接口文件

??我们需要在标准 HAL 层中创建接口文件,标准 HAL 层的路径是 hardware/interfaces

??首先,进入 hardware/interfaces 目录,新建一个文件夹 helloworld,在该文件夹中再新建一个 1.0 文件夹。

mkdir -p ./hardware/interfaces/helloworld/1.0/

?&emps;1.0 这个目录表示的是版本。

??在 1.0 文件夹中新建一个接口描述文件 IHelloWorld.hal,该文件内容如下。

package android.hardware.helloworld@1.0;

interface IHelloWorld {
    helloWorld(string name) generates (string result);
};

??看起来既熟悉又陌生是吧,这其实是一个 Google 定义的语言格式,C++ 和 Java 的结合体。
??generates(string result) 相当于返回值为 string。

??还是要吐槽下语言这个事,做 Andorid BSP 开发的啥都得会。
??汇编:bootloader 和 kernel 中可能会用到。
??C 语言:Linux Kernel。
??C++:HAL层和 Library。
??Java:Framework 层和 Application 层。
??Python:编译相关。
??Makefile:mk 文件。

??目录结构:

├── hardware/interfaces/helloworld/1.0
│   └── IHelloWorld.hal

??现在我们新建了一个接口文件 IHelloWorld.hal,传入 string 参数,返回一个 string 参数。下面我们利用工具来生成相关文件。

工具生成相应文件

??1. 在代码的根目录打开终端,执行下面命令。

source build/envsetup.sh # 加载环境变量
lunch db410c # 这个地方填你的TARGET_PRODUCT
make hidl-gen # 这个就是我们的工具

??2. 在终端中执行下面命令,来利用 hidl-gen 工具生成相关文件。

# 包目录
PACKAGE=android.hardware.helloworld@1.0
# 路径
LOC=hardware/interfaces/helloworld/1.0/default/
# 生成.h和.cpp文件
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
# 生成 .bp 文件
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

??此时的目录结构:

├── hardware/interfaces/helloworld/1.0
│   ├── default
│   │   ├── Android.bp
│   │   ├── HelloWorld.cpp
│   │   └──  HelloWorld.h
│   └── IHelloWorld.hal

??如果提示不支持 -L,PACKAGE 和 LOC 没有配置。

??3. 执行下面命令来使用脚本更新 Makefile,自动更新 Android.mk 文件并在 1.0 目录下生成 Android.bp。

./hardware/interfaces/update-makefiles.sh

??此时的目录结构:

├── hardware/interfaces/helloworld/1.0
│   ├──  IHelloWorld.hal
│   ├──  default
│   │    ├── Android.bp
│   │    ├── HelloWorld.cpp
│   │    └──  HelloWorld.h
│   └── Android.bp

??4. 创建两个空文件。

touch hardware/interfaces/helloworld/1.0/default/android.hardware.helloworld@1.0-service.rc
touch hardware/interfaces/helloworld/1.0/default/service.cpp

??.rc 文件是启动脚本,在系统启动时 init 的时候会加载这个文件,以便启动服务。service.cpp 则是服务代码,用来注册服务的。

??OK,相关的文件都已经生成了,除了 cpp 文件和 rc 文件,其他文件都不需要我们编写。下面我们来实现这两个文件的编写。

编辑文件并编译

??在这一步中,我们需要编写的文件有 helloworld.cpp、service.cpp、android.hardware.helloworld@1.0-service.rc。

helloworld.cpp

??helloworld.h 不需要修改,主要的代码编写就是在 helloworld.cpp 文件,这是实现接口的文件。

??helloworld.h 内容如下所示。

#pragma once
#include <android/hardware/helloworld/1.0/IHelloWorld.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>

namespace android {
namespace hardware {
namespace helloworld {
namespace V1_0 {
namespace implementation {

using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;

struct HelloWorld : public IHelloWorld {
    Return<void> helloWorld(const hidl_string& name, helloString_cb _hidl_cb) override;
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IHelloWorld* HIDL_FETCH_IHelloWorld(const char* name);

}  // namespace implementation
}  // namespace V1_0
}  // namespace helloworld
}  // namespace hardware
}  // namespace android

??如果取消 HelloWorld.h 中下面的注释,表示使用直通模式。这里我们采用绑定式,因此不需要修改。

// extern "C" IHelloWorld* HIDL_FETCH_IHelloWorld(const char* name);

??helloworld.cpp 生成的内容如下所示。

#include "HelloWorld.h"

namespace android {
namespace hardware {
namespace helloworld {
namespace V1_0 {
namespace implementation {
Return<void> HelloWorld::helloString(const hidl_string& name, helloString_cb _hidl_cb) {
    // TODO implement
    return Void();
}
// IHelloWorld* HIDL_FETCH_IHelloWorld(const char* /* name */) {
//    return new HelloWorld();
//}
}  // namespace implementation
}  // namespace V1_0
}  // namespace helloworld
}  // namespace hardware
}  // namespace android

??如果取消 HelloWorld.cpp 中下面三行的注释是采用直通模式。同样的,这里我们不需要修改。

// IHelloWorld* HIDL_FETCH_IHelloWorld(const char* /* name */) {
//    return new HelloWorld();
//}

??下面来完善 HelloWorld.cpp 代码,返回一个 “Hello, World” 字符串。

Return<void> HelloWorld::helloString(const hidl_string& name, helloString_cb _hidl_cb) {
    // TODO implement
    char buf[128];
    ::memset(buf, 0, 128);
    ::snprintf(buf, 128, "Hello World, %s", name.c_str());
    hidl_string result(buf);

    _hidl_cb(result);
    return Void();
}

service.cpp

??接下来我们来完善服务代码,因为 HIDL 将 HAL 层作为服务启动,而 service.cpp 这个文件是用来注册 HIDL 服务的。

#define LOG_TAG "android.hardware.helloworld@1.0-service"

#include <android/hardware/helloworld/1.0/IHelloWorld.h>
#include <hidl/HidlTransportSupport.h>

#include "HelloWorld.h"

using android::hardware::helloworld::V1_0::IHelloWorld;
using android::hardware::helloworld::V1_0::implementation::HelloWorld;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::sp;
using android::status_t;
int main() {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool will never exceed
    // size one because of this call.
    configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<IHelloWorld> helloworld = new HelloWorld();
    const status_t status = helloworld->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

将 service.cpp 添加到 Android.bp 中

??完成上一步只是写好了 service.cpp,但是并没有 Android.bp 中配置其编译,所以需要在在 default 文件夹中的 Android.bp 文件中添加下面的配置。

cc_binary {
    name: "android.hardware.helloworld@1.0-service",
    defaults: ["hidl_defaults"],
    proprietary: true,
    relative_install_path: "hw",
    srcs: ["service.cpp",
           "HelloWorld.cpp"],
    init_rc: ["android.hardware.helloworld@1.0-service.rc"],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
        "android.hardware.helloworld@1.0",
    ],
}

.rc 文件

??.rc 文件主要是用于开机时启动服务的。

??.rc 文件的内容如下:

service helloworld_service /vendor/bin/hw/android.hardware.helloworld@1.0-service
    class hal
    user system
    group system

PS:在测试时,因为一些权限问题,服务无法自动启动,所以我先用了手动启动服务来调试,测试成功之后尝试改为自动启动,需要设置自动启动的话可以看下我后面的问题总结。

编译代码

mmm hardware/interfaces/helloworld/1.0/default

??编译成功之后,会给我们一个路径,编译之后的 so 库就在这个路径下面。
??out/target/product/硬件名/vendor/lib/hw/android.hardware.helloworld@1.0-impl.so 这个是服务端使用的 so 文件,是实现的 so 文件。
??out/target/product/硬件名/system/lib/android.hardware.helloworld@1.0.so 这个是客户端使用的 so 文件,是接口的 so 文件。
??out/target/product/硬件名/vendor/bin/hw/android.hardware.helloworld@1.0-service

??把这三个文件 push 到 硬件名 之后的路径。

测试验证

??启动服务
??如果配置 sepolicy 应该是自动启动服务,如果没有添加 sepolicy 需要手动启动服务,/vendor/bin/hw/android.hardware.helloworld@1.0-service

??客户端验证
??在另一个终端中输入,测试代码的 mk 文件中,LOCAL_MODULE 的值。

问题总结

1. 手动启动服务时,提示 XXX.helloworld@1.0-service.so 找不到

??解决方案:将 so 库文件添加到 /system/etc/public.libraries.txt 中,这样就把 so 库放到了公共库里。

2. 客户端无法访问服务端

??服务启动后客户端无法获取服务,说明是 selinux 权限问题,可以修改下面文件,将 selinux 设置为宽容模式。

??文件路径: device/linaro/dragonboard/db410c32_only/BoardConfig.mk。

BOARD_KERNEL_BASE := 0x80008000
BOARD_MKBOOTIMG_ARGS := --ramdisk_offset 0x0
BOARD_KERNEL_CMDLINE := firmware_class.path=/system/vendor/firmware/ androidboot.hardware=db410c
BOARD_KERNEL_CMDLINE += printk.devkmsg=on
# --- 在这里添加代码 ---
BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive
# ---------------------
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1288491008
BOARD_USERDATAIMAGE_PARTITION_SIZE := 5653544960
TARGET_COPY_OUT_VENDOR := vendor

3. 服务自启动设置

vendor 下的服务

??如果服务编译并刷机之后是在 vendor/bin 下,修改下面4个文件即可。

??如果还没有编译,可以根据编译之后文件在那个位置来进行选择。

??1. 在 system\sepolicy\vendor\file_contexts 文件最下面加上一句:

/(vendor|system/vendor)/bin/hw/android\.hardware\.helloworld@1\.0-service   

??2. 在 system\sepolicy\vendor 目录下新增一个文件 hal_helloworld.te

type hal_helloworld, domain;
type hal_helloworld_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_helloworld)

??3. 在 system\sepolicy\prebuilts\api\26.0\private\file_contexts 里,增加如下一条:

/(vendor|system/vendor)/bin/hw/android\.hardware\.helloworld@1\.0-service     u:object_r:hal_helloworld_exec:s0

??4. 在 system\sepolicy\prebuilts\api\26.0\private 下新增一个文件 hal_helloworld.te

type hal_helloworld, domain;
type hal_helloworld_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_avi_memory)

??这样配置后,再全编译刷机,以后开机就可以自动启动我们的服务了。

system 下的服务

??如果要启动的是 system/bin 下的服务,需要配置的4个文件如下。

??1. 在 system\sepolicy\prebuilts\api\26.0\private\file_contexts 里新增如下一行代码:

/system/bin/autotimestampserver u:object_r:autotimestampserver_exec:s0

??2. 在 sepolicy\prebuilts\api\26.0\private 下新增文件 autotimestampserver.te

type autotimestampserver, coredomain;
type autotimestampserver_exec, exec_type, file_type;
init_daemon_domain(autotimestampserver)

??在 system\sepolicy\private\file_contexts 里新增如下一行代码:

/system/bin/autotimestampserver u:object_r:autotimestampserver_exec:s0

??在 system\sepolicy\private 下新增文件 autotimestampserver.te

type autotimestampserver, coredomain;
type autotimestampserver_exec, exec_type, file_type;
init_daemon_domain(autotimestampserver)

??完成上面的配置后,即可开机启动服务了。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 07:56:17  更:2021-07-28 07:57:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 6:40:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码