預備知識-什麼是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=1
start 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
*/
@UnsupportedAppUsage
public 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.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");
...
}
do_report_sysprop_change在report_sysprop_change被回調。
system/core/libutils/misc.cpp
void 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_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));
}
第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
@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 = 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();
}
}
}
總結
尾巴
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);
...省略大量代碼
}