美文网首页Android系统源码学习
Android init进程--属性服务器

Android init进程--属性服务器

作者: 覆水无言 | 来源:发表于2019-12-15 20:01 被阅读0次

    android源码学习目录

    背景

    对于系统或者应用程序来说 ,系统或应用会将自己的一些属性存储到注册表或者硬盘的文件上,这样系统或者应用在重启时会读取属性进行系统初始化,这样系统或者应用就会按照我们希望的方式来运行,而不是每次启动都完全从未运行过的状态。

    介绍--Android属性服务器

    从上文中知道了系统属性的重要,Android也提供了类似这样一种机制,我们称之为属性服务器(property server),
    eg:利用这个服务我们使用Android的adb也能获取Android手机的属性

    adb shell getprop
    
    ##输出
    # [bastet.service.enable]: [true]
    # [bg_fsck.pgid]: [453]
    # [bt.dpbap.enable]: [1]
    

    Android中的实现

    Android init进程一文中我们了解Android的属性服务器是在init进程的main函数中执行的。

    1.初始化
     property_init();  // system/core/init/init.cpp main函数,初始化属性服务器
    
    //property_init函数在 system/core/init/property_service.cpp中
    void property_init() {
        //初始化工作并没有做太多工作,_system_property_area_init函数只是进行了属性服务器所需内存的初始化
       if (__system_property_area_init()) { 
           LOG(ERROR) << "Failed to initialize property area";
           exit(1);
       }
    }
    
    2.属性服务器启动
    start_property_service(); // init.cpp main函数中进行属性服务器启动
    

    这个函数同样在property_service.cpp中。

    void start_property_service() {
        property_set("ro.property_service.version", "2");
        property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                        0666, 0, 0, NULL);   //创建一个socket,
        if (property_set_fd == -1) {
            PLOG(ERROR) << "start_property_service socket creation failed";
            exit(1);
        }
        listen(property_set_fd, 8);  //控制连接数
        //将socket上消息交给handle_property_set_fd函数处理
        register_epoll_handler(property_set_fd, handle_property_set_fd); 
    }
    
    • 之所以称之为属性服务器就是因为上述代码,属性服务器其实是一个socket,用来接受请求。
    • 控制连接数就是最多可以为8个链接提供服务。
    • socket上的消息处理交给了handler_property_set_fd, 这里register_epool_handler将会把property_set_fd交给epoll来监听,监听到的消息再交给handler_property_set_fd函数来处理,

    \color{#ea4335}{注意:} 关于c++的socket实现与建立,C++的epoll事件模型,有兴趣都可以了解一下有助于对底层系统的了解。

    3.服务器处理客户端请求
    //同样在property_service.cpp中
    static void handle_property_set_fd(){
        ......截取部分代码
    
        switch (cmd) {
        case PROP_MSG_SETPROP: {
            char prop_name[PROP_NAME_MAX];
            char prop_value[PROP_VALUE_MAX];
              //如果socket没有接收到属性数据则返回
            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;
            //进行socket接收到的属性数据处理
            handle_property_set(socket, prop_value, prop_value, true);
            break;
          }
      .....
    }
    
    

    这个方法获取了socket链接的使用,和判断了socket传过来的属性name是否合法

    
    static void handle_property_set(SocketConnection& socket,
                                    const std::string& name,
                                    const std::string& value,
                                    bool legacy_protocol) {
      const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
      if (!is_legal_property_name(name)) {   //查看socket传入数据是否合法
        LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\"";
        socket.SendUint32(PROP_ERROR_INVALID_NAME); //不合法返回通知客户端
        return;
      }
    
      struct ucred cr = socket.cred();
      char* source_ctx = nullptr;
      getpeercon(socket.socket(), &source_ctx);
    
      // 判断name是不是ctl.开头,以他开头表示控制属性
      if (android::base::StartsWith(name, "ctl.")) {
       //检查客户端权限
        if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
          //设置控制属性。
          handle_control_message(name.c_str() + 4, value.c_str());
          if (!legacy_protocol) {
            socket.SendUint32(PROP_SUCCESS); //发送给客户端设置结果
          }
        } else {
          //无控制属性权限
          LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
                     << " service ctl [" << value << "]"
                     << " uid:" << cr.uid
                     << " gid:" << cr.gid
                     << " pid:" << cr.pid;
          if (!legacy_protocol) {
            socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
          }
        }
      } else {
        //普通属性
        if (check_mac_perms(name, source_ctx, &cr)) {
          uint32_t result = property_set(name, value);  //设置属性
          if (!legacy_protocol) {
            socket.SendUint32(result);    //发送给客户端结果
          }
        } else {
          //无普通属性权限
          LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
          if (!legacy_protocol) {
            socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
          }
        }
      }
      freecon(source_ctx);    //释放链接
    }
    
    

    这个方法对socket请求的属性进行了权限检查和属性属于那种类型,类型有控制属性和普通属性。

    • 控制属性: 用来执行一些命令,eg:开机的动画
    • 普通属性:只用来设置一些系统普通属性

    我们这里只分析普通属性

    //property_service.cpp
    uint32_t property_set(const std::string& name, const std::string& value) {
        size_t valuelen = value.size();
    
        if (!is_legal_property_name(name)) {  //判断属性name是否合法
            LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
            return PROP_ERROR_INVALID_NAME;
        }
    
        if (valuelen >= PROP_VALUE_MAX) {   //判断属性value长度是否超出范围
            LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                       << "value too long";
            return PROP_ERROR_INVALID_VALUE;
        }
    
        if (name == "selinux.restorecon_recursive" && valuelen > 0) {
            if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
                LOG(ERROR) << "Failed to restorecon_recursive " << value;
            }
        }
    
      //从属性空间中找到该属性
        prop_info* pi = (prop_info*) __system_property_find(name.c_str()); 
        if (pi != nullptr) {
            // ro.* properties are actually "write-once".
            //如果属性以ro.开头,表示属性只读,不能修改,直接返回
            if (android::base::StartsWith(name, "ro.")) {  
                LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                           << "property 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) {
                LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                           << "__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.
        //如果属性名以 persist.开头,则记性处理
        if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
            write_persistent_property(name.c_str(), value.c_str());
        }
        //上文只添加属性,这里设置属性。
        property_changed(name, value);
        return PROP_SUCCESS;
    }
    
    

    从上文代码中可以看到最终的执行,就是判断了属性的各个合法条件和属性是否存在,再进行属性的添加和update.

    相关文章

      网友评论

        本文标题:Android init进程--属性服务器

        本文链接:https://www.haomeiwen.com/subject/ttslnctx.html