2018年8月12日 星期日

Android Property

相關文章
https://stackoverflow.com/questions/23758994/change-the-model-in-build-prop
https://www.techgainer.com/change-fake-android-device-model-number-and-brand-name/


====================================================================
出處 https://blog.csdn.net/Dylan_Sen/article/details/78757466

本文將從下面三方面簡單分析總結android property:
1. Property的使用方式
2. Property文件的加載
3. Property的存儲

1. Property的使用方式

在工作中經常通過下面三種方式使用property:
1.1 code裡面使用SystemProperties.java和properties.cpp
SystemProperties.java為Jave層提供了下面的方法:
 public static String get(String key){...}
 public static String get(String key, String def){...}
 public static int getInt(String key, int def){...}
 public static long getLong(String key, long def){...}
 public static boolean getBoolean(String key, boolean def){...}
 public static void set(String key, String val){...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
system/core/libcutils/properties.cpp給Native層提供了下面的API:
int property_set(const char *key, const char *value) {...}
int property_get(const char *key, char *value, const char *default_value) {...}
int8_t property_get_bool(const char *key, int8_t default_value) {...}
static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
                                  intmax_t default_value) {...}
int64_t property_get_int64(const char *key, int64_t default_value) {...}
int32_t property_get_int32(const char *key, int32_t default_value){...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
1.2 adb命令
adb的方式為我們調式提供了方便。
[格式] 
adb shell getprop [proptery_name] 
adb shell setprop propterty_name value 
[例子] 
adb shell getprop ro.build.type 
adb shell setprop persist.log.tag.ImsManager V
1.3 通過文件設置默認property;將property放在文件中,init進程去加載文件。
前兩種方式可以讀取所有的property;但是在寫方面,對於ro.* property這種write-once的property是不能覆蓋的。

2. Property文件的加載

Property的初始化,以及相關propety文件的加載都在Init進程中完成。所以這部分內容從init進程的main函數開始。按照main函數中的code順序,主要內容如下:
  • Property area初始化
  • 加載default property文件
  • 創建property service
  • 加載解析rc文件
  • 執行rc文件中的action; system property, persistent property和override property文件會在這個過程中相繼被加載。
main函數會調用proprerty_service.cpp中的property_init()函數來初始化property area。
void property_init() {
    if (__system_property_area_init()) {
        LOG(ERROR) << "Failed to initialize property area";
        exit(1);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
property_init函數調用了System_properties.cpp中的__system_property_area_init()函數。後者又先後調用了initialize_properties()和map_system_property_area函數。
initialize_properties()函數:
initialize_properties會嘗試加載”/property_contexts”,如果失敗會嘗試加載/system和/vendor下對應的文件,看來從Android O開始/property_contexts文件已經不再使用,取而代之的是/plat_property_contexts。“/property_contexts”等文件為property prefix分配了security context,用來控制set權限。
property_contexts內容如下:
...
sys.usb.config          u:object_r:system_radio_prop:s0
ril.                    u:object_r:radio_prop:s0
ro.ril.                 u:object_r:radio_prop:s0
gsm.                    u:object_r:radio_prop:s0
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
static bool initialize_properties() {
  // If we do find /property_contexts, then this is being
  // run as part of the OTA updater on older release that had
  // /property_contexts - b/34370523
  if (initialize_properties_from_file("/property_contexts")) {//加载"/property_contexts"
    return true;
  }

  // Use property_contexts from /system & /vendor, fall back to those from /
  if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
   //如果可以访问/system/etc/selinux/plat_property_contexts,就去加载解析文件。
    if (!initialize_properties_from_file("/system/etc/selinux/plat_property_contexts")) {
      return false;//这里如果解析失败就直接返回了,不再加载其他路径下的文件。
    }
    // Don't check for failure here, so we always have a sane list of properties.
    // E.g. In case of recovery, the vendor partition will not have mounted and we
    // still need the system / platform properties to function.
    //这里去加载/vendor/etc/selinux/nonplat_property_contexts, 但是不care结果。
    initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts");
  } else {
    //如果/system没有配置property_contexts文件,那么加载//plat_property_contexts
    if (!initialize_properties_from_file("/plat_property_contexts")) {
      return false;
    }
    initialize_properties_from_file("/nonplat_property_contexts");
  }

  return true;
}

/*initialize_properties_from_file负责解析property_contexts文件中的内容,
*将property前缀存在prefixes指向的链表中,将context存在contexts指向的链表中。
*/
static bool initialize_properties_from_file(const char* filename) {
  FILE* file = fopen(filename, "re");
  if (!file) {
    return false;
  }

  char* buffer = nullptr;
  size_t line_len;
  char* prop_prefix = nullptr;
  char* context = nullptr;

  while (getline(&buffer, &line_len, file) > 0) {
    int items = read_spec_entries(buffer, 2, &prop_prefix, &context);
    if (items <= 0) {
      continue;
    }
    if (items == 1) {
      free(prop_prefix);
      continue;
    }
    /*
     * init uses ctl.* properties as an IPC mechanism and does not write them
     * to a property file, therefore we do not need to create property files
     * to store them.
     */
    if (!strncmp(prop_prefix, "ctl.", 4)) {
      free(prop_prefix);
      free(context);
      continue;
    }

    auto old_context =
        list_find(contexts, [context](context_node* l) { return !strcmp(l->context(), context); });
    if (old_context) {
      list_add_after_len(&prefixes, prop_prefix, old_context);
    } else {
      list_add(&contexts, context, nullptr);
      list_add_after_len(&prefixes, prop_prefix, contexts);
    }
    free(prop_prefix);
    free(context);
  }

  free(buffer);
  fclose(file);

  return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
map_system_property_area函數:
在看map_system_property_area(…)函數之前,我們先看下__system_property_area_init()函數。
int __system_property_area_init() {
  free_and_unmap_contexts();
  //property_filename所指的路径为/dev/__properties__; #define PROP_FILENAME "/dev/__properties__"
  mkdir(property_filename, S_IRWXU | S_IXGRP | S_IXOTH);
  if (!initialize_properties()) {
    return -1;
  }
  bool open_failed = false;
  bool fsetxattr_failed = false;

  /*从property_contexts中解析的context全在contexts指向的链表中,下面这段代码就是遍历链表,
  *open函数将每个context_node节点对应的文件都打开,并映射到一块share memory中。
  *并在这块memory上构造一个prop_area指针存在context_node节点中。
  */
  list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {
    if (!l->open(true, &fsetxattr_failed)) {
      open_failed = true;
    }
  });
  if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
    free_and_unmap_contexts();
    return -1;
  }
  initialized = true;
  return fsetxattr_failed ? -2 : 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
下面看看map_system_property_area函數:
打開/dev/ properties /properties_serial文件,並映射到共享內存,將地址保存在system_property_area中。
static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) {
  char filename[PROP_FILENAME_MAX];
  //经过下面的格式化之后filename指向的文件为/dev/__properties__/properties_serial
  int len =
      __libc_format_buffer(filename, sizeof(filename), "%s/properties_serial", property_filename);
  if (len < 0 || len > PROP_FILENAME_MAX) {
    __system_property_area__ = nullptr;
    return false;
  }

  if (access_rw) {
   /*map_prop_area_rw函数会打开filename,映射一块共享内存,然后将地址返回。
   *地址将保存在 __system_property_area__中, 这个是一个全局变量,property
   *相关的操作还会用到这个变量。
   */
    __system_property_area__ =
        map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
  } else {
    __system_property_area__ = map_prop_area(filename);
  }
  return __system_property_area__;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
到這裡property area初始化就完成了。
在講property文件的加載之前,有必要先講下rc文件相關的知識點。rc文件被加載解析之後,所有的action會被放到ActionManager的actions_ (vector類型)容器裡;而action對應的commend對放到對應action的commands_ (vector類型)容器裡。ActionManager負責根據trigger來執行action。
command對像在被創建的時候,會根據關鍵字在KeywordMap類型的指針function_map_中查找對應的function; function_map_的賦值是在main函數中,使用的是BuiltinFunctionMap對象,BuiltinFunctionMap是繼承自KeywordMap。最終是在BuiltinFunctionMap內Map類型的變量builtin_functions中查找。
下面是buildin_functions中的部分內容:
        {"load_persist_props",      {0,     0,    do_load_persist_props}},
        {"load_system_props",       {0,     0,    do_load_system_props}},
        {"loglevel",                {1,     1,    do_loglevel}},
  • 1
  • 2
  • 3
下面是init.rc中的相關內容:
on post-fs
    # Load properties from
    #     /system/build.prop,
    #     /odm/build.prop,
    #     /vendor/build.prop and
    #     /factory/factory.prop
    load_system_props

    ...

on load_persist_props_action
    load_persist_props
    start logd
    start logd-reinit

    ...

    on property:vold.decrypt=trigger_load_persist_props
    load_persist_props
    start logd
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
當load_persist_props和load_system_props 命令執行的時候do_load_persist_props和do_load_system_props 函數會分別執行。
//下面是system property
static int do_load_system_props(const std::vector<std::string>& args) {
    load_system_props();
    return 0;
}

void load_system_props() {
    load_properties_from_file("/system/build.prop", NULL);
    load_properties_from_file("/odm/build.prop", NULL);
    load_properties_from_file("/vendor/build.prop", NULL);
    load_properties_from_file("/factory/factory.prop", "ro.*");
    load_recovery_id_prop();
}

//下面是persist properties
static int do_load_persist_props(const std::vector<std::string>& args) {
    load_persist_props();
    return 0;
}
void load_persist_props(void) {
    load_override_properties();
    /* Read persistent properties after all default values have been loaded. */
    load_persistent_properties();
    property_set("ro.persistent_properties.ready", "true");
}

static void load_override_properties() {
    if (ALLOW_LOCAL_PROP_OVERRIDE) {
        load_properties_from_file("/data/local.prop", NULL);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
在執行rc文件內的action之前,main函數會調用property_load_boot_defaults()函數加載默認property; 調用start_property_service()函數來創建了一個名字為property_service的Unix domain Socket(PROP_SERVICE_NAME:property_service)來處理set prop請求。
void property_load_boot_defaults() {
    if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
        // Try recovery path
        if (!load_properties_from_file("/prop.default", NULL)) {
            // Try legacy path
            load_properties_from_file("/default.prop", NULL);
        }
    }
    load_properties_from_file("/odm/default.prop", NULL);
    load_properties_from_file("/vendor/default.prop", NULL);

    update_sys_usb_config();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
下面總結了可能會被加載的property文件或property(按照code順序):
文件/屬性註釋
/system/etc/prop.default
/prop.default
/default.prop
/odm/default.prop
/vendor/default.prop
/system/build.prop
/odm/build.prop
/vendor/build.prop
/factory/factory.prop只加載ro.*
ro.recovery_id
/data/local.prop
/數據/屬性這個路徑下只加載persist開頭的property
/system/build.prop, /vendor/build.prop(PRODUCT_PROPERTY_OVERRIDES包含在內),/default.prop等生成規則都在代碼build/core/Makefile中有定義,當然如果不懂make 語法和函數,是不可能看的很明白。

3. Property的存儲

Property的存儲,工作中用不到,所以不想深究,根據System_properties.cpp中的註釋知道使用的是混合樹結構(hybrid trie),查找速度快,也省空間(分割了前綴,可以共用)。
Property是只能由init進程(單線程)更新,由property service完成。為了避免對線程讀的問題,在節點上使用了atomic_uint_least32_t。
/*
 * Properties are stored in a hybrid trie/binary tree structure.
 * Each property's name is delimited at '.' characters, and the tokens are put
 * into a trie structure.  Siblings at each level of the trie are stored in a
 * binary tree.  For instance, "ro.secure"="1" could be stored as follows:
 *
 * +-----+   children    +----+   children    +--------+
 * |     |-------------->| ro |-------------->| secure |
 * +-----+               +----+               +--------+
 *                       /    \                /   |
 *                 left /      \ right   left /    |  prop   +===========+
 *                     v        v            v     +-------->| ro.secure |
 *                  +-----+   +-----+     +-----+            +-----------+
 *                  | net |   | sys |     | com |            |     1     |
 *                  +-----+   +-----+     +-----+            +===========+
 */

沒有留言: