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 ...
随机推荐
- Spring框架之IOC和AOP底层原理
1.1简介 Spring:春天-->软件行业的春天 2002,首次推出了Spring框架的雏:interface21框架! Spring框架即以interface21框架为基础,经过重新设计, ...
- 实战指南:使用 xUnit.DependencyInjection 在单元测试中实现依赖注入【完整教程】
引言 上一篇我们创建了一个Sample.Api项目和Sample.Repository,并且带大家熟悉了一下Moq的概念,这一章我们来实战一下在xUnit项目使用依赖注入. Xunit.Depende ...
- 图像验证码识别,字母数字汉子均可cnn+lstm+ctc
图形验证码如下: 训练两轮时的准确率:上边显示的是未识别的 config_demo.yaml System: GpuMemoryFraction: 0.7 TrainSetPath: 'train/ ...
- MD5前端vue加密
Vue 前端md5加密用户注册时将加密后的密码发送给后端存储当登陆的时候,再将加密后的密码和数据库中加密的密码相匹配.npm: https://www.npmjs.com/package/crypto ...
- 力扣1768(java&python)-交替合并字符串(简单)
题目: 给你两个字符串 word1 和 word2 .请你从 word1 开始,通过交替添加字母来合并字符串.如果一个字符串比另一个字符串长,就将多出来的字母追加到合并后字符串的末尾. 返回 合并后的 ...
- 力扣306(java)-累加数(中等)
题目: 累加数 是一个字符串,组成它的数字可以形成累加序列. 一个有效的 累加序列 必须 至少 包含 3 个数.除了最开始的两个数以外,序列中的每个后续数字必须是它之前两个数字之和. 给你一个只包含数 ...
- Dataphin产品核心功能大图(六)发布中心:生产和开发隔离模式下的保护伞
简介:Dataphin,用中台方法论打造企业级好数据.Dataphin是阿里巴巴集团OneData数据治理方法论内部实践的云化输出,一站式提供数据采.建.管.用全生命周期的大数据能力,以助力企业显著 ...
- dotnet 读 WPF 源代码笔记 聊聊 HwndWrapper.GetGCMemMessage 调试消息
我在阅读 WPF 源代码,在 HwndWrapper 的静态构造函数看到了申请了 HwndWrapper.GetGCMemMessage 这个 Windows 消息,好奇这个消息是什么功能的.通过阅读 ...
- dotnet 使用 CsWin32 库简化 Win32 函数调用逻辑
很多开发者,包括开发老司机们,在碰到需要调用 Win32 函数时,都有一个困扰,那就是我应该如何去调用.有两个主要的选项,第一就是自己写 PInvoke 代码,第二就是使用其他大佬给许多 Win32 ...
- 一个在线下载地图XYZ瓦片的网站实现
1. 什么是XYZ瓦片 XYZ瓦片是一种在线地图数据格式,常见的地图底图如Google.OpenStreetMap 等互联网的瓦片地图服务,都是XYZ瓦片,严格来说是ZXY规范的地图瓦片 ZXY规范的 ...