Android 12(S) Binder(二)
前面一节学习了ServiceManager这个特殊service的工作过程,这一节来看看普通service的工作过程。
就用media.extractor这个service来当例子!
1、服务的注册及启动
media.extractor这个service的注册及启动在 frameworks/av/services/mediaextractor/main_extractorservice.cpp
int main(int argc __unused, char** argv)
{ #if __has_feature(hwaddress_sanitizer)
ALOGI("disable media.extractor memory limits (hwasan enabled)");
#else
ALOGI("enable media.extractor memory limits");
limitProcessMemory(
"ro.media.maxmem", /* property that defines limit */
SIZE_MAX, /* upper limit in bytes */
20 /* upper limit as percentage of physical RAM */);
#endif signal(SIGPIPE, SIG_IGN); //b/62255959: this forces libutis.so to dlopen vendor version of libutils.so
//before minijail is on. This is dirty but required since some syscalls such
//as pread64 are used by linker but aren't allowed in the minijail. By
//calling the function before entering minijail, we can force dlopen.
android::report_sysprop_change(); SetUpMinijail(kSystemSeccompPolicyPath, kVendorSeccompPolicyPath); strcpy(argv[0], "media.extractor");
// 1、打开binder driver
sp<ProcessState> proc(ProcessState::self());
// 2、获取ServiceManager
sp<IServiceManager> sm = defaultServiceManager();
// 3、实例化MediaExtractorService,并注册到ServiceManager当中
MediaExtractorService::instantiate();
// 4、
ProcessState::self()->startThreadPool();
// 5、
IPCThreadState::self()->joinThreadPool();
}
前面两个步骤已经很熟悉了,就直接来看看后面三步在干什么:
a. 实例化MediaExtractorService,并注册到ServiceManager当中
MediaExtractorService继承与BinderService,instantiate声明在 frameworks/native/libs/binder/include/binder/BinderService.h 中,同样被包含在libbinder中
template<typename SERVICE>
class BinderService
{
public:
static status_t publish(bool allowIsolated = false,
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
sp<IServiceManager> sm(defaultServiceManager());
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();
} static void instantiate() { publish(); } static status_t shutdown() { return NO_ERROR; } private:
static void joinThreadPool() {
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
ps->giveThreadPoolName();
IPCThreadState::self()->joinThreadPool();
}
};
服务的注册过程前面一节已经看过了,ServiceManager中的map会记录下传进来的这个BBinder对象。
b. ProcessState::self()->startThreadPool()
void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
} void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
String8 name = makeBinderThreadName();
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp<Thread> t = sp<PoolThread>::make(isMain);
t->run(name.string());
}
} class PoolThread : public Thread
{
public:
explicit PoolThread(bool isMain)
: mIsMain(isMain)
{
} protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
} const bool mIsMain;
};
在ProcessState中开了一个PoolThread线程来执行 IPCThreadState::self()->joinThreadPool,但是看看main函数执行的最后一句代码也是 IPCThreadState::self()->joinThreadPool,做了同样的事情,接下来看看:
void IPCThreadState::joinThreadPool(bool isMain)
{
LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); mIsLooper = true;
status_t result;
do {
processPendingDerefs();
// now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand(); if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
LOG_ALWAYS_FATAL("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
mProcess->mDriverFD, result);
} // Let this thread exit the thread pool if it is no longer
// needed and it is not the main process thread.
if(result == TIMED_OUT && !isMain) {
break;
}
} while (result != -ECONNREFUSED && result != -EBADF); LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",
(void*)pthread_self(), getpid(), result); mOut.writeInt32(BC_EXIT_LOOPER);
mIsLooper = false;
talkWithDriver(false);
}
这里有个do while循环用来检查是否client端有信息发过来并且处理信息,同时不让server进程结束。至于为什么调用了两次 IPCThreadState::joinThreadPool,这可能是和server端的多线程处理有关,这边后续再研究。
2、服务的获取
注册并启动服务之后我们要使用服务,首先要用ServiceManager来获取服务
这里摘一段MediaExtractorFactory中的代码:
sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
sp<IMediaExtractor> ex;
mediaExService->makeExtractor(
CreateIDataSourceFromDataSource(source),
mime ? std::optional<std::string>(mime) : std::nullopt,
&ex);
先来看看getService
sp<IBinder> ServiceManagerShim::getService(const String16& name) const
{
static bool gSystemBootCompleted = false; sp<IBinder> svc = checkService(name);
if (svc != nullptr) return svc; const bool isVendorService =
strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
constexpr int64_t timeout = 5000;
int64_t startTime = uptimeMillis();
// Vendor code can't access system properties
if (!gSystemBootCompleted && !isVendorService) {
#ifdef __ANDROID__
char bootCompleted[PROPERTY_VALUE_MAX];
property_get("sys.boot_completed", bootCompleted, "0");
gSystemBootCompleted = strcmp(bootCompleted, "1") == 0 ? true : false;
#else
gSystemBootCompleted = true;
#endif
}
// retry interval in millisecond; note that vendor services stay at 100ms
const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100; ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
ProcessState::self()->getDriverName().c_str()); int n = 0;
while (uptimeMillis() - startTime < timeout) {
n++;
usleep(1000*sleepTime); sp<IBinder> svc = checkService(name);
if (svc != nullptr) {
ALOGI("Waiting for service '%s' on '%s' successful after waiting %" PRIi64 "ms",
String8(name).string(), ProcessState::self()->getDriverName().c_str(),
uptimeMillis() - startTime);
return svc;
}
}
ALOGW("Service %s didn't start. Returning NULL", String8(name).string());
return nullptr;
}
核心方法就是checkService
sp<IBinder> ServiceManagerShim::checkService(const String16& name) const
{
sp<IBinder> ret;
if (!mTheRealServiceManager->checkService(String8(name).c_str(), &ret).isOk()) {
return nullptr;
}
return ret;
}
调用的就是BpServiceManager的checkService方法,对外的接口参数是String16,而BpServiceManager接口参数为String8。经过Binder驱动的处理最终调用到server端的checkService
Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) {
*outBinder = tryGetService(name, false);
// returns ok regardless of result for legacy reasons
return Status::ok();
}
sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfNotFound) {
auto ctx = mAccess->getCallingContext(); sp<IBinder> out;
Service* service = nullptr;
if (auto it = mNameToService.find(name); it != mNameToService.end()) {
service = &(it->second); if (!service->allowIsolated) {
uid_t appid = multiuser_get_app_id(ctx.uid);
bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END; if (isIsolated) {
return nullptr;
}
}
out = service->binder;
} if (!mAccess->canFind(ctx, name)) {
return nullptr;
} if (!out && startIfNotFound) {
tryStartService(name);
} if (out) {
// Setting this guarantee each time we hand out a binder ensures that the client-checking
// loop knows about the event even if the client immediately drops the service
service->guaranteeClient = true;
} return out;
}
注意到在addService时,保存到map中的IBinder实际是一个BnBinder对象,那Server端getSevice获得的也是一个BnBinder对象,返回给调用进程的就是这个BnBinder对象吗?其实不是的。
来看看BpServiceManager的checkService代码:
::android::binder::Status BpServiceManager::checkService(const ::std::string& name, ::android::sp<::android::IBinder>* _aidl_return) {
::android::Parcel _aidl_data;
_aidl_data.markForBinder(remoteStrong());
::android::Parcel _aidl_reply;
::android::status_t _aidl_ret_status = ::android::OK;
::android::binder::Status _aidl_status;
_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
if (((_aidl_ret_status) != (::android::OK))) {
goto _aidl_error;
}
_aidl_ret_status = _aidl_data.writeUtf8AsUtf16(name);
if (((_aidl_ret_status) != (::android::OK))) {
goto _aidl_error;
}
_aidl_ret_status = remote()->transact(BnServiceManager::TRANSACTION_checkService, _aidl_data, &_aidl_reply, 0);
if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IServiceManager::getDefaultImpl())) {
return IServiceManager::getDefaultImpl()->checkService(name, _aidl_return);
}
if (((_aidl_ret_status) != (::android::OK))) {
goto _aidl_error;
}
_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
if (((_aidl_ret_status) != (::android::OK))) {
goto _aidl_error;
}
if (!_aidl_status.isOk()) {
return _aidl_status;
}
// 获取代理对象
_aidl_ret_status = _aidl_reply.readNullableStrongBinder(_aidl_return);
if (((_aidl_ret_status) != (::android::OK))) {
goto _aidl_error;
}
_aidl_error:
_aidl_status.setFromStatusT(_aidl_ret_status);
return _aidl_status;
}
获得RPC调用结果aidl_reply之后,最后面还有一句Parcel.readNullableStrongBinder,目的就是获取代理
status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
{
return unflattenBinder(val);
} status_t Parcel::unflattenBinder(sp<IBinder>* out) const
{
if (isForRpc()) {
LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel"); int32_t isNull;
status_t status = readInt32(&isNull);
if (status != OK) return status; sp<IBinder> binder; if (isNull & 1) {
auto addr = RpcAddress::zero();
status_t status = addr.readFromParcel(*this);
if (status != OK) return status;
binder = mSession->state()->onBinderEntering(mSession, addr);
} return finishUnflattenBinder(binder, out);
} // 这里可以拿到一个Handle
const flat_binder_object* flat = readObject(false); if (flat) {
switch (flat->hdr.type) {
case BINDER_TYPE_BINDER: {
sp<IBinder> binder =
sp<IBinder>::fromExisting(reinterpret_cast<IBinder*>(flat->cookie));
return finishUnflattenBinder(binder, out);
}
case BINDER_TYPE_HANDLE: {
// 创建一个BpBinder(handle)
sp<IBinder> binder =
ProcessState::self()->getStrongProxyForHandle(flat->handle);
return finishUnflattenBinder(binder, out);
}
}
}
return BAD_TYPE;
}
后面调用interface_cast 获取一个 BpMediaExtractorService,这和之前SeviceManager是相同的。
template<typename INTERFACE>
inline sp<IMediaExtractorService> interface_cast(const sp<IBinder>& obj)
{
return IMediaExtractorService::asInterface(obj);
} ::android::sp<IMediaExtractorService> IMediaExtractorService::asInterface(
const ::android::sp<::android::IBinder>& obj)
{
::android::sp<IMediaExtractorService> intr;
if (obj != nullptr) {
intr = ::android::sp<IMediaExtractorService>::cast(
obj->queryLocalInterface(IMediaExtractorService::descriptor));
if (intr == nullptr) {
intr = ::android::sp<BpMediaExtractorService>::make(obj);
}
}
return intr;
} template<typename INTERFACE>
inline sp<IInterface> BnInterface<IMediaExtractorService>::queryLocalInterface(
const String16& _descriptor)
{
if (_descriptor == IMediaExtractorService::descriptor) return sp<IInterface>::fromExisting(this);
return nullptr;
}
3、服务的使用
client使用就不再看了,看到server端时想到一个问题,IPCThreadState监听消息,但是没有指定由谁来执行。之前ServiceManager调用了setTheContextObject指定执行对象,但是media.extractor没有执行这一句,但是仔细看代码,似乎是从client发送的数据中可以找到target
binder_transaction_data_secctx tr_secctx;
binder_transaction_data& tr = tr_secctx.transaction_data;
// ......
if (cmd == (int) BR_TRANSACTION_SEC_CTX) {
result = mIn.read(&tr_secctx, sizeof(tr_secctx));
} else {
result = mIn.read(&tr, sizeof(tr));
tr_secctx.secctx = 0;
}
// ......
if (tr.target.ptr) {
// We only have a weak reference on the target object, so we must first try to
// safely acquire a strong reference before doing anything else with it.
if (reinterpret_cast<RefBase::weakref_type*>(
tr.target.ptr)->attemptIncStrong(this)) {
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
} else {
error = UNKNOWN_TRANSACTION;
} } else {
error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
}
到这里binder service框架大致就了解了,之后看到相关内容也更加从容,binder驱动之后有空再学习。
Android 12(S) Binder(二)的更多相关文章
- Android 12(S) 图形显示系统 - 示例应用(二)
1 前言 为了更深刻的理解Android图形系统抽象的概念和BufferQueue的工作机制,这篇文章我们将从Native Level入手,基于Android图形系统API写作一个简单的图形处理小程序 ...
- Android 12(S) 图形显示系统 - Surface 一点补充知识(十二)
必读: Android 12(S) 图形显示系统 - 开篇 一.前言 因为个人工作主要是Android多媒体播放的内容,在工作中查看源码或设计程序经常会遇到调用API: static inline i ...
- ZT android -- 蓝牙 bluetooth (二) 打开蓝牙
android -- 蓝牙 bluetooth (二) 打开蓝牙 分类: Android的原生应用分析 2013-05-23 23:57 4773人阅读 评论(20) 收藏 举报 androidblu ...
- Android 12(S) 图形显示系统 - createSurface的流程(五)
题外话 刚刚开始着笔写作这篇文章时,正好看电视在采访一位92岁的考古学家,在他的日记中有这样一句话,写在这里与君共勉"不要等待幸运的降临,要去努力的掌握知识".如此朴实的一句话,此 ...
- Android 12(S) 图形显示系统 - BufferQueue/BLASTBufferQueue之初识(六)
题外话 你有没有听见,心里有一声咆哮,那一声咆哮,它好像在说:我就是要从后面追上去! 写文章真的好痛苦,特别是自己对这方面的知识也一知半解就更加痛苦了.这已经是这个系列的第六篇了,很多次都想放弃了,但 ...
- Android 12(S) 图形显示系统 - 初识ANativeWindow/Surface/SurfaceControl(七)
题外话 "行百里者半九十",是说步行一百里路,走过九十里,只能算是走了一半.因为步行越接近目的地,走起来越困难.借指凡事到了接近成功,往往是最吃力.最艰难的时段.劝人做事贵在坚持, ...
- Android 12(S) 图形显示系统 - BufferQueue的工作流程(八)
题外话 最近总有一个感觉:在不断学习中,越发的感觉自己的无知,自己是不是要从"愚昧之巅"掉到"绝望之谷"了,哈哈哈 邓宁-克鲁格效应 一.前言 前面的文章中已经 ...
- Android 12(S) 图形显示系统 - BufferQueue的工作流程(九)
题外话 Covid-19疫情的强烈反弹,小区里检测出了无症状感染者.小区封闭管理,我也不得不居家办公了.既然这么大把的时间可以光明正大的宅家里,自然要好好利用,八个字 == 努力工作,好好学习 一.前 ...
- Android 12(S) 图形显示系统 - 解读Gralloc架构及GraphicBuffer创建/传递/释放(十四)
必读: Android 12(S) 图形显示系统 - 开篇 一.前言 在前面的文章中,已经出现过 GraphicBuffer 的身影,GraphicBuffer 是Android图形显示系统中的一个重 ...
- Android 12(S) 图像显示系统 - SurfaceFlinger 之 VSync - 中篇(十七)
必读: Android 12(S) 图像显示系统 - 开篇 1 前言 这一篇文章,将继续讲解有关VSync的知识,前一篇文章 Android 12(S) 图像显示系统 - SurfaceFlinger ...
随机推荐
- HarmonyOS API Version 7版本特性说明
2020年9月11日,HarmonyOS SDK发布了首个Beta版本,支持基于HarmonyOS的华为智慧屏.智能穿戴.车机设备开发,让广大的开发者正式步入了HarmonyOS应用开发之旅. 开发者 ...
- HarmonyOS课程尝鲜计划,优享特权大礼包
报名入口:https://developer.huawei.com/consumer/cn/activity/901689042385499023
- 基于 Scriptable 从零开始美化iOS桌面(一)
今天我为大家带来新的作品,iOS17桌面组件神器(Scriptable)原创脚本,精美作品分享!喜欢的话就点关注吧!更多脚本正在路上... * script : ONE-Progress.js * v ...
- Excel 字符串拆分
用 Excel 处理数据时,有时需要对字符串进行拆分.对于比较简单的拆分,使用 Excel 函数可以顺利完成,但碰到一些特殊需求,或者拆分的规则比较复杂时,则很难用 Excel 实现了.这里列出一些拆 ...
- c# 后端与前端时间戳的转换
C# DateTime与时间戳转换 C# DateTime与时间戳的相互转换,包括JavaScript时间戳和Unix的时间戳. 1. 什么是时间戳 首先要清楚JavaScript与Unix的时间戳的 ...
- Oracle SQL 常用的将varchar数据处理成number的正则
Oracle SQL 常用的数据处理正则 去除所有的空格 replace(t.dxmz,chr(32),'') 匹配非纯数字 not regexp_like(t.zgbs,'^[[:digit:]]* ...
- Beetl 源码解析:GroupTemplate 类
本文首发于公众号:腐烂的橘子 前言 Beetl 是一款 Java 模板引擎,在公司的项目中大量运用,它的作用是写通用代码时,有一些差异化的逻辑需要处理,这时可以把这些差异化的逻辑写在模板里,程序直接调 ...
- 探秘RocketMQ源码——Series1:Producer视角看事务消息
简介: 探秘RocketMQ源码--Series1:Producer视角看事务消息 1. 前言 Apache RocketMQ作为广为人知的开源消息中间件,诞生于阿里巴巴,于2016年捐赠给了Apac ...
- iLogtail使用入门-iLogtail本地配置模式部署(For Kafka Flusher)
简介:iLogtail使用入门-iLogtail本地配置模式部署(For Kafka Flusher). 阿里已经正式开源了可观测数据采集器iLogtail.作为阿里内部可观测数据采集的基础设施,i ...
- Flink 在爱奇艺广告业务的实践
简介: 5 月 22 日北京站 Flink Meetup 分享的议题. 本文整理自爱奇艺技术经理韩红根在 5 月 22 日北京站 Flink Meetup 分享的议题<Flink 在爱奇艺广告业 ...