Android init進(jìn)程--屬性服務(wù)器

android源碼學(xué)習(xí)目錄

背景

對(duì)于系統(tǒng)或者應(yīng)用程序來(lái)說(shuō) ,系統(tǒng)或應(yīng)用會(huì)將自己的一些屬性存儲(chǔ)到注冊(cè)表或者硬盤的文件上,這樣系統(tǒng)或者應(yīng)用在重啟時(shí)會(huì)讀取屬性進(jìn)行系統(tǒng)初始化,這樣系統(tǒng)或者應(yīng)用就會(huì)按照我們希望的方式來(lái)運(yùn)行,而不是每次啟動(dòng)都完全從未運(yùn)行過(guò)的狀態(tài)。

介紹--Android屬性服務(wù)器

從上文中知道了系統(tǒng)屬性的重要,Android也提供了類似這樣一種機(jī)制,我們稱之為屬性服務(wù)器(property server),
eg:利用這個(gè)服務(wù)我們使用Android的adb也能獲取Android手機(jī)的屬性

adb shell getprop

##輸出
# [bastet.service.enable]: [true]
# [bg_fsck.pgid]: [453]
# [bt.dpbap.enable]: [1]

Android中的實(shí)現(xiàn)

Android init進(jìn)程一文中我們了解Android的屬性服務(wù)器是在init進(jìn)程的main函數(shù)中執(zhí)行的。

1.初始化
 property_init();  // system/core/init/init.cpp main函數(shù),初始化屬性服務(wù)器

//property_init函數(shù)在 system/core/init/property_service.cpp中
void property_init() {
    //初始化工作并沒(méi)有做太多工作,_system_property_area_init函數(shù)只是進(jìn)行了屬性服務(wù)器所需內(nèi)存的初始化
   if (__system_property_area_init()) { 
       LOG(ERROR) << "Failed to initialize property area";
       exit(1);
   }
}
2.屬性服務(wù)器啟動(dòng)
start_property_service(); // init.cpp main函數(shù)中進(jìn)行屬性服務(wù)器啟動(dòng)

這個(gè)函數(shù)同樣在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);   //創(chuàng)建一個(gè)socket,
    if (property_set_fd == -1) {
        PLOG(ERROR) << "start_property_service socket creation failed";
        exit(1);
    }
    listen(property_set_fd, 8);  //控制連接數(shù)
    //將socket上消息交給handle_property_set_fd函數(shù)處理
    register_epoll_handler(property_set_fd, handle_property_set_fd); 
}
  • 之所以稱之為屬性服務(wù)器就是因?yàn)樯鲜龃a,屬性服務(wù)器其實(shí)是一個(gè)socket,用來(lái)接受請(qǐng)求。
  • 控制連接數(shù)就是最多可以為8個(gè)鏈接提供服務(wù)。
  • socket上的消息處理交給了handler_property_set_fd, 這里register_epool_handler將會(huì)把property_set_fd交給epoll來(lái)監(jiān)聽(tīng),監(jiān)聽(tīng)到的消息再交給handler_property_set_fd函數(shù)來(lái)處理,

\color{#ea4335}{注意:} 關(guān)于c++的socket實(shí)現(xiàn)與建立,C++的epoll事件模型,有興趣都可以了解一下有助于對(duì)底層系統(tǒng)的了解。

3.服務(wù)器處理客戶端請(qǐng)求
//同樣在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沒(méi)有接收到屬性數(shù)據(jù)則返回
        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;
        //進(jìn)行socket接收到的屬性數(shù)據(jù)處理
        handle_property_set(socket, prop_value, prop_value, true);
        break;
      }
  .....
}

這個(gè)方法獲取了socket鏈接的使用,和判斷了socket傳過(guò)來(lái)的屬性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傳入數(shù)據(jù)是否合法
    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.開(kāi)頭,以他開(kāi)頭表示控制屬性
  if (android::base::StartsWith(name, "ctl.")) {
   //檢查客戶端權(quán)限
    if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
      //設(shè)置控制屬性。
      handle_control_message(name.c_str() + 4, value.c_str());
      if (!legacy_protocol) {
        socket.SendUint32(PROP_SUCCESS); //發(fā)送給客戶端設(shè)置結(jié)果
      }
    } else {
      //無(wú)控制屬性權(quán)限
      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);  //設(shè)置屬性
      if (!legacy_protocol) {
        socket.SendUint32(result);    //發(fā)送給客戶端結(jié)果
      }
    } else {
      //無(wú)普通屬性權(quán)限
      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);    //釋放鏈接
}

這個(gè)方法對(duì)socket請(qǐng)求的屬性進(jìn)行了權(quán)限檢查和屬性屬于那種類型,類型有控制屬性和普通屬性。

  • 控制屬性: 用來(lái)執(zhí)行一些命令,eg:開(kāi)機(jī)的動(dòng)畫
  • 普通屬性:只用來(lái)設(shè)置一些系統(tǒng)普通屬性

我們這里只分析普通屬性

//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長(zhǎng)度是否超出范圍
        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.開(kāi)頭,表示屬性只讀,不能修改,直接返回
        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.開(kāi)頭,則記性處理
    if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
        write_persistent_property(name.c_str(), value.c_str());
    }
    //上文只添加屬性,這里設(shè)置屬性。
    property_changed(name, value);
    return PROP_SUCCESS;
}

從上文代碼中可以看到最終的執(zhí)行,就是判斷了屬性的各個(gè)合法條件和屬性是否存在,再進(jìn)行屬性的添加和update.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容