属性服务端的启动以及通信架构

属性服务端的启动以及通信架构

  • init进程与属性服务线程之间通过本地套接字socket进行通信。
  • 客户端进程与属性服务线程(init属性服务进程)之间通信: /dev/socket/property_service
  • 属性服务线程位于init进程中。
  • 如要修改持久化的属性,在属性服务线程中还会通过调用属性的持久化线程服务的接口,以队列的方式发送请求。

源码分析

  1. StartPropertyService
c 复制代码
void StartPropertyService(int* epoll_socket) {
    InitPropertySet("ro.property_service.version", "2");

    int sockets[2];

    // 创建双向通信的unix 套接字,用于propety_service 和 init 进程之间的通信
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
    }
    *epoll_socket = from_init_socket = sockets[0];  // 一个给 init 进程
    init_socket = sockets[1];  // 一个给 service
    StartSendingMessages();

    // 创建监听socket, /dev/socket/property_service 这个节点
    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   /*passcred=*/false, /*should_listen=*/false, 0666, /*uid=*/0,
                                   /*gid=*/0, /*socketcon=*/{});
        result.ok()) {
        property_set_fd = *result;
    } else {
        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
    }

    // 最多允许8个客户端连接到 property_service
    listen(property_set_fd, 8);

    // 创建服务线程
    auto new_thread = std::thread{PropertyServiceThread};
    property_service_thread.swap(new_thread);

    auto async_persist_writes =
            android::base::GetBoolProperty("ro.property_service.async_persist_writes", false);

    // 如果支持异步写入
    if (async_persist_writes) {
        persist_write_thread = std::make_unique<PersistWriteThread>();
    }
}
  1. PropertyServiceThread
c 复制代码
static void PropertyServiceThread() {
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        LOG(FATAL) << result.error();
    }

    // 接收来自系统的属性设置处理
    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
        !result.ok()) {
        LOG(FATAL) << result.error();
    }

    // 接收来自init 进程的消息
    if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
        LOG(FATAL) << result.error();
    }

    while (true) {
        auto epoll_result = epoll.Wait(std::nullopt);
        if (!epoll_result.ok()) {
            LOG(ERROR) << epoll_result.error();
        }
    }
}
  1. handle_property_set_fd、PROP_MSG_SETPROP 和 PROP_MSG_SETPROP2
c 复制代码
static void handle_property_set_fd() {
    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */

    // 接收一个来自客户端的连接
    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
    if (s == -1) {
        return;
    }

    ucred cr;
    socklen_t cr_size = sizeof(cr);
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
        close(s);
        PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
        return;
    }

    SocketConnection socket(s, cr);
    uint32_t timeout_ms = kDefaultSocketTimeout;

    uint32_t cmd = 0;
    // 接收cmd
    if (!socket.RecvUint32(&cmd, &timeout_ms)) {
        PLOG(ERROR) << "sys_prop: error while reading command from the socket";
        socket.SendUint32(PROP_ERROR_READ_CMD);
        return;
    }

    // 处理cmd 类型
    switch (cmd) {
    case PROP_MSG_SETPROP: {
        char prop_name[PROP_NAME_MAX];
        char prop_value[PROP_VALUE_MAX];

        // 接收name 及value
        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
          return;
        }

        prop_name[PROP_NAME_MAX-1] = 0;
        prop_value[PROP_VALUE_MAX-1] = 0;

        // 获取上下文
        std::string source_context;
        if (!socket.GetSourceContext(&source_context)) {
            PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
            return;
        }

        const auto& cr = socket.cred();
        std::string error;

        // 调用 handler 处理
        auto result = HandlePropertySetNoSocket(prop_name, prop_value, source_context, cr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }

        break;
      }

    case PROP_MSG_SETPROP2: {
        std::string name;
        std::string value;
        if (!socket.RecvString(&name, &timeout_ms) ||
            !socket.RecvString(&value, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
          socket.SendUint32(PROP_ERROR_READ_DATA);
          return;
        }

        std::string source_context;
        if (!socket.GetSourceContext(&source_context)) {
            PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
            socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
            return;
        }

        // HandlePropertySet takes ownership of the socket if the set is handled asynchronously.
        const auto& cr = socket.cred();
        std::string error;
        auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
        if (!result) {
            // Result will be sent after completion.
            return;
        }
        if (*result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }
        socket.SendUint32(*result);
        break;
      }

    default:
        LOG(ERROR) << "sys_prop: invalid command " << cmd;
        socket.SendUint32(PROP_ERROR_INVALID_CMD);
        break;
    }
}
  1. persist_write_thread
c 复制代码
static std::optional<uint32_t> PropertySet(const std::string& name, const std::string& value,
                                           SocketConnection* socket, std::string* error) {
    size_t valuelen = value.size();

    if (!IsLegalPropertyName(name)) {
        *error = "Illegal property name";
        return {PROP_ERROR_INVALID_NAME};
    }

    if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
        *error = result.error().message();
        return {PROP_ERROR_INVALID_VALUE};
    }

    prop_info* pi = (prop_info*)__system_property_find(name.c_str());
    if (pi != nullptr) {
        // ro.* properties are actually "write-once".
        if (StartsWith(name, "ro.")) {
            *error = "Read-only property was already set";
            return {PROP_ERROR_READ_ONLY_PROPERTY};
        }

        __system_property_update(pi, value.c_str(), valuelen);
    } else {
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
        if (rc < 0) {
            *error = "__system_property_add failed";
            return {PROP_ERROR_SET_FAILED};
        }
    }

    // Don't write properties to disk until after we have read all default
    // properties to prevent them from being overwritten by default values.
    if (socket && persistent_properties_loaded && StartsWith(name, "persist.")) {
        if (persist_write_thread) {
            // 如果有异步persist 线程,调用线程发消息
            persist_write_thread->Write(name, value, std::move(*socket));
            return {};
        }
        // 同步操作
        WritePersistentProperty(name, value);
    }

    // 通知属性发生变化
    NotifyPropertyChange(name, value);
    return {PROP_SUCCESS};
}
c 复制代码
void PersistWriteThread::Write(std::string name, std::string value, SocketConnection socket) {
    {
        std::unique_lock<std::mutex> lock(mutex_);

        // std::deque<std::tuple<std::string, std::string, SocketConnection>> work_;
        // 从队列中取出一个work_对象
        work_.emplace_back(std::move(name), std::move(value), std::move(socket));
    }
    cv_.notify_all();
}

总结

相关推荐
冬奇Lab23 分钟前
稳定性性能系列之九——启动性能优化:Boot、冷启动与热启动
android·性能优化
没有bug.的程序员26 分钟前
Istio 架构全景解析:控制面 vs 数据面、核心组件与流量路径深度拆解
微服务·云原生·架构·istio·架构设计·envoy·servicemesh
STCNXPARM27 分钟前
Android 显示系统 - View体系、WMS
android·wms·view·android显示子系统
weixin_4469388730 分钟前
谷歌play上架广告app
android
Kapaseker1 小时前
初级与中级的Android面试题区别在哪里
android·kotlin
Kapaseker1 小时前
AOSP 发布节奏调整:一年两更
android
huibin1478523691 小时前
开机后无网络,多次重启手机发现开机时间永远是版本编译时间(高通)
android
m0_598177231 小时前
MYSQL开发- (1)
android·java·mysql
2501_915909061 小时前
iOS 应用在混淆或修改后,如何完成签名、重签名与安装测试
android·ios·小程序·https·uni-app·iphone·webview
程序员小胖胖1 小时前
每天一道面试题之架构篇|Java应用无感热补丁系统架构设计
java·架构·系统架构