歡迎加入我的知識(shí)星球Android系統(tǒng)開發(fā)指南
歡迎關(guān)注微信公眾號(hào) 無限無羨
歡迎關(guān)注知乎賬號(hào) 無限無羨
native服務(wù)添加
native服務(wù)就是用c++寫的系統(tǒng)服務(wù),通過init進(jìn)程啟動(dòng),可以實(shí)現(xiàn)binder接口供client調(diào)用。
下面我們以實(shí)現(xiàn)一個(gè)beanserver的后臺(tái)服務(wù)為例:
- 首先需要寫一個(gè)rc文件
// 文件路徑根據(jù)自己需求放置
// vendor/zzh/native-service/bean-server/beanserver.rc
service beanserver /system/bin/beanserver
class main
- 寫服務(wù)的main函數(shù)
// 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;
}
這個(gè)服務(wù)我們啟動(dòng)后只是打印了一行日志就退出了,具體可以根據(jù)自己的需求加入自己開機(jī)處理的業(yè)務(wù)。
- 編寫Android.bp
cc_binary {
// 最終會(huì)生成到/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",
],
// 最終會(huì)生成到/system/etc/init/beanserver.rc
// init進(jìn)程啟動(dòng)時(shí)會(huì)解析/system/etc/init/目錄下的rc文件
init_rc: ["beanserver.rc"],
vintf_fragments: [
],
}
beanserver.rc在經(jīng)過編譯后會(huì)生成到/system/etc/init/beanserver.rc,然后init進(jìn)程啟動(dòng)的時(shí)候就會(huì)解析該文件啟動(dòng)進(jìn)程。
init進(jìn)程解析/system/etc/init/的代碼
// system/core/init/init.cpp
// 可以看到init會(huì)解析多個(gè)目錄下的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);
}
}
- 加到編譯鏡像環(huán)境
// device/generic/car/emulator/aosp_car_emulator.mk
// 找到自己項(xiàng)目的mk文件加入
PRODUCT_PACKAGES += beanserver
這樣才會(huì)將beanserver編譯到鏡像中。
- 編譯鏡像驗(yàn)證
編譯完啟動(dòng)時(shí)看到有如下報(bào)錯(cuò)日志:
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權(quán)限配置,我們可以在Google官網(wǎng)看到label_new_services_and_address_denials
下面我們配置一下selinux權(quán)限
selinux權(quán)限配置
- 編寫自定義服務(wù)的te文件
// 我這里將新加的selinux配置放在了自己創(chuàng)建目錄中
// 注意文件的首尾保留一行空行
// 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編譯規(guī)則
// device/generic/car/emulator/aosp_car_emulator.mk
BOARD_VENDOR_SEPOLICY_DIRS += vendor/zzh/sepolicy/vendor
- 編譯后啟動(dòng)模擬器
這個(gè)時(shí)候能看到服務(wù)啟動(dòng)了,但是發(fā)現(xiàn)beanserver一直在重啟,日志如下:
經(jīng)過排查發(fā)生這個(gè)現(xiàn)象的原因有兩個(gè):
(1)我們的進(jìn)程啟動(dòng)后只是打印了一行日志就return 0退出了,通過查看init的代碼發(fā)現(xiàn),init進(jìn)程會(huì)通過signal 9去kill掉退出的進(jìn)程,這里總感覺有點(diǎn)矛盾,可能時(shí)init擔(dān)心進(jìn)程自己退出的不徹底,所以來個(gè)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函數(shù)
service->Reap(siginfo);
if (service->flags() & SVC_TEMPORARY) {
ServiceList::GetInstance().RemoveService(*service);
}
return pid;
}
void Service::Reap(const siginfo_t& siginfo) {
// 如果沒有oneshot屬性或者設(shè)置了restart的屬性,則會(huì)調(diào)用SIGKILL殺掉進(jìn)程
// 這里的oneshot和restart指的是rc文件里面的配置,我們的rc文件只聲明了class main
// 如果沒有聲明oneshot屬性,則服務(wù)被殺后會(huì)重新啟動(dòng),如果配置了oneshot則只會(huì)啟動(dòng)一次
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);
}
}
...
}
為了保證服務(wù)不退出,我們先在main函數(shù)里加個(gè)死循環(huán)看下效果
int main(int argc __unused, char** argv __unused)
{
ALOGD(" beamserver start......");
while (true) {
sleep(3);
ALOGD("beanserver_thread...........");
}
return 0;
}
編譯后啟動(dòng)模擬器看下啟動(dòng)日志:
可以看到目前服務(wù)已經(jīng)正常了,這個(gè)服務(wù)將一直在后臺(tái)運(yùn)行。
如果我們只想開機(jī)后執(zhí)行一些操作后退出,那也可以,我們修改下看看效果。
修改beanserver.rc文件:
service beanserver /system/bin/beanserver
class main
oneshot //只啟動(dòng)一次
修改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;
}
將一組數(shù)據(jù)用快速排序進(jìn)行排序后輸出,然后服務(wù)退出,通過日志可以看到,服務(wù)退出后init發(fā)送了signal 9,到此服務(wù)進(jìn)程退出。
通過binder訪問服務(wù)
先看下文件目錄:
如果想要被別的進(jìn)程調(diào)用,就需要實(shí)現(xiàn)binder接口,這樣才算是一個(gè)完整的系統(tǒng)服務(wù)。
- 定義aidl接口
// vendor/zzh/native-service/bean-server/libbeanservice/aidl/com/zzh/IBeanService.aidl
package com.zzh;
interface IBeanService {
void sayHello();
}
- 加入Android.bp中進(jìn)行編譯
// 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
- 實(shí)現(xiàn)服務(wù),繼承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函數(shù)用來返回服務(wù)的名稱,后續(xù)注冊(cè)服務(wù)時(shí)會(huì)用到這個(gè)名字。
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",
}
- 注冊(cè)服務(wù)
// 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()這個(gè)函數(shù)會(huì)調(diào)用父類BinderService的方法,最終會(huì)調(diào)用publish方法進(jìn)行服務(wù)注冊(cè)。
template<typename SERVICE>
class BinderService
{
public:
// 進(jìn)行服務(wù)注冊(cè)的方法,被instantiate()調(diào)用
static status_t publish(bool allowIsolated = false,
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
sp<IServiceManager> sm(defaultServiceManager());
// 這里進(jìn)行服務(wù)注冊(cè),SERVICE::getServiceName()這個(gè)就是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();
}
// 調(diào)用publish()方法
static void instantiate() { publish(); }
static status_t shutdown() { return NO_ERROR; }
...
}
- 編譯后啟動(dòng)模擬器,看日志:
接下來根據(jù)提示配置selinux權(quán)限
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;
我們將規(guī)則加入beanserver.te即可:
selinux一般情況下并不能一次加完,每次都是添加了log中所報(bào)的權(quán)限后,再次運(yùn)行時(shí)又會(huì)有新的權(quán)限錯(cuò)誤,我們按照上面的方法逐一添加即可,最終的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
簡單解釋一下,我們需要為定義的服務(wù)定義一個(gè)標(biāo)簽,ServiceManager執(zhí)行addService操作時(shí)會(huì)進(jìn)行檢查,如果不定義標(biāo)簽的話會(huì)使用默認(rèn)的default_android_service,但Selinux是不允許以這個(gè)標(biāo)簽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 *;
再次編譯啟動(dòng)模擬器:
到此,我們的服務(wù)已經(jīng)成功的添加到ServiceManager中。
- Client通過binder訪問服務(wù)
我們寫一個(gè)demo進(jìn)行訪問服務(wù)
測(cè)試文件:
// 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 {
// 通過服務(wù)名稱拿到代理對(duì)象
binder = sm->getService(String16("bean.like"));
if (binder != 0) {
break;
}
usleep(500000); // 0.5s
} while (true);
// 轉(zhuǎn)換成IBeanService
sp<IBeanService> beanService = interface_cast<IBeanService>(binder);
// 通過代理調(diào)用服務(wù)端函數(shù)
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: ["."],
}
編譯到系統(tǒng)目錄
// device/generic/car/emulator/aosp_car_emulator.mk
PRODUCT_PACKAGES += beanserver_client
也可以不加上面的編譯選項(xiàng),直接make beanserver_client,然后將生成的產(chǎn)物adb push到系統(tǒng)目錄也可以。
啟動(dòng)模擬器,在終端執(zhí)行beanserver_client,查看日志:
可以看出是不允許shell訪問beanserver服務(wù),這里在實(shí)際業(yè)務(wù)中要對(duì)client端配置selinux,由于我們這里是測(cè)試,就不贅述了。直接setenforce 0進(jìn)行測(cè)試
su
setenforce 0
然后再重新執(zhí)行beanservere_client
調(diào)用成功!
為了方便大家清晰的理解代碼結(jié)果,下面列出了本例中的代碼目錄:文章來源:http://www.zghlxwxcb.cn/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
上述文件我已經(jīng)整理好,大家想要的話可以關(guān)注我的微信公眾號(hào)無限無羨,回復(fù)"nt" 即可獲取,獲取后只需修改部分名稱,添加編譯選項(xiàng)到自己項(xiàng)目的配置文件即可。文章來源地址http://www.zghlxwxcb.cn/news/detail-652118.html
到了這里,關(guān)于Android 13添加自定義native服務(wù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!