Settings.System数据监听/prop&SystemProperties数据监听
Settings.System数据监听
利用ContentObserver来实现
//Android 11
import android.database.ContentObserver;
private ContentObserver mScreenshotShowObserver;
private ContentResolver mContentResolver;
mContentResolver = getContext().getContentResolver();
//数据变化之后做啥
mScreenshotShowObserver = new ContentObserver( getContext().getMainThreadHandler()) {
@Override
public void onChange(boolean selfChange) {
boolean isShow = Settings.System.getInt(getContext().getContentResolver(), Settings.System.SCREENSHOT_BUTTON_SHOW, 1) == 1;
ButtonDispatcher screenshotButton = mNavigationBarView.getScreenshotButton();
screenshotButton.setVisibility(isShow ? View.VISIBLE : View.GONE);
}
};
//注册监听器
mContentResolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREENSHOT_BUTTON_SHOW), true,
mScreenshotShowObserver, UserHandle.USER_ALL);
prop&SystemProperties数据监听
prop属性设置和获取
- adb
adb shell getprop <key>
adb shell setprop <key> <value>
- C++
/system/core/libcutils/properties.cpp
int property_get(const char* key, char* value, const char* default_value)
int property_set(const char* key, const char* value)
- java
/frameworks/base/core/java/android/os/SystemProperties.java
public static String get(@NonNull String key)
public static void set(@NonNull String key, @Nullable String val)
SystemPropPoker 监听
frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/SystemPropPoker.java
private static final SystemPropPoker sInstance = new SystemPropPoker();
@NonNull
public static SystemPropPoker getInstance() {
return sInstance;
}
public void poke() {
if (!mBlockPokes) {
createPokerTask().execute();
}
}
@VisibleForTesting
PokerTask createPokerTask() {
return new PokerTask();
}
public static class PokerTask extends AsyncTask<Void, Void, Void> {
@VisibleForTesting
String[] listServices() {
return ServiceManager.listServices();
}
@VisibleForTesting
IBinder checkService(String service) {
return ServiceManager.checkService(service);
}
@Override
protected Void doInBackground(Void... params) {
String[] services = listServices();
if (services == null) {
Log.e(TAG, "There are no services, how odd");
return null;
}
for (String service : services) {
IBinder obj = checkService(service);
if (obj != null) {
Parcel data = Parcel.obtain();
try {
obj.transact(IBinder.SYSPROPS_TRANSACTION, data, null, 0);
} catch (RemoteException e) {
// Ignore
} catch (Exception e) {
Log.i(TAG, "Someone wrote a bad service '" + service
+ "' that doesn't like to be poked", e);
}
data.recycle();
}
}
return null;
}
}
监听SystemProperties的变化,查看SystemPropPoker类可知,它会监听所有SystemProperties属性值的变化,
而且会通过obj.transact(IBinder.SYSPROPS_TRANSACTION, data, null, 0);
,
通知所有的服务.
所以使用SystemPropPoker监听,需要在代码下做些判断.
demo:
它并不能具体指定监听某一系统属性,只要有系统属性发生变化并poke,注册的回调函数就会被调用.
在调用SystemProperties.set时poke,通知监听方:
import com.android.settingslib.development.SystemPropPoker;
SystemProperties.set("persist.xxx.is_drop_down","false");
SystemPropPoker.getInstance().poke();
监听回调函数:
//SystemProperties.addChangeCallback只添加一次,不要多次重复添加,不然回调多次
static boolean register = false;
if (!register) {
register = true;
SystemProperties.addChangeCallback(new Runnable() {
@Override
public void run() {
Log.d(TAG,"SystemProperties prop changed");
boolean newState = SystemProperties.getBoolean("persist.xxx.is_drop_down",true);
if(newState != oldState){
//to do
}
}
});
}
ystemProperties属性变化监听
安卓prop/SystemProperties如何监听值变化
PS:上面的demo我在源码中测试过,只能在同进程(同一个app)中使用,不能跨进程通信.
我感觉很鸡肋的函数,然后我搜索源码,发现SystemPropertiesTest源码内有类似的用法:
./frameworks/base/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@SmallTest
public void testCallbacks() {
...
Runnable r1 = new Runnable() {
boolean done = false;
@Override
public void run() {
if (done) {
return;
}
done = true;
wait1.countDown();
throw new RuntimeException("test");
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
wait2.countDown();
}
};
SystemProperties.addChangeCallback(r1);
SystemProperties.addChangeCallback(r2);
SystemProperties.reportSyspropChanged();
...
}
@SmallTest
public void testProperties() throws Exception {
String value;
SystemProperties.set(KEY, "");
value = SystemProperties.get(KEY, "default");
assertEquals("default", value);
...
}
为什么不能跨进程通信?
那只能跟踪源码分析了.原因在下面链接:
分析能否监听system property值
/frameworks/base/core/java/android/os/SystemProperties.java
@SuppressWarnings("unused") // Called from native code.//从native层被回调
private static void callChangeCallbacks() {
ArrayList<Runnable> callbacks = null;
synchronized (sChangeCallbacks) {
//Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
if (sChangeCallbacks.size() == 0) {
return;
}
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) {
// Ignore and try to go on. Don't use wtf here: that
// will cause the process to exit on some builds and break tests.
Log.e(TAG, "Exception in SystemProperties change callback", t);
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
跟踪callChangeCallbacks()
,它是被native层被回调.
frameworks/base/core/jni/android_os_SystemProperties.cpp
jmethodID 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");
...
}
callChangeCallbacks
是在Native层中被do_report_sysprop_change
回调的
system/core/libutils/misc.cpp
void report_sysprop_change() {
do_report_sysprop_change();
}
do_report_sysprop_change
在report_sysprop_change
被回调
整个流程:
report_sysprop_change ---> do_report_sysprop_change --->
callChangeCallbacks ---> callbacks.get(i).run();
遍历源码,看哪里调用了report_sysprop_change
,然后一个个分析.我直接用了王小二的技术栈的分析
第1处调用点
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_change
native_report_sysprop_change();
}
frameworks/base/core/jni/android_os_SystemProperties.cpp
void 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));
}
流程:可能修改完system property之后主动调用SystemProperties.reportSyspropChanged()才能回调我们设置的callback.
第2处调用点
./frameworks/native/libs/binder/Binder.cpp
// NOLINTNEXTLINE(google-default-arguments)
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;
}
...
}
发现一个关于SYSPROPS_TRANSACTION的Binder通信,来触发report_sysprop_change().
既然是Binder通信,有server端,就应该有client端.
client——1
//ANR 守护进程
./system/extras/ANRdaemon/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);
}
}
}
client——2
./frameworks/native/cmds/atrace/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;
}
上述代码解析:
循环遍历注册在SM(ServiceManager)的实名Binder,
然后发起SYSPROPS_TRANSACTION的Binder通信,这样子所有注册在SM中注册实名Binder的进程可以触发report_sysprop_change.
可以看出需要监听修改system property的地方主动执行client-1和client-2类似的代码,
才能通知到其他进程system property发生了变化然后触发callback.
如果自己写的App中并没有注册实名Binder到SM,那App如何接收system property的变化?
client——3
./frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public 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 = mProcessList.mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
SparseArray<ProcessRecord> apps =
mProcessList.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();
}
}
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
// The activity manager only throws certain exceptions intentionally, so let's
// log all others.
if (!(e instanceof SecurityException
|| e instanceof IllegalArgumentException
|| e instanceof IllegalStateException)) {
Slog.wtf(TAG, "Activity Manager Crash."
+ " UID:" + Binder.getCallingUid()
+ " PID:" + Binder.getCallingPid()
+ " TRANS:" + code, e);
}
throw e;
}
}
总结
如果system property监听在同一个进程内,可以用SystemProperties.addChangeCallback()监听,
在Settings源码内添加过(Android 11),演示监听成功.
还有另一种说法,
在修改system property的地方,主动调用SystemProperties.reportSyspropChanged或者类似前面的client1和client2处的代码.
前者只能通知当前进程,后者可以通知所有注册实名Binder到SM的进程和保存在AMS的ProcessRecord对应的App.
在Android 11 内没有搜索到这个函数,不知道是那个版本有. 它模仿类似与client1/client2的通信java版.
/**
* Notifies {@link #getRunningAppProcesses app processes} that the system properties
* have changed.
*
* @see SystemProperties#addChangeCallback
*
* @hide
*/
@TestApi
public void notifySystemPropertiesChanged() {
// Note: this cannot use {@link ServiceManager#listServices()} to notify all the services,
// as that is not available from tests.
final var binder = ActivityManager.getService().asBinder();
if (binder != null) {
var data = Parcel.obtain();
try {
binder.transact(IBinder.SYSPROPS_TRANSACTION, data, null /* reply */,
0 /* flags */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
data.recycle();
}
}
如何使用:
//App1进程
+ SystemProperties.set("xy.test.callback","true");
+ ActivityManager am = (ActivityManager) getSystemService(
+ Context.ACTIVITY_SERVICE);
+ am.notifySystemPropertiesChanged();
//App2进程
static boolean register = false;
+ if (!register) {
+ register = true;
+ SystemProperties.addChangeCallback(new Runnable() {
+ @Override
+ public void run() {
+ android.util.Log.i("text","SystemProperties ChangeCallback testxxxxxxxx);
+ }
+ });
+ }
App1进程setprop,然后通知 ---> system_server利用ams通知进程system property改变 ---> App2进程收到通知,addChangeCall执行
.rc 监听system property值
利用on property 触发器,启动某个服务
./frameworks/native/cmds/atrace/atrace.rc
on property:persist.debug.atrace.boottrace=1
start boottrace
rc文件中是如何监听system property?
原来是通过hook了PropertySet方法来监听system property值的变化
./system/core/init/property_service.cpp
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);
...
}
./system/core/init/property_service.cpp
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
2022-02-17 关于笔记本电脑--软件