欢迎加入我的知识星球Android系统开发指南
欢迎关注微信公众号 无限无羡
欢迎关注知乎账号 无限无羡
native服务添加
native服务就是用c++写的系统服务,通过init进程启动,可以实现binder接口供client调用。
下面我们以实现一个beanserver的后台服务为例:
- 首先需要写一个rc文件
// 文件路径根据自己需求放置
// vendor/zzh/native-service/bean-server/beanserver.rc
service beanserver /system/bin/beanserver
class main
- 写服务的main函数
// vendor/zzh/native-service/bean-server/main_beanserver.cpp
#define LOG_TAG "beanserver"
//#define LOG_NDEBUG 0
#include <hidl/HidlTransportSupport.h>
using namespace android;
int main(int argc __unused, char** argv __unused)
{
ALOGD(" beamserver start......");
return 0;
}
这个服务我们启动后只是打印了一行日志就退出了,具体可以根据自己的需求加入自己开机处理的业务。
- 编写Android.bp
cc_binary {
// 最终会生成到/system/bin/beanserver
name: "beanserver",
srcs: ["main_beanserver.cpp"],
header_libs: [
],
shared_libs: [
"liblog",
"libutils",
"libui",
"libgui",
"libbinder",
"libhidlbase",
],
compile_multilib: "first",
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wno-unused-parameter",
],
// 最终会生成到/system/etc/init/beanserver.rc
// init进程启动时会解析/system/etc/init/目录下的rc文件
init_rc: ["beanserver.rc"],
vintf_fragments: [
],
}
beanserver.rc在经过编译后会生成到/system/etc/init/beanserver.rc,然后init进程启动的时候就会解析该文件启动进程。
init进程解析/system/etc/init/的代码
// system/core/init/init.cpp
// 可以看到init会解析多个目录下的rc文件
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
- 加到编译镜像环境
// device/generic/car/emulator/aosp_car_emulator.mk
// 找到自己项目的mk文件加入
PRODUCT_PACKAGES += beanserver
这样才会将beanserver编译到镜像中。
- 编译镜像验证
编译完启动时看到有如下报错日志:
03-29 07:13:38.364 0 0 I init : Parsing file /system/etc/init/beanserver.rc...
03-29 07:13:41.706 0 0 E init : Could not start service 'beanserver' as part of class 'core': File /system/bin/beanserver(labeled "u:object_r:system_file:s0") has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined. Have you configured your service correctly? https://source.android.com/security/selinux/device-policy#label_new_services_and_address_denials. Note: this error shows up even in permissive mode in order to make auditing denials possible.
提示缺少selinux权限配置,我们可以在Google官网看到label_new_services_and_address_denials
下面我们配置一下selinux权限
selinux权限配置
- 编写自定义服务的te文件
// 我这里将新加的selinux配置放在了自己创建目录中
// 注意文件的首尾保留一行空行
// vendor/zzh/sepolicy/vendor/beanserver.te
# beanserver
type beanserver, domain, coredomain;
type beanserver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(beanserver)
- 编写file_contexts文件
// vendor/zzh/sepolicy/vendor/file_contexts
// 注意文件的首尾保留一行空行
/system/bin/beanserver u:object_r:beanserver_exec:s0
添加到sepolicy编译规则
// device/generic/car/emulator/aosp_car_emulator.mk
BOARD_VENDOR_SEPOLICY_DIRS += vendor/zzh/sepolicy/vendor
- 编译后启动模拟器
这个时候能看到服务启动了,但是发现beanserver一直在重启,日志如下:
经过排查发生这个现象的原因有两个:
(1)我们的进程启动后只是打印了一行日志就return 0退出了,通过查看init的代码发现,init进程会通过signal 9去kill掉退出的进程,这里总感觉有点矛盾,可能时init担心进程自己退出的不彻底,所以来个signal 9? init 里的代码如下:
// system/core/init/sigchld_handler.cpp
static pid_t ReapOneProcess() {
siginfo_t siginfo = {};
// This returns a zombie pid or informs us that there are no zombies left to be reaped.
// It does NOT reap the pid; that is done below.
if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
PLOG(ERROR) << "waitid failed";
return 0;
}
auto pid = siginfo.si_pid;
if (pid == 0) return 0;
// At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
// whenever the function returns from this point forward.
// We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
// want the pid to remain valid throughout that (and potentially future) usages.
auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
std::string name;
std::string wait_string;
Service* service = nullptr;
if (SubcontextChildReap(pid)) {
name = "Subcontext";
} else {
service = ServiceList::GetInstance().FindService(pid, &Service::pid);
if (service) {
name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
if (service->flags() & SVC_EXEC) {
auto exec_duration = boot_clock::now() - service->time_started();
auto exec_duration_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
} else if (service->flags() & SVC_ONESHOT) {
auto exec_duration = boot_clock::now() - service->time_started();
auto exec_duration_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
.count();
wait_string = StringPrintf(" oneshot service took %f seconds in background",
exec_duration_ms / 1000.0f);
}
} else {
name = StringPrintf("Untracked pid %d", pid);
}
}
// beanserver退出后,si_code为CLD_EXITED
if (siginfo.si_code == CLD_EXITED) {
LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
} else {
LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
}
if (!service) {
LOG(INFO) << name << " did not have an associated service entry and will not be reaped";
return pid;
}
// 这里将siginfo传入Reap函数
service->Reap(siginfo);
if (service->flags() & SVC_TEMPORARY) {
ServiceList::GetInstance().RemoveService(*service);
}
return pid;
}
void Service::Reap(const siginfo_t& siginfo) {
// 如果没有oneshot属性或者设置了restart的属性,则会调用SIGKILL杀掉进程
// 这里的oneshot和restart指的是rc文件里面的配置,我们的rc文件只声明了class main
// 如果没有声明oneshot属性,则服务被杀后会重新启动,如果配置了oneshot则只会启动一次
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL, false);
} else {
// Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not
// kill the process group in this case.
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
// The new behavior in Android R is to kill these process groups in all cases. The
// 'true' parameter instructions KillProcessGroup() to report a warning message where it
// detects a difference in behavior has occurred.
KillProcessGroup(SIGKILL, true);
}
}
...
}
为了保证服务不退出,我们先在main函数里加个死循环看下效果
int main(int argc __unused, char** argv __unused)
{
ALOGD(" beamserver start......");
while (true) {
sleep(3);
ALOGD("beanserver_thread...........");
}
return 0;
}
编译后启动模拟器看下启动日志:
可以看到目前服务已经正常了,这个服务将一直在后台运行。
如果我们只想开机后执行一些操作后退出,那也可以,我们修改下看看效果。
修改beanserver.rc文件:
service beanserver /system/bin/beanserver
class main
oneshot //只启动一次
修改main_beanserver.cpp文件:
#define LOG_TAG "beanserver"
//#define LOG_NDEBUG 0
#include <hidl/HidlTransportSupport.h>
using namespace android;
void quickSort(int arr[], int left, int right) {
int i = left, j = right;
int tmp;
int pivot = arr[(left + right) / 2];
/* partition */
while (i <= j) {
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
};
/* recursion */
if (left < j)
quickSort(arr, left, j);
if (i < right)
quickSort(arr, i, right);
}
int main(int argc __unused, char** argv __unused)
{
ALOGD(" beamserver start...");
int data[] = {9, 21, 3, 66, 100, 8, 36};
quickSort(data, 0, 7);
for (int i = 0; i < 7; i++) {
ALOGD("data[%d]=%d", i, data[i]);
}
return 0;
}
将一组数据用快速排序进行排序后输出,然后服务退出,通过日志可以看到,服务退出后init发送了signal 9,到此服务进程退出。
通过binder访问服务
先看下文件目录:
如果想要被别的进程调用,就需要实现binder接口,这样才算是一个完整的系统服务。
- 定义aidl接口
// vendor/zzh/native-service/bean-server/libbeanservice/aidl/com/zzh/IBeanService.aidl
package com.zzh;
interface IBeanService {
void sayHello();
}
- 加入Android.bp中进行编译
// vendor/zzh/native-service/bean-server/libbeanservice/Android.bp
cc_library_shared {
name: "libbeanservice_aidl",
aidl: {
export_aidl_headers: true,
local_include_dirs: ["aidl"],
include_dirs: [
],
},
srcs: [
":beanservice_aidl",
],
shared_libs: [
"libbase",
"libcutils",
"libutils",
"liblog",
"libbinder",
"libgui",
],
cflags: [
"-Werror",
"-Wall",
"-Wextra",
],
}
filegroup {
name: "beanservice_aidl",
srcs: [
"aidl/com/zzh/IBeanService.aidl",
],
path: "aidl",
}
make libbeanservice_aidl 后可以看到out下生成的文件:
IBeanService.cpp
IBeanService.h
BpBeanService.h
BnBeanService.h
- 实现服务,继承BnBeanService
BeanService.h
// vendor/zzh/native-service/bean-server/libbeanservice/BeanService.h
#ifndef BEANSERVICE_H
#define BEANSERVICE_H
#include <com/zzh/BnBeanService.h>
#include <binder/BinderService.h>
namespace android {
class BeanService:
public BinderService<BeanService>,
public virtual ::com::zzh::BnBeanService,
public virtual IBinder::DeathRecipient
{
public:
// Implementation of BinderService<T>
static char const* getServiceName() { return "bean.like"; }
// IBinder::DeathRecipient implementation
virtual void binderDied(const wp<IBinder> &who);
BeanService();
~BeanService();
virtual binder::Status sayHello();
};
}
#endif
这里BeanService继承了BinderService,必须重写getServiceName函数用来返回服务的名称,后续注册服务时会用到这个名字。
BeanService.cpp
// vendor/zzh/native-service/bean-server/libbeanservice/BeanService.cpp
#define LOG_TAG "BeanService"
//#define LOG_NDEBUG 0
#include "BeanService.h"
#include <iostream>
namespace android {
using binder::Status;
BeanService::BeanService() {
}
BeanService::~BeanService() {
}
/*virtual*/void BeanService::binderDied(const wp<IBinder> &who) {
ALOGE("%s: Java client's binder died, removing it from the list of active clients, who=%p",
__FUNCTION__, &who);
}
Status BeanService::sayHello() {
ALOGD(" BeanService::sayHello ");
return Status::ok();
}
}
Android.bp
// vendor/zzh/native-service/bean-server/libbeanservice/Android.bp
cc_library_shared {
name: "libbeanservice",
srcs: [
"BeanService.cpp",
],
header_libs: [
],
shared_libs: [
"libbeanservice_aidl",
"libbase",
"libui",
"liblog",
"libutils",
"libbinder",
"libcutils",
],
static_libs: [
],
include_dirs: [
],
export_shared_lib_headers: [
"libbinder",
"libbeanservice_aidl",
],
export_include_dirs: ["."],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wno-ignored-qualifiers",
],
}
cc_library_shared {
name: "libbeanservice_aidl",
aidl: {
export_aidl_headers: true,
local_include_dirs: ["aidl"],
include_dirs: [
],
},
srcs: [
":beanservice_aidl",
],
shared_libs: [
"libbase",
"libcutils",
"libutils",
"liblog",
"libbinder",
"libgui",
],
cflags: [
"-Werror",
"-Wall",
"-Wextra",
],
}
filegroup {
name: "beanservice_aidl",
srcs: [
"aidl/com/zzh/IBeanService.aidl",
],
path: "aidl",
}
- 注册服务
// vendor/zzh/native-service/bean-server/main_beanserver.cpp
#define LOG_TAG "beanserver"
//#define LOG_NDEBUG 0
#include "BeanService.h"
#include <hidl/HidlTransportSupport.h>
using namespace android;
int main(int argc __unused, char** argv __unused)
{
ALOGD(" beamserver start...");
signal(SIGPIPE, SIG_IGN);
// Set 5 threads for HIDL calls. Now cameraserver will serve HIDL calls in
// addition to consuming them from the Camera HAL as well.
hardware::configureRpcThreadpool(5, /*willjoin*/ false);
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());
BeanService::instantiate();
ALOGI("ServiceManager: %p done instantiate", sm.get());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
BeanService::instantiate()这个函数会调用父类BinderService的方法,最终会调用publish方法进行服务注册。
template<typename SERVICE>
class BinderService
{
public:
// 进行服务注册的方法,被instantiate()调用
static status_t publish(bool allowIsolated = false,
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
sp<IServiceManager> sm(defaultServiceManager());
// 这里进行服务注册,SERVICE::getServiceName()这个就是BeanService重写的方法,返回"bean.like"
return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,
dumpFlags);
}
static void publishAndJoinThreadPool(
bool allowIsolated = false,
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
publish(allowIsolated, dumpFlags);
joinThreadPool();
}
// 调用publish()方法
static void instantiate() { publish(); }
static status_t shutdown() { return NO_ERROR; }
...
}
- 编译后启动模拟器,看日志:
接下来根据提示配置selinux权限
adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy
输出信息如下:
#============= beanserver ==============
allow beanserver servicemanager:binder call;
#============= hal_audio_caremu ==============
allow hal_audio_caremu audioserver:fifo_file write;
allow hal_audio_caremu default_prop:file read;
#============= init ==============
allow init vendor_toolbox_exec:file execute_no_trans;
我们将规则加入beanserver.te即可:
selinux一般情况下并不能一次加完,每次都是添加了log中所报的权限后,再次运行时又会有新的权限错误,我们按照上面的方法逐一添加即可,最终的te文件如下:
// vendor/zzh/sepolicy/vendor/beanserver.te
# beanserver
type beanserver, domain, coredomain;
type beanserver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(beanserver)
allow beanserver servicemanager:binder { call transfer };
allow beanserver beanserver_service:service_manager add;
除了上述修改外,还需要做如下修改:
// vendor/zzh/sepolicy/public/service.te
type beanserver_service, service_manager_type;
// vendor/zzh/sepolicy/private/service_contexts
bean.like u:object_r:beanserver_service:s0
// device/generic/car/emulator/aosp_car_emulator.mk
SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS += vendor/zzh/sepolicy/public
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS += vendor/zzh/sepolicy/private
简单解释一下,我们需要为定义的服务定义一个标签,ServiceManager执行addService操作时会进行检查,如果不定义标签的话会使用默认的default_android_service,但Selinux是不允许以这个标签add service的。
// system/sepolicy/public/domain.te
# Do not allow service_manager add for default service labels.
# Instead domains should use a more specific type such as
# system_app_service rather than the generic type.
# New service_types are defined in {,hw,vnd}service.te and new mappings
# from service name to service_type are defined in {,hw,vnd}service_contexts.
neverallow * default_android_service:service_manager *;
neverallow * default_android_vndservice:service_manager *;
neverallow * default_android_hwservice:hwservice_manager *;
再次编译启动模拟器:
到此,我们的服务已经成功的添加到ServiceManager中。
- Client通过binder访问服务
我们写一个demo进行访问服务
测试文件:
// vendor/zzh/native-service/bean-server/client/BeanServer_client_test.cpp
#define LOG_TAG "beanclient"
//#define LOG_NDEBUG 0
#include <com/zzh/IBeanService.h>
#include <binder/IServiceManager.h>
using namespace android;
using com::zzh::IBeanService;
int main(int argc __unused, char** argv __unused) {
ALOGD(" beanclient start...");
// 先获取IServiceManager
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
// 通过服务名称拿到代理对象
binder = sm->getService(String16("bean.like"));
if (binder != 0) {
break;
}
usleep(500000); // 0.5s
} while (true);
// 转换成IBeanService
sp<IBeanService> beanService = interface_cast<IBeanService>(binder);
// 通过代理调用服务端函数
beanService->sayHello();
return 0;
}
Android.bp
// vendor/zzh/native-service/bean-server/client/Android.bp
cc_binary {
name: "beanserver_client",
srcs: ["BeanServer_client_test.cpp"],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wno-unused-parameter",
],
compile_multilib: "first",
shared_libs: [
"libbeanservice_aidl",
"libbase",
"libcutils",
"libutils",
"liblog",
"libbinder",
"libgui",
],
include_dirs: ["."],
}
编译到系统目录
// device/generic/car/emulator/aosp_car_emulator.mk
PRODUCT_PACKAGES += beanserver_client
也可以不加上面的编译选项,直接make beanserver_client,然后将生成的产物adb push到系统目录也可以。
启动模拟器,在终端执行beanserver_client,查看日志:
可以看出是不允许shell访问beanserver服务,这里在实际业务中要对client端配置selinux,由于我们这里是测试,就不赘述了。直接setenforce 0进行测试
su
setenforce 0
然后再重新执行beanservere_client
调用成功!
为了方便大家清晰的理解代码结果,下面列出了本例中的代码目录:文章来源:https://www.toymoban.com/news/detail-652118.html
zzh@ubuntu:~/work/android/aosp/android-13.0.0_r35/vendor/zzh$ tree
.
├── native-service
│ └── bean-server
│ ├── Android.bp
│ ├── beanserver.rc
│ ├── client
│ │ ├── Android.bp
│ │ └── BeanServer_client_test.cpp
│ ├── libbeanservice
│ │ ├── aidl
│ │ │ └── com
│ │ │ └── zzh
│ │ │ └── IBeanService.aidl
│ │ ├── Android.bp
│ │ ├── BeanService.cpp
│ │ └── BeanService.h
│ └── main_beanserver.cpp
└── sepolicy
├── private
│ └── service_contexts
├── public
│ └── service.te
└── vendor
├── beanserver.te
└── file_contexts
11 directories, 13 files
上述文件我已经整理好,大家想要的话可以关注我的微信公众号无限无羡,回复"nt" 即可获取,获取后只需修改部分名称,添加编译选项到自己项目的配置文件即可。文章来源地址https://www.toymoban.com/news/detail-652118.html
到了这里,关于Android 13添加自定义native服务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!