Android 属性服务研究

提问

1.framework中,很多时候都会用到属性值,是不是属性服务起来的比zygote还早?

2.属性服务是怎么跟system_server进行跨进程通信的?

3.加载system/build.prop和vendor/build.prop的先后顺序是什么?如果system/build.prop

中有一个ro属性,同样在vendor/build.prop也有一个,最后会以谁的值为准?

分析

注:参考Android 8.1系统

代码路径

system/core/init/ 
init.cpp 
property_service.cpp 

system/core/libcutils 
properties.cpp 
bionic/libc/bionic/ 
system_properties.cpp 

bionic/libc/include/sys/ 
_system_properties.h 

frameworks/base/core/java/android/os/ 
SystemProperties.java 

frameworks/base/core/jni/ 
android_os_SystemProperties.cpp

过程

system/core/init/init.cpp
int main(int argc, char** argv) {
    ······
    property_init();
    ······
    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    export_kernel_boot_props();

    // Make the time that init started available for bootstat to log.
    property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
    ······
    property_load_boot_defaults();
    export_oem_lock_status();
    start_property_service();
    ······
    ActionManager& am = ActionManager::GetInstance();
    ServiceManager& sm = ServiceManager::GetInstance();
    Parser& parser = Parser::GetInstance();

    parser.AddSectionParser("service", std::make_unique(&sm));
    parser.AddSectionParser("on", std::make_unique(&am));
    parser.AddSectionParser("import", std::make_unique(&parser));
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/init.rc");
        parser.set_is_system_etc_init_loaded(
                parser.ParseConfig("/system/etc/init"));
        parser.set_is_vendor_etc_init_loaded(
                parser.ParseConfig("/vendor/etc/init"));
        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
    } else {
        parser.ParseConfig(bootscript);
        parser.set_is_system_etc_init_loaded(true);
        parser.set_is_vendor_etc_init_loaded(true);
        parser.set_is_odm_etc_init_loaded(true);
    }
    ······
    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");//这里是启动zygote进程的指令
    }
}
在init的main函数中,初始化property_init的地方早于加载init.rc的地方,同时,
start_property_service()的地方也比加载init.rc的地方早很多。从这里,可以看出,
属性服务比zygote进程起来的早,所以,可以被system_server进程随时调用。
其实,换一个思路看,单纯看init.rc,也是可以知道的,如下:
system/core/rootdir/init.rc
on zygote-start && property:ro.crypto.state=unencrypted
这里就判断了属性,这就代表属性服务早于zygote的启动。
这里重点一下start_property_service():
void start_property_service() {
    property_set("ro.property_service.version", "2");

    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, nullptr, sehandle);//1
    if (property_set_fd == -1) {
        PLOG(ERROR) << "start_property_service socket creation failed";
        exit(1);
    }

    listen(property_set_fd, 8);//2

    register_epoll_handler(property_set_fd, handle_property_set_fd);//3
}
注释1:创建非阻塞(SOCK_NONBLOCK)的socket
注释2:a.调用listen函数对property_set_fd进行监听,这样创建的socket就成为了server,也就是属性服务;
       b.listen函数的第二个参数设置8,意味着属性服务最多可以同时为8个试图设置属性的用户提供服务。
注释3:将property_set_fd放入了epoll句柄中,用epoll来监听property_set_fd:当property_set_fd中有数据到来时,init进程将用handle_property_set_fd函数进行处理。
注:在linux新的内核中,epoll用来替换select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为内核中的select实现是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。作者:刘望舒
static void handle_property_set_fd() {
    ······
    handle_property_set(socket, name, value, false); 
    ······  
}

static void handle_property_set(SocketConnection& socket,
                                const std::string& name,
                                const std::string& value,
                                bool legacy_protocol) {
  ······
    if (check_mac_perms(name, source_ctx, &cr)) {//1
      uint32_t result = property_set(name, value);//2
      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);
      }
    }
   ······
}
注释1:检测客户端权限
注释2:对属性进行修改

property_set-->PropertySetImpl
static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
    ······
    prop_info* pi = (prop_info*) __system_property_find(name.c_str());//1
    if (pi != nullptr) {//2
        // ro.* properties are actually "write-once".
        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 {//3
        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.
    if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
        write_persistent_property(name.c_str(), value.c_str());//4
    }
    property_changed(name, value);

    return PROP_SUCCESS;
}
注释1:从属性池中找是否有对应对应的属性
如果有,注释2:进行更新操作,如果是ro属性,就不能进行更新了
如果没有,注释3:添加进属性池
注释4:如果是persist.属性,就会写进/data/property
拓展:
a.从这里可以看出,对于已经定义的ro属性,你想更改是不可能的,但是对于没有定义的,就可以进行设置值了。
b.persist.的属性类别,可以通过adb在/data/property查看
c.这里也可以明白,如果build.prop定义了一个值,另外一个build.prop也定义同样一个值的话,优先认第一个加载的。

我们看一下,客户端是怎么处理写操作的?
SystemProperties.java
public static void set(String key, String val) {
        if (val != null && val.length() > PROP_VALUE_MAX) {
            throw newValueTooLargeException(key, val);
        }
        if (TRACK_KEY_ACCESS) onKeyAccess(key);
        native_set(key, val);
}

android_os_SystemProperties.cpp
static void SystemProperties_set(JNIEnv *env, jobject clazz,
                                      jstring keyJ, jstring valJ)
{
    ······
    err = property_set(key, val);
    ······
}

system/core/libcutils/properties.cpp
int property_set(const char *key, const char *value) {
    return __system_property_set(key, value);
}

bionic/libc/bionic/system_properties.cpp
int __system_property_set(const char* key, const char* value) {
  ······
  if (g_propservice_protocol_version == kProtocolVersion1) {
    // Old protocol does not support long names
    ······
  } else {
    // Use proper protocol
    PropertyServiceConnection connection;//1
    ······

    SocketWriter writer(&connection);//2
    if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {//3
      errno = connection.GetLastError();
      async_safe_format_log(ANDROID_LOG_WARN,
                            "libc",
                            "Unable to set property \"%s\" to \"%s\": write failed; errno=%d (%s)",
                            key,
                            value,
                            errno,
                            strerror(errno));
      return -1;
    }
    ······
  }
}
注释1:定义了一个PropertyServiceConnection,用来包装Socket
注释2:定义了一个SocketWriter ,用来Socket写数据

总结,属性服务通过socket来进行跨进程通信。注,这是仅仅是针对写操作


对此,我们看一下客户端是怎么进行读呢?
SystemProperties.java ----> android_os_SystemProperties.cpp ----> properties.cpp ----> system_properties.cpp
native_get --> SystemProperties_getSS --> property_get --> __system_property_get --> __system_property_find

const prop_info* __system_property_find(const char* name) {
  if (!__system_property_area__) {//1
    return nullptr;
  }

  prop_area* pa = get_prop_area_for_name(name);
  if (!pa) {
    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name);
    return nullptr;
  }

  return pa->find(name);
}
注释1:__system_property_area__是从这里找对应的值

我们回到属性服务初始化的地方
init.cpp --> property_service.cpp --> system_properties.cpp
main --> property_init --> __system_property_area_init

int __system_properties_init() {
  // This is called from __libc_init_common, and should leave errno at 0 (http://b/37248982).
  ErrnoRestorer errno_restorer;

  if (initialized) {
    list_foreach(contexts, [](context_node* l) { l->reset_access(); });
    return 0;
  }
  if (is_dir(property_filename)) {//1
    if (!initialize_properties()) {
      return -1;
    }
    if (!map_system_property_area(false, nullptr)) {//2
      free_and_unmap_contexts();
      return -1;
    }
  } else {
    __system_property_area__ = map_prop_area(property_filename);//3
    if (!__system_property_area__) {
      return -1;
    }
    list_add(&contexts, "legacy_system_prop_area", __system_property_area__);
    list_add_after_len(&prefixes, "*", contexts);
  }
  initialized = true;
  return 0;
}
注释1:property_filename 就是 PROP_FILENAME "/dev/__properties__"
注释2、注释3:都是用来初始化__system_property_area__,它是一个prop_area

static prop_area* map_prop_area(const char* filename) {
  int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
  if (fd == -1) return nullptr;

  prop_area* map_result = map_fd_ro(fd);
  close(fd);

  return map_result;
}

static prop_area* map_fd_ro(const int fd) {
  struct stat fd_stat;
  if (fstat(fd, &fd_stat) < 0) {
    return nullptr;
  }

  if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
      ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
      (fd_stat.st_size < static_cast(sizeof(prop_area)))) {
    return nullptr;
  }

  pa_size = fd_stat.st_size;
  pa_data_size = pa_size - sizeof(prop_area);

  void* const map_result = mmap(nullptr, pa_size, PROT_READ, MAP_SHARED, fd, 0);//1
  if (map_result == MAP_FAILED) {
    return nullptr;
  }

  prop_area* pa = reinterpret_cast(map_result);//2
  if ((pa->magic() != PROP_AREA_MAGIC) || (pa->version() != PROP_AREA_VERSION)) {
    munmap(pa, pa_size);
    return nullptr;
  }

  return pa;
}
注释1:map_result 搞了一个内存映射
注释2:pa为含有内存共享的链表

总结,读属性的操作,用的是内存共享的方式进行的跨进程操作

总结

属性服务(写属性)通过socket来进行跨进程通信。

读属性的操作,用的是内存共享的方式进行的跨进程通信

补充

1.adb 查询:

setprop、getprop、watchprops

2.查看缓存地址

persist.

/data/property

属性地址//这里涉及selinux权限设置

/dev/properties

属性服务通信

/dev/socket/property_service

3.系统涉及到的properties值

system/etc/selinux/plat_property_contexts

vendor/etc/selinux/nonplat_property_contexts

参考学习

https://blog.csdn.net/qq_19923217/article/details/82014989

https://blog.csdn.net/hp910315/article/details/79540898

https://blog.csdn.net/woai110120130/article/details/87891566

https://www.jianshu.com/p/1d3e722871e8

https://www.baidu.com/link?url=V077ThE47EBpxoyo9nJGfyV3bXlfzFlFMVeGOBdmNRr7wV7qTem87iY3WzDhb5CtSkPTUVp1nNydjSoPOafl9u1tkCiS9MdBWzeKKJ6YKPm&wd=&eqid=e88ad0860000815e000000065d4978d7

https://www.baidu.com/link?url=EzVP75jjUV9MF_2jV2xvVji3Up6RQrvhYfNj91BiBRnnv3IV1Y09O912Ce-3jD4nq6PaZ7KLMlQWMIuoBzmNDbLTd6OLxgoMbJVkD7zokP_&wd=&eqid=d0836e8c00036552000000065d49795d

你可能感兴趣的:(Android 属性服务研究)