Android4.4属性系统-初始化(下)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 笔记

5.2 property_service.c解析

system/core/init/property_service.c

#include "property_service.h"
#include <sys/socket.h>
#include <sys/_system_properties.h>
#define PERSISTENT_PROPERTY_DIR  "/data/property"
//标识persistent proerties是否加载完成
static int persistent_properties_loaded = 0;
//标识property初始化是否完成
static int property_area_inited = 0;
typedef struct {
    size_t size;  //共享内存空间大小
    int fd;  //句柄
} workspace;
//property初始化
void property_init(void)
{
    init_property_area();
}
/*init_property_area 初始化共享内存空间。在 Android 系统中,所有的进程是共享
*系统属性值的,Android 系统提供了一个名为属性的保存空间,在共享内存区域中
*(ASHMEM),创建并初始化属性域*/
static workspace pa_workspace;
static int init_property_area(void)
{
    if (property_area_inited)  //只能初始化一次
        return -1;
    //初始化共享内存区域,位于system_properties.c
    if(__system_property_area_init())
        return -1;
    //初始化工作空间,大小初始化为0
    if(init_workspace(&pa_workspace, 0))
        return -1;
    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
    property_area_inited = 1;  //设置初始化完成标志
    return 0;
}
 //初始化工作空间,workspace在该文件中被定义,参数<工作空间,空间大小>
static int init_workspace(workspace *w, size_t size)
{
    void *data;
    //#define PROP_FILENAME "/dev/__properties__"  定义在_system_properties.h
    int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);  //打开内存映射文件
    if (fd < 0)
        return -1;
    w->size = size;  //设置空间大小
    w->fd = fd;  //设置文件句柄,fd 为/dev/__properties__文件句柄
    return 0;
}
void property_load_boot_defaults(void)
{
    //从文件中加载,PROP_PATH_RAMDISK_DEFAULT=“/defalult.prop”
    //定义在_system_properties.h文件中
    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
/*
default.prop 文件中的内容是在 Android系统编译时写入到 boot.img 中的,而每次
开机,boot.img 会进行解压,所以想要修改该文件中的内容可以重新编译系统,或
者修改 boot.img 文件,在/build/core/main.mk 文件中定义了不同属性的值,
ADDITIONAL_DEFAULT_PROPERTIES 变量值就是需要写入到 default.prop 文件中
的内容
build/core/main.mk部分内容如下:
# Target is secure in user builds.
ADDITIONAL_DEFAULT_PROPERTIES += ro.secure=1
# Disallow mock locations by default for user builds
ADDITIONAL_DEFAULT_PROPERTIES += ro.allow.mock.location=0
default.prop文件内容一般如下 :
shell@sp9820w_poc_780:/ $ cat default.prop
#
# ADDITIONAL_DEFAULT_PROPERTIES
#
ro.secure=1
ro.allow.mock.location=0
ro.debuggable=1
camera.disable_zsl_mode=1
persist.sys.usb.config=mtp,adb
*/
//从指定文件中加载属性
static void load_properties_from_file(const char *fn)
{
    char *data;
    unsigned sz;
    //打开文件,读取内容
    data = read_file(fn, &sz);
    if(data != 0) {
        //打开成功,加载property
        load_properties(data);
        //关闭文件
        free(data);
    }
}
//解析property数据,逐行解析
static void load_properties(char *data)
{   
    char *key, *value, *eol, *sol, *tmp;
    sol = data;  //sol指向文件开始
    //char *strchr(const char *str, int c)返回在字符串 str 中第一次出现字符 c 的位置,实现逐行读取
    while((eol = strchr(sol, '\n'))) {  //返回换行符的指针
        key = sol;   //key表示行首的指针
        *eol++ = 0;  //相关于*eol=0; eol++; '\n'替换为0,此时eol为下一行的行首指针
        sol = eol;    //赋值给sol
        value = strchr(key, '=');  //截取一行中'='前面的字符,value指向'='
        if(value == 0) continue;  //如果=前面为空,跳过
        *value++ = 0;  //’=‘替换为0,value指向原'='后面的内容,即实际的value字节地址
        while(isspace(*key)) key++;  //去掉一行前面的空格
        if(*key == '#') continue;  //如果是'#'开头,表示注释,跳过
        tmp = value - 2;  //value-2为key的末尾地址
        while((tmp > key) && isspace(*tmp)) *tmp-- = 0; //判断key末尾是否有空白符,如果有替换为0
        while(isspace(*value)) value++;  //判断value起始内容中是否有空白符,如果有替换为0
        tmp = eol - 2;  //判断value末尾内容中是否有空白符,如果有替换为0
        while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
        //设置属性,关键调用,该方法本章不再继续深究
        property_set(key, value);
    }
}
//启动属性服务
void start_property_service(void)
{
    int fd;
    //1.加载/system/build.prop
    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
    //2.加载 /system/default.prop
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
    //3.加载override props
    load_override_properties();
    /* Read persistent properties after all default values have been loaded. */
    load_persistent_properties();
    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
    if(fd < 0) return;
    fcntl(fd, F_SETFD, FD_CLOEXEC);
    fcntl(fd, F_SETFL, O_NONBLOCK);
    listen(fd, 8);
    property_set_fd = fd;
}

5.3 system_properties.c解析

属性存储在混合(trie)字典树 /(binary)二叉树结构中。每个属性的名称都以'.'分隔,字符和标记放在一起成为一个特里结构。 特里结构的每个级别的兄弟姐妹都存储在一个二叉树。例如,“ro.secure”=“1”可以存储如下:

11.png



特里结构的根结点是空的,后面第二个节点为ro,ro结点是一个二叉树的根结点,它的左孩子为net,右孩子为sys。ro的后驱结点为secure,它也是一个二驻树的根结点,左孩子为com,右孩子为prop,值为1。这样把prop前面的串起来就是ro.secure=1。

bionic/libc/include/sys/system_properties.h

typedef struct prop_info prop_info;
//prop key和value的最大长度
#define PROP_NAME_MAX   32
#define PROP_VALUE_MAX  92

bionic/libc/include/sys/_system_properties.h

#define PROP_AREA_MAGIC   0x504f5250
#define PROP_AREA_VERSION 0xfc6ed0ab
#define PROP_AREA_VERSION_COMPAT 0x45434f76
#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"
#define PROP_PATH_FACTORY          "/factory/factory.prop"
//定义映射内存空间的大小128K
#define PA_SIZE         (128 * 1024)
//prop service的名称
#define PROP_SERVICE_NAME "property_service"
//prop内存映射文件
#define PROP_FILENAME "/dev/__properties__"

bionic/libc/bionic/system_properties.c

static char property_filename[PATH_MAX] = PROP_FILENAME;
//property共享内存结构体
struct prop_area {
    unsigned bytes_used;  //已使用的字节数
    unsigned volatile serial; //serial 表示整个 prop_area 被修改的次数,包括
新增加的
    unsigned magic;
    unsigned version;
    unsigned reserved[28];
    char data[0];  //实际的共享内存区域
};
//定义全局变量
typedef struct prop_area prop_area;
//property info结构体
struct prop_info {
    //prop_info 中的 serial,其高8 位表示该 prop_info 中 name 的长度,而低 24 位表示该 prop_info 被更新的次数
    unsigned volatile serial;
    char value[PROP_VALUE_MAX];
    char name[0];
};
//定义全局变量
typedef struct prop_info prop_info;
//初始化共享内存
int __system_property_area_init()
{
    return map_prop_area_rw();
}
//初始化可读写的共享内存区域
static int map_prop_area_rw()
{
    prop_area *pa; //prop_area结构体定义在该文件中
    int fd;
    int ret;
    /* dev is a tmpfs that we can use to carve a shared workspace
     * out of, so let's do that...
     */
    //dev是一个临时文件系统,我们可以用来定制共享工作区
    fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
    if (fd < 0) {
        if (errno == EACCES) {
            /* for consistency with the case where the process has already
             * mapped the page in and segfaults when trying to write to it
             */
            abort();
        }
        return -1;
    }
    //fcntl 根据文件描述词来操作文件特性,给 init_workspace 中创建的文件描述符设置文件描述符标记
    ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
    if (ret < 0)
        goto out;
    if (ftruncate(fd, PA_SIZE) < 0)
        goto out;
    pa_size = PA_SIZE;  //设置映射内存空间大小
    pa_data_size = pa_size - sizeof(prop_area);  //数据空间大小=总空间-prop_area结构体占用的空间
    compat_mode = false;
    /*映射为共享内存,mmap将一个文件或者其它对象映射进内存
      *这里是关键的部分,关于mmap可以参考(https://blog.csdn.net/yangle4695/article/details/52139585)
    */
    /*参数<映射的内存起始地址为NULL=0,映射大小,可读|可写,
      *MAP_SHARED :对映射区域的写入数据会复制回文件内, 而且允许其他映射该文件的进程共享
      * fd文件描述符, offset:表示被映射对象(即文件)从那里开始对映,通常都是用0>
      * 返回被映射区的指针pa
      */
    pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(pa == MAP_FAILED)
        goto out;
    memset(pa, 0, pa_size);  //初始化映射内存区域为 0
    pa->magic = PROP_AREA_MAGIC;
    pa->version = PROP_AREA_VERSION;
    /* reserve root node */
    pa->bytes_used = sizeof(prop_bt);
    /* plug into the lib property services */
    //将 pa 赋值给__system_property_area__
    __system_property_area__ = pa;
    close(fd);
    return 0;
out:
    close(fd);
    return -1;
}
//查询prop
const prop_info *__system_property_find(const char *name)
{
    if (__predict_false(compat_mode)) {
        return __system_property_find_compat(name);
    }
    return find_property(root_node(), name, strlen(name), NULL, 0, false);
}
//前面介绍过了,属性的存储结构为特里结构+二叉树结构,所以查找属性其实就是
//特里结构+二叉树结构的遍历 
static const prop_info *find_property(prop_bt *trie, const char *name,
        uint8_t namelen, const char *value, uint8_t valuelen,
        bool alloc_if_needed)
{
    const char *remaining_name = name;
    while (true) {
        char *sep = strchr(remaining_name, '.');
        bool want_subtree = (sep != NULL);
        uint8_t substr_size;
        prop_bt *root;
        if (want_subtree) {
            substr_size = sep - remaining_name;
        } else {
            substr_size = strlen(remaining_name);
        }
        if (!substr_size)
            return NULL;
        if (trie->children) {
            root = to_prop_obj(trie->children);
        } else if (alloc_if_needed) {
            root = new_prop_bt(remaining_name, substr_size, &trie->children);
        } else {                                                                               
            root = NULL;
        }
        if (!root)
            return NULL;
        trie = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
        if (!trie)
            return NULL;
        if (!want_subtree)
            break;
        remaining_name = sep + 1;
    }
    if (trie->prop) {
        return to_prop_obj(trie->prop);
    } else if (alloc_if_needed) {
        return new_prop_info(name, namelen, value, valuelen, &trie->prop);
    } else {
        return NULL;
    }
}

12.png

5.4 init_parser.c解析

system/core/init/init_parser.c

static list_declare(service_list);
static list_declare(action_list);  //声明action list
static list_declare(action_queue);
//参数<函数指针,函数指针指向函数的参数>
void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
{
    struct action *act;  //定义在init.h中
    struct command *cmd;  //定义在init.h中
    act = calloc(1, sizeof(*act));  //act分配内存
    act->name = name;  //Android 系统属性服务来说,该值为 property_service_init
    list_init(&act->commands);  //初始化,将 listnode 的 next 和 prev 都指向自
    list_init(&act->qlist);  //初始化qlist
    cmd = calloc(1, sizeof(*cmd));  //cmd分配内存
    cmd->func = func;  //设置函数指针,对于属性服务为property_service_init_action函数
    cmd->args[0] = name; //args 表示函数运行的参数值,此处只有一个name参数
    list_add_tail(&act->commands, &cmd->clist); //在action的commands链表的尾部添加节点cmd->clist
    //action_list尾部添加节点act->alist
    list_add_tail(&action_list, &act->alist);
    //
    action_add_queue_tail(act);
}
void action_add_queue_tail(struct action *act)
{
    if (list_empty(&act->qlist)) {  //如果链表为空则添加
        list_add_tail(&action_queue, &act->qlist);
    }
}
//从action_queue获取头节点,并删除头结点 
struct action *action_remove_queue_head(void)
{
    if (list_empty(&action_queue)) {
        return 0;
    } else {
        struct listnode *node = list_head(&action_queue);
        struct action *act = node_to_item(node, struct action, qlist);
        list_remove(node);
        list_init(node);
        return act;
    }
}

queue_builtin_action方法执行完后,action 和 command 结构体以及 action_list 和 action_queue 的关系如下图(queue_builtin_action 在 此 处 并 不 是 最 后 一 次 被 调 用 , 因 此 action_list 以 及action_queue 和 action 之间用虚点线表示,action_list 和 action_queue 之前还有其他的 action结构体变量)


13.png5.5 cutils库解析

目录位置system/core/libcutils,是一些c的函数工具库,现在介绍一些涉及到的文件

system/core/include/cutils/list.h

//可构成双向链表
struct listnode
{   
    struct listnode *next;  
    struct listnode *prev;
};
//宏定义,声明并初始化链表
#define list_declare(name) \
    struct listnode name = { \
        .next = &name, \
        .prev = &name, \
    }

system/core/libcutils/list.c

#include <cutils/list.h>
//初始化链表
void list_init(struct listnode *node)
{
    node->next = node;
    node->prev = node;
}
//末尾添加节点
void list_add_tail(struct listnode *head, struct listnode *item)
{
    item->next = head;
    item->prev = head->prev;
    head->prev->next = item;
    head->prev = item;
}
//移除节点
void list_remove(struct listnode *item)
{
    item->next->prev = item->prev;
    item->prev->next = item->next;
}


六、参考


Android属性系统

Linux 内存映射函数 mmap

trie

Linux 内存映射函数 mmap()函数详解

Android属性系统


目录
相关文章
|
2月前
|
Android开发
基于android-11.0.0_r39,系统应用的手动签名方法和过程
本文介绍了基于Android 11.0.0_r39版本进行系统应用手动签名的方法和解决签名过程中遇到的错误,包括处理`no conscrypt_openjdk_jni-linux-x86_64`和`RegisterNatives failed`的问题。
88 2
|
2月前
|
JavaScript 前端开发 Java
[Android][Framework]系统jar包,sdk的制作及引用
[Android][Framework]系统jar包,sdk的制作及引用
43 0
|
8天前
|
监控 Android开发 iOS开发
深入探索安卓与iOS的系统架构差异:理解两大移动平台的技术根基在移动技术日新月异的今天,安卓和iOS作为市场上最为流行的两个操作系统,各自拥有独特的技术特性和庞大的用户基础。本文将深入探讨这两个平台的系统架构差异,揭示它们如何支撑起各自的生态系统,并影响着全球数亿用户的使用体验。
本文通过对比分析安卓和iOS的系统架构,揭示了这两个平台在设计理念、安全性、用户体验和技术生态上的根本区别。不同于常规的技术综述,本文以深入浅出的方式,带领读者理解这些差异是如何影响应用开发、用户选择和市场趋势的。通过梳理历史脉络和未来展望,本文旨在为开发者、用户以及行业分析师提供有价值的见解,帮助大家更好地把握移动技术发展的脉络。
|
5天前
|
Dart 开发工具 Android开发
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
|
28天前
|
Android开发 UED 开发者
Android经典实战之WindowManager和创建系统悬浮窗
本文详细介绍了Android系统服务`WindowManager`,包括其主要功能和工作原理,并提供了创建系统悬浮窗的完整步骤。通过示例代码,展示了如何添加权限、请求权限、实现悬浮窗口及最佳实践,帮助开发者轻松掌握悬浮窗开发技巧。
55 1
|
2月前
|
Java 物联网 Android开发
移动应用与系统:技术演进与未来展望探索安卓应用开发:从新手到专家的旅程
【8月更文挑战第28天】本文将深入探讨移动应用开发的技术演进、移动操作系统的发展历程以及未来的发展趋势。我们将通过实例和代码示例,展示如何利用最新的技术和工具来开发高效、可靠的移动应用。无论你是初学者还是经验丰富的开发者,这篇文章都将为你提供有价值的信息和见解。 【8月更文挑战第28天】在这个数字时代,掌握安卓应用的开发技能不仅是技术人员的追求,也成为了许多人实现创意和梦想的途径。本文将通过深入浅出的方式,带领读者从零基础开始,一步步走进安卓开发的奇妙世界。我们将探讨如何配置开发环境,理解安卓应用的核心组件,以及如何通过实际编码来构建一个功能完整的应用。无论你是编程新手还是希望提升自己的开发者
|
2月前
|
存储 安全 物联网
Android经典实战之跳转到系统设置页面或其他系统应用页面大全
本文首发于公众号“AntDream”,关注获取更多技巧。文章总结了Android开发中跳转至系统设置页面的方法,包括设备信息、Wi-Fi、显示与声音设置等,并涉及应用详情与电池优化页面。通过简单的Intent动作即可实现,需注意权限与版本兼容性。每日进步,尽在“AntDream”。
168 2
|
2月前
|
安全 Android开发 iOS开发
安卓与iOS的终极对决:哪个系统更适合你?
在智能手机的世界里,安卓和iOS两大操作系统如同两座巍峨的山峰,各自拥有庞大的用户群体。本文将深入浅出地探讨这两个系统的优缺点,并帮助你找到最适合自己的那一款。让我们一起揭开这场技术盛宴的序幕吧!
|
2月前
|
Android开发
AutoX——当Android中clickable属性显示为false,实际可点击的布局如何处理
AutoX——当Android中clickable属性显示为false,实际可点击的布局如何处理
28 0
|
2月前
|
搜索推荐 安全 Android开发
安卓与iOS的较量:哪个系统更适合你?
在智能手机市场中,安卓和iOS两大操作系统一直占据主导地位。本文将从多个方面对这两个系统进行比较,以帮助读者更好地了解它们之间的区别和优劣。我们将重点关注它们的用户界面、性能、安全性、应用生态等方面。无论您是安卓粉丝还是iOS忠实拥趸,这篇文章都将为您提供有价值的信息。让我们一起探索这两个系统的世界吧!