再学安卓 - 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。

相关推荐
CYRUS_STUDIO26 分钟前
使用 AndroidNativeEmu 调用 JNI 函数
android·逆向·汇编语言
梦否31 分钟前
【Android】类加载器&热修复-随记
android
徒步青云1 小时前
Java内存模型
android
今阳1 小时前
鸿蒙开发笔记-6-装饰器之@Require装饰器,@Reusable装饰器
android·app·harmonyos
-优势在我6 小时前
Android TabLayout 实现随意控制item之间的间距
android·java·ui
hedalei6 小时前
android13修改系统Launcher不跟随重力感应旋转
android·launcher
Indoraptor7 小时前
Android Fence 同步框架
android
峥嵘life8 小时前
DeepSeek本地搭建 和 Android
android
叶羽西8 小时前
Android14 Camera框架中Jpeg流buffer大小的计算
android·安卓
jiasting8 小时前
Android 中 如何监控 某个磁盘有哪些进程或线程在持续的读写
android