Android init进程--属性服务器

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.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 229,963评论 6 542
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,348评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,083评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,706评论 1 317
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,442评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,802评论 1 328
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,795评论 3 446
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,983评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,542评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,287评论 3 358
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,486评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,030评论 5 363
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,710评论 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,116评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,412评论 1 294
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,224评论 3 398
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,462评论 2 378

推荐阅读更多精彩内容