安卓系统监听system property值?

预备知识-什么是system property

system property是系统属性,以key-value格式保存。
可以通过以下方式读取和修改system property的值:
1.adb

adb shell getprop <key> 
adb shell setprop <key> <value>

2.C/C++

int property_get(const char *key, char *value, const char *default_value)
int property_set(const char *key, const char *value)

3.Java

SystemProperties.get(key)
SystemProperties.set(key, value);

前言

有一个朋友问我能否在App中监听system property值的变化,我想到rc文件中有大量类似下面的写法,通过监听system property的值启动服务。

on property:persist.debug.atrace.boottrace=1start boottrace

我猜肯定可以App中监听,果不其然在SystemProperties类中找到了addChangeCallback方法,看注释就就感觉自己找对了,但是这个类和方法都是@hide的,无法直接通过SDK调用addChangeCallback方法。这能难得到我嘛,我有三种方法调用hide的接口,我选用反射。

/frameworks/base/core/java/android/os/SystemProperties.java/*** Gives access to the system properties store.  The system properties* store contains a list of string key-value pairs.* {@hide}*/
@SystemApi
@TestApi
public class SystemProperties {@UnsupportedAppUsage@GuardedBy("sChangeCallbacks")private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();/*** Add a callback that will be run whenever any system property changes.* @param callback The {@link Runnable} that should be executed when a system property* changes.* @hide*/@UnsupportedAppUsagepublic static void addChangeCallback(@NonNull Runnable callback) {synchronized (sChangeCallbacks) {if (sChangeCallbacks.size() == 0) {native_add_change_callback();}sChangeCallbacks.add(callback);}}
}

很快我写完了demo,结果发现修改system property的值,根本不会触发注册的callback。
我检查了很多次我写的代码,都没发现问题,这是怎么回事呢?

这是一个典型的观察者模式,那我们跟踪一下代码,找一下触发callback的地方。

代码分析

开始跟踪代码,callback是被callChangeCallbacks回调的,从注释就可以看出callChangeCallbacks这个方法是从native层被回调。

@SuppressWarnings("unused")  // Called from native code.private static void callChangeCallbacks() {synchronized (sChangeCallbacks) {//Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");if (sChangeCallbacks.size() == 0) {return;}ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);final long token = Binder.clearCallingIdentity();try {for (int i = 0; i < callbacks.size(); i++) {try {callbacks.get(i).run();//循环调用callback} catch (Throwable t) {Log.wtf(TAG, "Exception in SystemProperties change callback", t);// Ignore and try to go on.}}} finally {Binder.restoreCallingIdentity(token);}}}

callChangeCallbacks是在Native层中被do_report_sysprop_change回调的。

frameworks/base/core/jni/android_os_SystemProperties.cppjmethodID sCallChangeCallbacks;void do_report_sysprop_change() {...env->CallStaticVoidMethod(sClazz, sCallChangeCallbacks);...
}void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz)
{...sCallChangeCallbacks = env->GetStaticMethodID(sClazz, "callChangeCallbacks", "()V");...
}

do_report_sysprop_change在report_sysprop_change被回调。

system/core/libutils/misc.cppvoid report_sysprop_change() {do_report_sysprop_change();
}
整个流程:report_sysprop_change ---> do_report_sysprop_change ---> callChangeCallbacks ---> callbacks.get(i).run();

report_sysprop_change的调用点

基本理清楚了整个流程,只要找到report_sysprop_change的地方就可以解开谜题了。但是我发现好多地方调用report_sysprop_change,一处处的分析。

第1处调用点

看到下面的代码我懵逼了,饶了一圈又回到SystemProperties.java的reportSyspropChanged这个方法,这时候我有种不祥的预感,难道这个report_sysprop_change需要我们在修改完system property之后主动调用SystemProperties.reportSyspropChanged()才能回调我们设置的callback嘛,而且就算需要我们主动调用reportSyspropChanged也无法跨进程通知其他应用的callback,那这个功能太鸡肋了。

frameworks/base/core/java/android/os/SystemProperties.java
/**
* Notifies listeners that a system property has changed
* @hide
*/
public static void reportSyspropChanged() {//调用native层的SystemProperties_report_sysprop_changenative_report_sysprop_change();
}frameworks/base/core/jni/android_os_SystemProperties.cppvoid SystemProperties_report_sysprop_change(JNIEnv /**env*/, jobject /*clazz*/)
{report_sysprop_change();
}int register_android_os_SystemProperties(JNIEnv *env)
{const JNINativeMethod method_table[] = {...{ "native_report_sysprop_change", "()V",(void*) SystemProperties_report_sysprop_change },};return RegisterMethodsOrDie(env, "android/os/SystemProperties",method_table, NELEM(method_table));
}

第2处调用点

看到下面这个代码,我还是抱着一点希望,说不准会有一次地方会发起SYSPROPS_TRANSACTION的Binder通信,来触发这个方法,但是一想也有点怪怪的,既然是Binder通信,有server端,就应该有client端。

status_t BBinder::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/)
{switch (code) {...case SYSPROPS_TRANSACTION: {report_sysprop_change();return NO_ERROR;}}
}
我们找到了三处client端:
client1:
ANRdaemon.cpp
/** Force the userland processes to refresh their property for logging.*/
static void dfs_poke_binder(void) {sp<IServiceManager> sm = defaultServiceManager();Vector<String16> services = sm->listServices();for (size_t i = 0; i < services.size(); i++) {sp<IBinder> obj = sm->checkService(services[i]);if (obj != NULL) {Parcel data;obj->transact(IBinder::SYSPROPS_TRANSACTION, data, NULL, 0);}}
}
client2:
atrace.cpp
// Poke all the binder-enabled processes in the system to get them to re-read
// their system properties.
static bool pokeBinderServices()
{sp<IServiceManager> sm = defaultServiceManager();Vector<String16> services = sm->listServices();for (size_t i = 0; i < services.size(); i++) {sp<IBinder> obj = sm->checkService(services[i]);if (obj != NULL) {Parcel data;if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data,NULL, 0) != OK) {if (false) {// XXX: For some reason this fails on tablets trying to// poke the "phone" service.  It's not clear whether some// are expected to fail.String8 svc(services[i]);fprintf(stderr, "error poking binder service %s\n",svc.string());return false;}}}}return true;
}

可以发现client1和client2的代码差不多:循环遍历注册在SM的实名Binder,然后发起SYSPROPS_TRANSACTION的Binder通信,这样子所有注册在SM中注册实名Binder的进程可以触发report_sysprop_change这个方法。看到这里我基本可以确定需要修改system property的地方主动执行client1和client2类似的代码,才能通知到其他进程system property发生了变化,然后触发callback。

但是还有一个疑问,我们自己写的App中并没有注册实名Binder到SM,那App如何接收system property的变化?

答案就在client3:通过实名Binder对象ActivityManagerService通知转发SYSPROPS_TRANSACTION到每个App中的匿名Binder对象ApplicationThread。

client3
ActivityManagerService.java
@Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws RemoteException {if (code == SYSPROPS_TRANSACTION) {// We need to tell all apps about the system property change.ArrayList<IBinder> procs = new ArrayList<IBinder>();synchronized(this) {final int NP = mProcessNames.getMap().size();for (int ip=0; ip<NP; ip++) {SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);final int NA = apps.size();for (int ia=0; ia<NA; ia++) {ProcessRecord app = apps.valueAt(ia);if (app.thread != null) {//遍历所有的ProcessRecord,获得每个ProcessRecord中保存thread,//thread是每个应用中ApplicationThread这个Binder对象的Client端。procs.add(app.thread.asBinder());}}}}int N = procs.size();for (int i=0; i<N; i++) {Parcel data2 = Parcel.obtain();try {//发送SYSPROPS_TRANSACTION到每个应用。procs.get(i).transact(IBinder.SYSPROPS_TRANSACTION, data2, null,Binder.FLAG_ONEWAY);} catch (RemoteException e) {}data2.recycle();}}}

总结

分析到这里我们基本可以回答标题的提问,我们无法通过addChangeCallback监听system property值的变化,除非在修改system property的地方,主动调用SystemProperties.reportSyspropChanged或者类似前面的client1和client2处的代码,前者只能通知当前进程,后者可以通知所有注册实名Binder到SM的进程和保存在AMS的ProcessRecord对应的App

尾巴

我还是想不明白rc文件中是如何监听system property,我继续找代码,直到我找到了下面的代码,原来是通过hook了PropertySet方法来监听system property值的变化。

static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {...省略大量代码property_changed(name, value);return PROP_SUCCESS;
}void property_changed(const std::string& name, const std::string& value) {...省略大量代码if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);...省略大量代码
}

扫码或长按关注

回复「 篮球的大肚子」进入技术群聊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467960.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为什么全天坐在电脑前会让你精疲力竭

Tips 原文作者&#xff1a;Katie Heaney 原文地址&#xff1a;Why Sitting at Your Computer All Day Can Wipe You Out 像大多数日子一样&#xff0c;我昨天大部分时间坐在我认为是公寓最美丽的一角&#xff0c;一直在笔记本电脑打字。 我提交了一个故事&#xff0c;转述了几个…

这届全明星,把NBA又燃回来了

第一个罚球&#xff0c;戴维斯出手后&#xff0c;听到哐当医生&#xff0c;皮球掉了出来。我又紧张了。微信群了很多人开始发消息&#xff0c;说詹姆斯队又要输了。回到比赛。戴维斯当时没有任何微笑&#xff0c;我估计他内心也是紧张的&#xff0c;他有点埋怨哈登&#xff0c;…

PYTHON__ ITERTOOLS模块

组成 总体&#xff0c;整体了解 无限迭代器 迭代器 参数 结果 例子 count() start, [step] start, startstep, start2*step, ... count(10) --> 10 11 12 13 14 ... cycle() p …

redis分布式锁java代码_基于redis实现分布式锁

“ 在上一篇文章中介绍了动态配置定时任务&#xff0c;其中的原理跟spring 定时任务注解Scheduled一样的&#xff0c;都是通过线程池和定义执行时间来控制。来思考一个问题&#xff0c;如果我们的定时任务在分布式微服务里面呢&#xff1f;在分布式微服务里面一个微服务肯定可以…

数值字符串

加粗样式 数值与字符串 受限于电脑内存 数字 int float 布尔 none 列表list:l[1,2,3] l[1]2 字典表dict:d{‘name’;‘tom’,‘age’:20} d.get(‘name’) d[‘name’] 元组t(1,2,3,4) 元组与列表区别&#xff1a;列表可以改变相应下标数据&#xff0c;元组不行。 数值&#x…

C语言写个贪吃蛇游戏

贪吃蛇是个非常经典的游戏&#xff0c;用C语言来实现也是一个好玩的事情。这个游戏我写完后放在知乎&#xff0c;竟然点赞的人数超级多。我觉得大家喜欢&#xff0c;一个方面是因为写得简单&#xff0c;大家都能看得懂&#xff0c;一个可扩展性还是非常强的。我试了说一下这个代…

seir模型matlab_疫情专题 | 传染病的经典数学模型

在此次新冠肺炎疫情防控过程中&#xff0c;对疫情发展趋势的科学预测显得尤为重要。而这背后&#xff0c;离不开对传染病传播规律的建模。今天&#xff0c;小编就带各位数学学子们来了解一下传染病的四大经典数学模型&#xff1a;SI/SIS/SIR/SEIR。其中用到了许多微分方程的知识…

vlh 标签详解

1.vlh:root root标签做为所有vlh标签的根标签. 1)value 在给定的范围内&#xff0c;包含在ValueList或list的变量名. List的实例自动被DefaultListBackedValueList包装在ValueList中 2)id 如果有多个表被包含在一个request中&#xff0c;ID属性能区分每个表。id被追加到所…

ubuntu电脑安装硬盘

最近在做安卓开发&#xff0c;一套RK3399的安卓代码&#xff0c;解压编译后占用170多G的硬盘。所以呢&#xff0c;原来1T大小的硬盘&#xff0c;很快就沾满了&#xff0c;然后我赶紧给北京总部申请买了一个新的硬盘。现在的台式电脑都是用的SATA硬盘接口&#xff0c;我赶紧就上…

python 制作抽奖箱_用Excel函数制作抽奖箱

话说各在公司每年的年会上&#xff0c;或者平时的一些分组活动上&#xff0c;又或者是某个内部组织的业务竞赛上……偶尔会遇到抽奖或抽签的环节&#xff0c;例如你的公司开展了一个实操的业务竞赛&#xff0c;一共80道题目&#xff0c;参赛人员要随机抽取题目然后做答。好的&a…

字典表

字典表 &#xff1a; dict 声明 键;值dict(键值) 操作 获取d.get(键‘默认值) 合并d.update(d2) 键值emp.items 菜单emp.keys 效果emp.values得到视图 遍历打印for x in emp.keys 支持嵌套 由于哈希算法导致顺序混乱可以将方法转换为列表&#xff0c;在排序 方法二 全局函…

Sql Server常用函数及技巧

使用Sql Server好长时间了&#xff0c;今天特别想总结一下&#xff0c;算是回顾吧&#xff01; 总结&#xff1a; 其实很多技巧&#xff0c;都是基于SQL Server自带的System Views&#xff0c;System Stored Procedures&#xff0c;System Functions (常用函数都在在里面)。 常…

十分钟让你明白AIDL

前言我在[003]AIDL是什么中介绍的AIDL&#xff0c;但是好像还有朋友不明白问我&#xff0c;那我就来写一个终极版的文章&#xff0c;让你十分钟彻底明白AIDL&#xff0c;以下代码全为手写。目标Server进程注册一个Binder服务到SM&#xff0c;该Binder服务提供两个接口&#xff…

表达式

表达式与分支 语句 分割文档main spilit 风格pep8 赋值 序列赋值 列表切割法 扩展序列解包赋值 *获取剩余 多目标赋值 数字256为界以内指向同对象 以外就不同 字符串3个 列表属于引用类型 不要共同引用两种方法 参数化赋值 列表也可以进行参数化赋值 表达式 函数 方法…

Linux下故障分析方法

1、背景有时候会遇到一些疑难杂症&#xff0c;并且监控插件并不能一眼立马发现问题的根源。这时候就需要登录服务器进一步深入分析问题的根源。那么分析问题需要有一定的技术经验积累&#xff0c;并且有些问题涉及到的领域非常广&#xff0c;才能定位到问题。所以&#xff0c;分…

TEXT宏

TEXT宏是windows程序设计中经常遇到的宏&#xff0c;定义在 <winnt.h>中 TCHAR *P TEXT("this is a const string"); 如果使用UNICODE字符集&#xff0c;则TEXT&#xff08;“....”&#xff09; &#xff0c;相当于 L"....." &#xff1b; 如果使…

作业题

import random fopen(‘data.txt’,‘w’) for i in range(10000): f.write(str(random.randint(1,100))) f.write(’\n’) f.seek(0) print(f.read()) f.close f.seek()函数 移动文件中n个操作 正为向结束方向 enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符…

即将放弃python的app_python放弃之 模块和包

importprint(frrom the my_module.py)money1000def rend1():print(my_my_module->reand1->money,money)def rend2():print(my_module->read2 calling read1)read1()def change():global moneymoney0模块可以包含可执行语句和函数的定义&#xff0c;这些语句的目的是初…

ubuntu 安装gitlab

gitlab 类似github&#xff0c;可以用来管理代码。当然除了他们两个还有很多代码管理的工具&#xff0c;国内的也有。我这篇文章就只讲gitlab的安装过程。但是gitlab并不是轻量级的东西&#xff0c;占用大概4~8G的内存&#xff0c;特别是merge的代码比较多的时候&#xff0c;占…

一些常用的linux命令(2)

参考&#xff1a;http://www.cnblogs.com/laov/p/3541414.html 系统管理命令 stat 显示指定文件的详细信息&#xff0c;比ls更详细 who 显示在线登陆用户 whoami 显示当前操作用户 hostname 显示主机名 uname 显示系统信…