再学安卓 - binder之ServiceManager

本文分析基于Android14(aosp分支android-14.0.0_r28)

前言

上一篇我们对Binder通信的流程有了大致的了解,但始终有一个问题萦绕在我们心里,CountBinder需要AMS和APP建立的已有通道才能传输,那么AMS和APP之间的通道是怎么建立,系统中的第一个Binder通道是怎么建立的?我们可能很自然就想到上一篇提到过的ServiceManager,作为管理模块,它应该就是那个创始者。

ServiceManager的启动

通过第5篇init进程启动我们知道很多系统服务进程都是通过rc脚本启动的,ServiceManager也不例外。来看看init.rc:

rc 复制代码
on init
    ... ...
    start servicemanager

在init阶段,启动了servicemanager。在第6篇zygote中我们提到过,除了系统初始化rc脚本,其他模块脚本基本都和模块代码在相同目录下,ServiceManager也不例外。

rc 复制代码
// frameworks/native/cmds/servicemanager/servicemanager.rc

service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    critical
    file /dev/kmsg w
    onrestart setprop servicemanager.ready false
    onrestart restart --only-if-running apexd
    onrestart restart audioserver
    onrestart restart gatekeeperd
    onrestart class_restart --only-enabled main
    onrestart class_restart --only-enabled hal
    onrestart class_restart --only-enabled early_hal
    task_profiles ServiceCapacityLow
    shutdown critical

由rc脚本得知,ServiceManager对应的二进制可执行文件为/system/bin/servicemanager。

我们故技重施,和第6篇zygote一样,在Android.bp中找到了ServiceManager模块的定义。

bp 复制代码
// frameworks/native/cmds/servicemanager/Android.bp

cc_binary {
    name: "servicemanager",
    defaults: ["servicemanager_defaults"],
    init_rc: ["servicemanager.rc"],
    srcs: ["main.cpp"],
    bootstrap: true,
}

入口理所当然就是main.cpp的main函数。

cpp 复制代码
// frameworks/native/cmds/servicemanager/main.cpp

int main(int argc, char** argv) {
    ... ...
    // 从启动参数中获取binder设备文件位置,默认为/dev/binder
    const char* driver = argc == 2 ? argv[1] : "/dev/binder";

	// ① 创建ProcessState实例,并将其指针赋给static sp<ProcessState> gProcess
	// ProcessState实例是进程单例,主要负责Binder相关的初始化配置
    sp<ProcessState> ps = ProcessState::initWithDriver(driver);
    // 通过系统调用将Binder线程池最大线程数设置为0,即不使用线程池
    ps->setThreadPoolMaxThreadCount(0);
    ... ...

	// ② 创建ServiceManager实例,并将自己添加为Service
    sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
    if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
        LOG(ERROR) << "Could not self register servicemanager";
    }

    // ③ 将ServiceManager与进程、线程管理类关联起来
    IPCThreadState::self()->setTheContextObject(manager);
    ps->becomeContextManager();

	// ④ 启动循环
    sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
    BinderCallback::setupTo(looper);
    ClientCallbackCallback::setupTo(looper, manager);
	... ...

    while(true) {
        looper->pollAll(-1);
    }

    // should not be reached
    return EXIT_FAILURE;
}

总体来看,main函数干了4件事:

  • 创建ProcessState实例
  • 创建ServiceManager实例,并将自己添加为Service
  • 将ServiceManager与进程、线程管理类关联起来
  • 启动循环

① 我们先看看ProcessState的构造函数做了些什么(我们跳过了单例创建ProcessState的模板代码)。

cpp 复制代码
// frameworks/native/libs/binder/ProcessState.cpp

ProcessState::ProcessState(const char* driver)
      : mDriverName(String8(driver)),
        mDriverFD(-1),
        mVMStart(MAP_FAILED),
        ... ...
        // 线程池最大线程数
        mMaxThreads(DEFAULT_MAX_BINDER_THREADS),
        mCurrentThreads(0),
        mKernelStartedThreads(0),
        ... ...
        mThreadPoolStarted(false),
        mThreadPoolSeq(1),
        mCallRestriction(CallRestriction::NONE) {
    // 打开Binder设备文件
    base::Result<int> opened = open_driver(driver);

    if (opened.ok()) {
        // 通过系统调用mmap将用户虚拟空间和内核虚拟空间映射到同一块物理内存上
        // 该内存区域大小为BINDER_VM_SIZE(1M - (内存页大小 * 2)),即Binder通信传输内容的大小限制
        // Android 14及之前内存页大小都为4K
        mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
                        opened.value(), 0);
        ... ...
    }
	... ....
    if (opened.ok()) {
        mDriverFD = opened.value();
    }
}

② 再来看看ServiceManager,它的类结构如下:

可以看出,ServiceManager也是一个BBinder,这与我们上一篇的结论吻合:BBinder就是Binder接口本身在native层的形态。因为ServiceManager是管理Service的类,所以它作为这些Service的Server端,提供注册、查询的功能,非常符合逻辑。此外,ServiceManager本身的业务接口由IServiceManager定义,比如:注册函数、查询函数等。

接着,ServiceManager调用了addService函数将自己添加到Service集合中。

cpp 复制代码
// frameworks/native/cmds/servicemanager/ServiceManager.cpp

Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
    ... ...
    // 将Service添加到map<std::string, Service>中,其中Service是代表服务的结构体,binder字段存放IBinder接口指针
    mNameToService[name] = Service{
            .binder = binder,
            .allowIsolated = allowIsolated,
            .dumpPriority = dumpPriority,
            .ctx = ctx,
    };
	... ...
    return Status::ok();
}

③ 将ServiceManager作为BBinder保存到IPCThreadState的字段the_context_object中。IPCThreadState是线程单例,它与ProcessState类作用类似,但它保存Binder线程相关的属性且负责与内核的数据交互。 接着调用ProcessState#becomeContextManager将其在Binder驱动中注册为上下文管理者。讲人话就是告诉Binder驱动当前进程是ServiceManager进程,以后在用户空间中这个进程就是Binder大总管。

cpp 复制代码
// frameworks/native/libs/binder/ProcessState.cpp

bool ProcessState::becomeContextManager() {
	... ...
    flat_binder_object obj {
        .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
    };

	// 通过系统调用注册Binder上下文管理者
    int result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
    ... ...
    return result == 0;
}

④ Looper#prepare会通过系统调用 epoll_create1()创建一个eventpoll实例。

BinderCallback#setupTo 向eventpoll注册Bidner设备文件描述符,这样当设备文件发生变化时,就会调用BinderCallback#handleEvent() -> IPCThreadState#handlePolledCommands() -> getAndExecuteCommand()。getAndExecuteCommand在上一篇中我们提到过,它读取驱动发送的消息,根据通信指令进行处理。

cpp 复制代码
// frameworks/native/cmds/servicemanager/main.cpp

class BinderCallback : public LooperCallback {
public:
    static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
	    // 创建回调
        sp<BinderCallback> cb = sp<BinderCallback>::make();

        int binder_fd = -1;
        // 将Binder设备文件描述符赋给binder_fd
        IPCThreadState::self()->setupPolling(&binder_fd);
        LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);

		// 将Binder设备文件描述符注册到Looper监听,并传入callback
		// 当有Binder驱动的消息来临时,handleEvent函数就会被回调
        int ret = looper->addFd(binder_fd,
                                Looper::POLL_CALLBACK,
                                Looper::EVENT_INPUT,
                                cb,
                                nullptr /*data*/);
        LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper");

        return cb;
    }

    int handleEvent(int /* fd */, int /* events */, void* /* data */) override {
        IPCThreadState::self()->handlePolledCommands();
        // 返回1表示继续接收回调,返回0表示结束接收回调
        return 1;
    }
};

ClientCallbackCallback#setupTo 向eventpoll注册一个定时文件描述符,该文件每5秒发生一次变化,变化时会回调ClientCallbackCallback#handleEvent -> ServiceManager#handleClientCallbacks。handleClientCallbacks会通知Server端当前是否有Client在使用它。当然前提是Server端需要主动调用registerClientCallback接口注册回调。

cpp 复制代码
// frameworks/native/cmds/servicemanager/main.cpp

class ClientCallbackCallback : public LooperCallback {
public:
    static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
        // 创建回调
        sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager);

		// 创建一个定时文件描述符
        int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
        LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);

        itimerspec timespec {
            .it_interval = {
                .tv_sec = 5,
                .tv_nsec = 0,
            },
            .it_value = {
                .tv_sec = 5,
                .tv_nsec = 0,
            },
        };

		// 设置interval为5秒
        int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, &timespec, nullptr);
        LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno);

		// 将文件描述符注册到Looper监听,并传入callback
		// 当文件描述符变化时也就是5秒间隔到来时,handleEvent函数就会被回调
        int addRes = looper->addFd(fdTimer,
                                   Looper::POLL_CALLBACK,
                                   Looper::EVENT_INPUT,
                                   cb,
                                   nullptr);
        LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper");

        return cb;
    }

    int handleEvent(int fd, int /*events*/, void* /*data*/) override {
        uint64_t expirations;
        int ret = read(fd, &expirations, sizeof(expirations));
        if (ret != sizeof(expirations)) {
            ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno);
        }

        mManager->handleClientCallbacks();
        return 1;
    }
    ... ...
};

最后,调用Looper#pollAll启动消息循环。

ServiceManager的启动并不复杂,主要是通过ioctl系统调用跟Binder驱动交互,完成一系列的初始化工作,最后启动循环等待处理消息。但其中有一个很大的疑点:为什么要将线程池数目设置为0?ServiceManager作为Binder通信的Server端之一,为什么没有像上一篇讲的那样采用线程池的方案来处理消息?

个人认为有以下两点原因:

  1. 通信频率低:ServiceManager仅仅在添加Service和通道建立阶段(Client端一般都会缓存来自Server端的Proxy)有一定的通信量,真正的C/S通信时无需ServiceManager参与,因此它不需要线程池支持,也能完成使命。
  2. 复用需求低:ServiceManager作为一个特殊的Binder Server端,它的生命周期几乎与整个系统相当,但它的复用性需求并不高。反观普通的Binder Server端,通信次数可能很多,但通信完成之后不再需要,为了减少线程创建回收的性能损耗,线程池的高效利用是最合理的方案。

获取Service

上一篇我们讲Binder接口的序列化时就提到通过ActivityManager.getService()可以获得IActivityManager接口,这是AMS对外开放的远程接口。那么我们就以IActivityManager的获取为例,看看获取Service接口的流程。

java 复制代码
// frameworks/base/core/java/android/app/ActivityManager.java

public static IActivityManager getService() {  
    return IActivityManagerSingleton.get();  
}

private static final Singleton<IActivityManager> IActivityManagerSingleton =  
        new Singleton<IActivityManager>() {  
            @Override  
            protected IActivityManager create() {  
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);  
                final IActivityManager am = IActivityManager.Stub.asInterface(b);  
                return am;  
            }  
        };
cpp 复制代码
// frameworks/base/core/java/android/os/ServiceManager.java

public static IBinder getService(String name) {  
    try {
	    // 尝试从缓存中获取IActivityManager,若没有,则通过rawGetService函数获取
        IBinder service = sCache.get(name);  
        if (service != null) {  
            return service;  
        } else {  
            return Binder.allowBlocking(rawGetService(name));  
        }  
    } catch (RemoteException e) {  
        Log.e(TAG, "error in getService", e);  
    }  
    return null;  
}

private static IBinder rawGetService(String name) throws RemoteException {  
    ... ...
    // 首先通过getIServiceManager获取ServiceManager的BpBinder,之后再调用getService获取IActivityManager
    final IBinder binder = getIServiceManager().getService(name);
    ... ...
    return binder;  
}

具体看看ServiceManager的Proxy类是怎么获取的。

java 复制代码
// frameworks/base/core/java/android/os/ServiceManager.java

private static IServiceManager getIServiceManager() {  
    if (sServiceManager != null) {  
        return sServiceManager;  
    }  
  
    // Find the service manager  
    sServiceManager = ServiceManagerNative  
            .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));  
    return sServiceManager;  
}
cpp 复制代码
// frameworks/base/core/jni/android_util_Binder.cpp

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz) {
	// 获取BpBinder指针
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    // javaObjectForIBinder我们在上一篇中已经分析过
    // 它的主要作用就是根据传入的IBinder创建Java层的BinderProxy并返回
    return javaObjectForIBinder(env, b);
}
cpp 复制代码
// frameworks/native/libs/binder/ProcessState.cpp

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/) {
	// getStrongProxyForHandle也是上一篇分析过的函数,作用是创建BpBinder实例并将传入的handle保存起来
	// handle可以简单理解为S端接口的编号,binder驱动将其与真正的远端接口映射起来,实现关联
    sp<IBinder> context = getStrongProxyForHandle(0);
    ... ...
    return context;
}

可以看到这里的handle直接硬编码为0,这就是从无到有跟ServiceManager取得联系的关键。Binder机制默认ServiceManager的handle为0,是驱动在内核中创建的第一个Binder节点。

到这里,我们已经获得了ServiceManager的Proxy,回到rawGetService函数中,接下来就是调用getService函数获取IActivityManager接口。

cpp 复制代码
// frameworks/base/core/java/android/os/ServiceManager.java

private static IBinder rawGetService(String name) throws RemoteException {  
    ... ...
    // getIServiceManager()最终返回经过包装的ServiceManagerProxy,调用它的getService
    final IBinder binder = getIServiceManager().getService(name);
    ... ...
    return binder;  
}
java 复制代码
// frameworks/base/core/java/android/os/ServiceManagerNative.java

public IBinder getService(String name) throws RemoteException {  
    // mServierManger为远端接口,因此ServiceManager.cpp中必定有checkService的函数实现
    return mServiceManager.checkService(name);  
}

调用过程我们已经很熟悉了,这里就不跟踪了,直接看checkService的函数实现。

cpp 复制代码
// frameworks/native/cmds/servicemanager/ServiceManager.cpp

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) {
    ... ...
    sp<IBinder> out;
    Service* service = nullptr;
    // 从mNameToService map中查找目标service的binder
    if (auto it = mNameToService.find(name); it != mNameToService.end()) {
        service = &(it->second);
        if (!service->allowIsolated && is_multiuser_uid_isolated(ctx.uid)) {
            return nullptr;
        }
        out = service->binder;
    }
    ... ...
    if (!out && startIfNotFound) {
	    // 若对应的服务未找到,尝试启动该服务
        tryStartService(name);
    }
    ... ...
    return out;
}

void ServiceManager::tryStartService(const std::string& name) {
	... ...
	// 启动一个新的线程,修改属性系统中的属性,init进程会被唤醒,尝试启动该服务
    std::thread([=] {
        if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
            ALOGI("Tried to start aidl service %s as a lazy service, but was unable to. Usually "
                  "this happens when a "
                  "service is not installed, but if the service is intended to be used as a "
                  "lazy service, then it may be configured incorrectly.",
                  name.c_str());
        }
    }).detach();
}

若所需Service已注册,则会返回目标Binder(IActivityManager),通过binder驱动将其转发给调用方,流程结束。 若尚未注册,则ServiceManager尝试通过属性系统异步启动该服务,此时会返回空指针。这里就有疑点了,若启动成功,Client如何才能获得通知继续流程呢?请看注册Service流程,会有我们想要的答案。

注册Service

本小节依然以我们目前最熟悉的IActivityManager为例,看看它是如何添加到ServiceManager中的。延续我们之前的风格,稍微发散,凡事都追根问底一下,既然Binder接口是AMS跟外界通信的唯一通道,那么应该在启动阶段就会去ServiceManager注册接口。因此,我们回到第7篇SystemServer的启动流程,当时只是轻描淡写的提了一下startBootstrapServices函数会启动基础服务,其中就包括ActivityManagerService。

java 复制代码
// frameworks/base/services/java/com/android/server/SystemServer.java

private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
	... ...
	t.traceBegin("SetSystemProcess");
	mActivityManagerService.setSystemProcess();
	t.traceEnd();
	... ...
}

函数中通过反射创建AMS实例的过程我们跳过,创建完成之后,调用了setSystemProcess,启动注册。

java 复制代码
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public void setSystemProcess() {
    ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,  
                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
    ... ...
}
java 复制代码
// frameworks/base/core/java/android/os/ServiceManager.java

public static void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority) {
    try {
	    // 调用IServiceManager的addService接口
        getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
    } catch (RemoteException e) {
        Log.e(TAG, "error in addService", e);
    }
}

根据我们之前的分析经验,IServiceManager的addService接口实现必定在ServiceManager.cpp中。

cpp 复制代码
// frameworks/native/cmds/servicemanager/ServiceManager.cpp

Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
    ... ...

	// 注册通知功能:通过registerForNotifications接口注册回调,可以在相应的service注册成功之后收到回调
    if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) {
        // See also getService - handles case where client never gets the service,
        // we want the service to quit.
        mNameToService[name].guaranteeClient = true;
        CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false));
        mNameToService[name].guaranteeClient = true;

        for (const sp<IServiceCallback>& cb : it->second) {
            // permission checked in registerForNotifications
            cb->onRegistration(name, binder);
        }
    }

    return Status::ok();
}

addService函数就是本篇开头ServiceManager启动时将自己注册为Service时调用的那个函数。不过,我们这里关注一下刚才我们忽略的部分。注册通知功能就是我们上一小节疑点的答案。它对于关心某个服务是否已启动的Client来说非常有用。因为Client在getService时服务若未启动,ServiceManager会尝试通过属性系统启动该服务。而Client不可能同步等待启动结果,因此需要ServiceManager提供此回调功能,实现通知。当然,前提是Client需要主动调用registerForNotifications接口注册回调。

至此,注册流程完毕。

总结

本篇介绍了ServiceManager的启动、注册和获取Service的源码流程。经过分析得出,ServiceManger是独立的系统进程,采用单线程eventpoll方案实现消息机制。ServiceManager类作为BBinder的子类,本身也是一个Binder接口,启动时将自己也注册到了管理集合中,命名为"manager"。Client端通过handle为0的硬编码获取ServiceManager的Proxy接口,因为ServiceManager调用系统调用将自己注册为"Binder大管家"时驱动就在内核中将其handle值设为了0。

相关推荐
冉冉同学17 分钟前
【HarmonyOS NEXT】解决微信浏览器无法唤起APP的问题
android·前端·harmonyos
韶博雅1 小时前
mysql表类型查询
android·数据库·mysql
studyForMokey1 小时前
【Android学习记录】工具使用
android·学习
小wanga1 小时前
【MySQL】索引特性
android·数据库·mysql
牛了爷爷2 小时前
php伪协议
android·开发语言·php
鸿蒙布道师2 小时前
鸿蒙NEXT开发文件预览工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师2 小时前
鸿蒙NEXT开发全局上下文管理类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
树獭非懒2 小时前
ContentProvider存在的意义:从Android沙箱机制看安全数据共享的设计哲学
android·客户端
恋猫de小郭2 小时前
IntelliJ IDEA 2025.1 发布 ,默认 K2 模式 | Android Studio 也将跟进
android·前端·flutter
tan &3 小时前
Android开发案例——简单计算器
android