Initialize the

The init process starts service_manager and Zygote using init.rc. Zygote then starts system_server and various application processes. This means that SM starts before all application processes

After launching sm, there are several preparations to be done. Only after completion can SM become a real butler and accept calls from clients:

  1. Open binder drive
  2. Register yourself as Service_manager
  3. Wait for events to arrive through a loop. Prior to this step, the SM interacts with the Binder driver by telling the binder driver that it has entered a loop

Sm’s events all start in the main function

// strong pointer, used to deal with the possible occurrence of wild Pointers in c++ and other problems, can be understand as Java strong reference
// This step is to open and register
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
// Step 2, register yourself as manager service
sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
if(! manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
     LOG(ERROR) << "Could not self register servicemanager";
}
// ...
ps->becomeContextManager(nullptr.nullptr);
// Step 3: Wait
BinderCallback::setupTo(looper);
Copy the code

Open the

The first step creates the ProcessState object and calls the constructor of ProcessState, which calls Open_driver. ProcessState is also saved as the global member gProcess. This will be used later by IPCThreadState.

The following methods are excerpts, and only positive logic is guaranteed:

The // argument is the /dev/binder string
ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
    , mDriverFD(open_driver(driver))
    , mVMStart(MAP_FAILED)
{
    if (mDriverFD >= 0) {
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); }}static int open_driver(const char *driver)
{
    // First open the binder driver through open and finally go to the binder_open method
    int fd = open(driver, O_RDWR | O_CLOEXEC);
    if (fd >= 0) {
        int vers = 0;
        // Compare the version number
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS; / / value is 15
        // ioctl calls binder_ioctl
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);  
    } else{}return fd;
}
Copy the code

When open_driver is turned on and started, it first compares a version number and calls BINDER_SET_MAX_THREADS, which passes the value 15 to the Binder driver. At binder_open, a binder_proc is created, and this 15 is eventually set to binder_proc->max_threads. Since these two operations do not need to communicate with third-party processes, they can be invoked without MMAP.

Back to the constructor, after open_driver(), mDriverFD points to the corresponding FD of the binder driver’s device file, which is normally at least 0, so mmap() is called again. And use void * mVMStart to point to the return value of mmap. So at this point SM stores a memory map with the kernel

registered

The first step is to register with addService locally and the second step is to notify the Binder driver

sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
if(! manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
        LOG(ERROR) << "Could not self register servicemanager";
}
// Record manager using the_context_object of this process
IPCThreadState::self() - >setTheContextObject(manager);
// Notify binder drivers
ps->becomeContextManager(nullptr.nullptr);
Copy the code

Start with addService(), which simply stores the ServiceManager instance and Manager string into a map

// Start by listing the attributes used by addService
using ServiceMap = std::map<std::string, Service>;
ServiceMap mNameToService;

/ / the addService excerpt

// Name = "manager",binder = ServiceManager instance, allowIsolated = false
// dumpPriority = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT
Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
    // omit security checks
    // Emplace stores key-value in map
    auto entry = mNameToService.emplace(name, Service {
        .binder = binder,
        .allowIsolated = allowIsolated,
        .dumpPriority = dumpPriority,
        .debugPid = ctx.debugPid,
    });
    return Status::ok(a); }Copy the code

BecomeContextManager (), which calls the binder_ioctl method via the IOCtl system

/ / ProcessState
// mDriverFD is assigned in the constructor and corresponds to the FD returned by opening the Binder device file
// Therefore, this sentence is executed to binder_ioctl, which sets sm to manager
result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
Copy the code

Wait for events

The third line in Main calls setupTo, a static method called BinderCallback

class BinderCallback : public LooperCallback {
public:
    static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
        sp<BinderCallback> cb = new BinderCallback;

        int binder_fd = - 1;
        // self() can be understood as Java getInstance(), which returns an object via static methods
        // So the IPCThreadState constructor is called
        // Internally points to the global ProcessState object via mProcess
        IPCThreadState::self() - >setupPolling(&binder_fd);

        // Pass the newly written value to the binder driver
        IPCThreadState::self() - >flushCommands(a);// Looper is looper, the core native implementation of the handler mechanism
        // binder_fd is monitored by epoll
        // The following handleEvent() is called when binder_fd is readable
        int ret = looper->addFd(binder_fd,
                                Looper::POLL_CALLBACK,
                                Looper::EVENT_INPUT,
                                cb,
                                nullptr /*data*/);
        return cb;
    }

    int handleEvent(int /* fd */.int /* events */.void* /* data */) override {
        IPCThreadState::self() - >handlePolledCommands(a);return 1;  // Continue receiving callbacks.}};Copy the code

setupPolling This method writes an int value BC_ENTER_LOOPER to ipcThreadState. mOut, which is passed to the binder driver at flushCommands below. Binder_fd also points to the fd corresponding to binder device files when open(‘/dev/binder’).

Talk about the addFd method below. It is a method in Looper that listens for FDS corresponding to binder via the epoll mechanism and calls the following handleEvent when readable.

At this point, SM is ready to receive calls from clients. In older versions, the SM loop was implemented using continuous messaging to binder drivers instead of relying on Looper.

Receive commands

As mentioned above, SM uses the epoll mechanism to execute handleEvent whenever binder_fd changes. This method simply tells SM that binder is readable and that the information needs to be read from binder again. HandlePolledCommands executes to getAndExecuteCommand()

// IPCThreadState::getAndExecuteCommand()

// Interact with binder drivers to read arrays
// talkWithDriver defaults to true when no arguments are passed. The default parameter function is used here
result = talkWithDriver(a);if (result >= NO_ERROR) {
    cmd = mIn.readInt32(a); result =executeCommand(cmd);
}
Copy the code

Now it’s time to analyze executeCommand(). But let’s first look at the implementation of the Java layer SM.

Java layer