Android系统设置属性详解
(一)Android上有三种主要途径来get/set属性。
(1)native code
当编写本地应用程序时,可以使用property_get和property_set 这两个API来读取/设置属性。要使用它们,我们需要include cutils/properties.h,并链接libcutils库。
//frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp#include status_t StartPropertySetThread::Start() {return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}bool StartPropertySetThread::threadLoop() {// Set property service.sf.present_timestamp, consumer need check its readinessproperty_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");// Clear BootAnimation exit flagproperty_set("service.bootanim.exit", "0");// Start BootAnimation if not startedproperty_set("ctl.start", "bootanim");// Exit immediatelyreturn false;
}//frameworks/native/services/surfaceflinger/Android.bp
shared_libs: ["libcutils",
]
(2)java code
在Java包(java.lang.System)中提供有System.getProperty和System.setProperty方法。但值得注意的是,尽管这两个API在语义上等同native函数,但其将数据存储于完全不同的位置。所以,用这两个API存储的属性是独立的,不能存取native属性,反之亦然。
(3)Android内部隐藏类:SystemProperties
Android有一个内部隐藏类(@hide,对SDK不可见)android.os.SystemProperties来操纵native属性。其通过jni来存取native属性库。
Ap如需使用,需将Ap的权限提升到system权限。
1.在AndroidManifest.xml中,在manifest加入android:sharedUserId="android.uid.system"2.在Android.mk中,將LOCAL_CERTIFICATE := XXX修改成LOCAL_CERTIFICATE :=platform
SystemProperties的使用很简单,在SystemProperties.java中所以方法都是static,直接通过SystemProperties.get(String key)或SystemProperties.set(String key, String val)就可以了,系统属性都是以键值对的形式存在即name和value。
//frameworks/base/core/java/android/os/SystemProperties.java/*** Android O removed the property name length limit, but com.amazon.kindle 7.8.1.5* uses reflection to read this whenever text is selected (http://b/36095274).* @hide*/public static final int PROP_NAME_MAX = Integer.MAX_VALUE;/** @hide */public static final int PROP_VALUE_MAX = 91;private static native String native_get(String key);private static native String native_get(String key, String def);private static native int native_get_int(String key, int def);private static native long native_get_long(String key, long def);private static native boolean native_get_boolean(String key, boolean def);private static native void native_set(String key, String def);@NonNull@SystemApipublic static String get(@NonNull String key) {if (TRACK_KEY_ACCESS) onKeyAccess(key);return native_get(key);}@NonNull@SystemApi@TestApipublic static String get(@NonNull String key, @Nullable String def) {if (TRACK_KEY_ACCESS) onKeyAccess(key);return native_get(key, def);}public static void set(@NonNull String key, @Nullable String val) {if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {throw new IllegalArgumentException("value of system property '" + key+ "' is longer than " + PROP_VALUE_MAX + " characters: " + val);}if (TRACK_KEY_ACCESS) onKeyAccess(key);native_set(key, val);}
SystemProperties.java中所有native方法都是在android_os_SystemProperties.cpp里实现,先来看看java中个方法是怎么和cpp中方法对应起来的。
//frameworks/base/core/jni/android_os_SystemProperties.cppstatic const JNINativeMethod method_table[] = {{ "native_get", "(Ljava/lang/String;)Ljava/lang/String;",(void*) SystemProperties_getS },{ "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",(void*) SystemProperties_getSS },{ "native_get_int", "(Ljava/lang/String;I)I",(void*) SystemProperties_get_int },{ "native_get_long", "(Ljava/lang/String;J)J",(void*) SystemProperties_get_long },{ "native_get_boolean", "(Ljava/lang/String;Z)Z",(void*) SystemProperties_get_boolean },{ "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",(void*) SystemProperties_set },{ "native_add_change_callback", "()V",(void*) SystemProperties_add_change_callback },{ "native_report_sysprop_change", "()V",(void*) SystemProperties_report_sysprop_change },
};//在AndroidRuntime.cpp进行注册
int register_android_os_SystemProperties(JNIEnv *env)
{return RegisterMethodsOrDie(env, "android/os/SystemProperties", method_table,NELEM(method_table));
}static jxxx SystemProperties_get_xxx(JNIEnv *env, jobject clazz,jstring keyJ, jint defJ)
{const char* key;char buf[PROPERTY_VALUE_MAX];len = property_get(key, buf, "");return result;
}static void SystemProperties_set(JNIEnv *env, jobject clazz,jstring keyJ, jstring valJ)
{const char* key;const char* val;err = property_set(key, val);
}
register_android_os_SystemProperties方法是在AndroidRuntime.cpp中调用的(Android系统开机时会注册相关JNI方法)。
在android_os_SystemProperties.cpp中可以发现最终实现是不区分SystemProperties_get_int或SystemProperties_getS,基本所有的方法都是调用property_get和property_set方法来实现的。
property_get和property_set是在properties.cpp里实现的,如下
//system/core/libcutils/properties.cpp#include int property_set(const char *key, const char *value) {return __system_property_set(key, value);
}int property_get(const char *key, char *value, const char *default_value) {int len = __system_property_get(key, value);if (len > 0) {return len;}if (default_value) {len = strnlen(default_value, PROPERTY_VALUE_MAX - 1);memcpy(value, default_value, len);value[len] = '\0';}return len;
}
__system_property_set和__system_property_get调用到如下位置。
//bionic/libc/bionic/system_property_api.cppint __system_properties_init() {return system_properties.Init(PROP_FILENAME) ? 0 : -1;
}const prop_info* __system_property_find(const char* name) {return system_properties.Find(name);
}int __system_property_read(const prop_info* pi, char* name, char* value) {return system_properties.Read(pi, name, value);
}int __system_property_get(const char* name, char* value) {return system_properties.Get(name, value);
}int __system_property_update(prop_info* pi, const char* value, unsigned int len) {return system_properties.Update(pi, value, len);
}int __system_property_add(const char* name, unsigned int namelen, const char* value,unsigned int valuelen) {return system_properties.Add(name, namelen, value, valuelen);
}
设置属性通过异步socket通信,向property_service服务发送消息
//bionic/libc/bionic/system_property_set.cppint __system_property_set(const char* key, const char* value) {if (key == nullptr) return -1;if (value == nullptr) value = "";if (strlen(key) >= PROP_NAME_MAX) return -1;if (strlen(value) >= PROP_VALUE_MAX) return -1;prop_msg msg;memset(&msg, 0, sizeof msg);msg.cmd = PROP_MSG_SETPROP;strlcpy(msg.name, key, sizeof msg.name);strlcpy(msg.value, value, sizeof msg.value);return send_prop_msg(&msg);
}#define PROP_SERVICE_NAME "property_service"
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;static int send_prop_msg(const prop_msg *msg)
{ //sokcet 通信 /dev/socket/property_service socket_ = ::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);if (socket_ == -1) {last_error_ = errno;return;}//property_service_socketconst size_t namelen = strlen(property_service_socket);sockaddr_un addr;memset(&addr, 0, sizeof(addr));strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));addr.sun_family = AF_LOCAL;socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;if (TEMP_FAILURE_RETRY(connect(socket_, reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {last_error_ = errno;close(socket_);socket_ = -1;}
}
property_service服务是在init进程调用start_property_service启动的,在property_service.cpp中
//system/core/init/init.cppint main(int argc, char** argv) {start_property_service();
}//system/core/init/property_service.cpp
void start_property_service() {selinux_callback cb;cb.func_audit = SelinuxAuditCallback;selinux_set_callback(SELINUX_CB_AUDIT, cb);property_set("ro.property_service.version", "2");//创建socketproperty_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,false, 0666, 0, 0, nullptr);if (property_set_fd == -1) {PLOG(FATAL) << "start_property_service socket creation failed";}//监听socketlisten(property_set_fd, 8);register_epoll_handler(property_set_fd, handle_property_set_fd);
}
在handle_property_set_fd()处理对应属性值
//system/core/init/property_service.cpp
static void handle_property_set_fd() {static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms *///等待建立通信int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);if (s == -1) {return;}ucred cr;socklen_t cr_size = sizeof(cr);//获取套接字相关信息if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {close(s);PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";return;}SocketConnection socket(s, cr);uint32_t timeout_ms = kDefaultSocketTimeout;uint32_t cmd = 0;if (!socket.RecvUint32(&cmd, &timeout_ms)) {PLOG(ERROR) << "sys_prop: error while reading command from the socket";socket.SendUint32(PROP_ERROR_READ_CMD);return;}//处理消息switch (cmd) {case PROP_MSG_SETPROP: {char prop_name[PROP_NAME_MAX];char prop_value[PROP_VALUE_MAX];//接收属性设置请求消息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;const auto& cr = socket.cred();std::string error;//处理属性请求uint32_t result =HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);if (result != PROP_SUCCESS) {LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value<< "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "<< error;}break;}
进入HandlePropertySet处理请求
//system/core/init/property_service.cpp
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,const std::string& source_context, const ucred& cr, std::string* error) {//检查属性名是否合法if (!IsLegalPropertyName(name)) {*error = "Illegal property name";return PROP_ERROR_INVALID_NAME;}//处理ctl.开头消息if (StartsWith(name, "ctl.")) {//检查权限,处理以ctl开头的属性if (!CheckControlPropertyPerms(name, value, source_context, cr)) {*error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,value.c_str());return PROP_ERROR_HANDLE_CONTROL_MESSAGE;}//(1)相当于handle_control_message()函数HandleControlMessage(name.c_str() + 4, value, cr.pid);return PROP_SUCCESS;}const char* target_context = nullptr;const char* type = nullptr;property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);//检查权限if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {*error = "SELinux permission check failed";return PROP_ERROR_PERMISSION_DENIED;}if (type == nullptr || !CheckType(type, value)) {*error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",(type ?: "(null)"));return PROP_ERROR_INVALID_VALUE;}// sys.powerctl is a special property that is used to make the device reboot. We want to log// any process that sets this property to be able to accurately blame the cause of a shutdown.if (name == "sys.powerctl") {std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);std::string process_cmdline;std::string process_log_string;if (ReadFileToString(cmdline_path, &process_cmdline)) {// Since cmdline is null deliminated, .c_str() conveniently gives us just the process// path.process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());}LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid<< process_log_string;}if (name == "selinux.restorecon_recursive") {return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);}//(2)进入PropertySet()函数设置属性return PropertySet(name, value, error);
}
(1)处理ctl.开头消息
//system/core/init/init.cpp
void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid){
180 Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
181 if (svc == nullptr) {
182 LOG(ERROR) << "no such service '" << name << "'";
183 return;
184 }
185
186 if (msg == "start") {
187 svc->Start();
188 } else if (msg == "stop") {
189 svc->Stop();
190 } else if (msg == "restart") {
191 svc->Restart();
192 } else {
193 LOG(ERROR) << "unknown control msg '" << msg << "'";
194 }
195}
(2)进入PropertySet()函数设置属性
//system/core/init/property_service.cpp
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {size_t valuelen = value.size();//检查属性名是否合法if (!IsLegalPropertyName(name)) {*error = "Illegal property name";return PROP_ERROR_INVALID_NAME;}if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {*error = "Property value too long";return PROP_ERROR_INVALID_VALUE;}if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {*error = "Value is not a UTF8 encoded string";return PROP_ERROR_INVALID_VALUE;}prop_info* pi = (prop_info*) __system_property_find(name.c_str());if (pi != nullptr) {// ro.* properties are actually "write-once".//处理ro开头属性if (StartsWith(name, "ro.")) {*error = "Read-only property was 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) {*error = "__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 && StartsWith(name, "persist.")) {WritePersistentProperty(name, value);}property_changed(name, value);return PROP_SUCCESS;
}//system/core/init/init.cpp
void property_changed(const std::string& name, const std::string& value) {// If the property is sys.powerctl, we bypass the event queue and immediately handle it.// This is to ensure that init will always and immediately shutdown/reboot, regardless of// if there are other pending events to process or if init is waiting on an exec service or// waiting on a property.// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific// commands to be executed.if (name == "sys.powerctl") {shutdown_command = value;do_shutdown = true;}
(二)系统属性类别和加载优先级
- 属性名称以“ro.”开头,被视为只读属性。一旦设置,属性值不能改变;
- 属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property;
- 属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名;
- 属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在init.rc中定义。系统启动时,与init守护进程将解析init.rc和启动属性服务;
系统属性是在init.rc中加载的,具体启动方式如下:
on property:sys.boot_from_charger_mode=1trigger late-initon late-inittrigger early-fstrigger fstrigger post-fstrigger late-fstrigger post-fs-datatrigger zygote-starttrigger load_persist_props_actiontrigger firmware_mounts_completetrigger early-boottrigger booton post-fs# Load properties from# /system/build.prop,# /odm/build.prop,# /vendor/build.prop# /factory/factory.propload_system_props# start essential servicesstart logdstart servicemanagerstart hwservicemanagerstart vndservicemanageron load_persist_props_actionload_persist_propsstart logdstart logd-reinit
当属性值sys.boot_from_charger_mode为1时,会触发late-init,在late-init又会触发load_system_props和load_persist_props_action(load_persist_props)
void load_system_props() {load_properties_from_file("/system/build.prop", NULL);//加载system/build.propload_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();//加载recovery相关prop
}void load_persist_props(void) {load_override_properties();//如果"ro.debuggable"为1加载data/local.prop里的属性load_persistent_properties();//加载/data/property里的persistent properties
}static void load_override_properties() {if (ALLOW_LOCAL_PROP_OVERRIDE) {load_properties_from_file("/data/local.prop", NULL);}
}static void load_persistent_properties() {auto persistent_properties = LoadPersistentProperties();for (const auto& persistent_property_record : persistent_properties.properties()) {property_set(persistent_property_record.name(), persistent_property_record.value());}persistent_properties_loaded = true;property_set("ro.persistent_properties.ready", "true");
}
(三)利用系统属性动态设置程序中Log的开关
Android 动态控制logcat日志开关,通过Log.isLoggable(TAG,level)方法动态控制。
以FlashlightController类为例:
private static final String TAG = “FlashlightController”;
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
利用adb命令设置属性值来控制该日志开关:
adb shell setprop log.tag.FlashlightController DEBUG
设置该TAG的输出级别为DEBUG,则 level为DEBUG以上的都返回true
需要注意的是通过adb shell setprop设置的属性值每次重启后都会恢复之前的值。
可以将该属性添加在data/local.prop属性文件中,不同的是只要存在local.prop,该手机重启与否属性值都存在,另外需要注意的是user版ro.debuggable=0,系统启动时不会读取/data/local.prop文件。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
