什么是
UEventObserver 是 Android 框架中一个隐藏的抽象类(@hide),位于 frameworks/base/core/java/android/os/UEventObserver.java,主要用于监听内核(Kernel)发送的 UEvent(用户事件)。这些事件通常来自内核的 netlink 套接字,涉及硬件变化如 USB 连接、电池状态、GPIO 变化等。UEventObserver 允许应用程序或系统服务(如 BatteryService、UsbService)订阅特定匹配字符串的事件,并在事件发生时回调 onUEvent 方法。
该类不是标准 SDK 的一部分(android.jar 中不可见),因此在应用开发中无法直接导入和使用,通常需要在 AOSP(Android Open Source Project)环境中编译或通过反射访问。它依赖于 JNI(Java Native Interface)与底层 C/C++ 代码交互,使用 hardware_legacy/uevent.h 库处理内核事件。
源码概述(UEventObserver.java)
关键类结构
UEventObserver:抽象基类。
抽象方法:onUEvent(UEvent event) – 子类需实现此方法处理事件。
UEvent:内部静态类,表示单个事件,封装了键值对(如 "USB_STATE=1")。方法:get(String key) 获取值;toString() 输出事件字符串。
UEventThread:内部静态类,继承 Thread,负责事件轮询和分发。
单例:通过 getThread() 获取,确保进程只有一个实例。
成员:ArrayList<Object> mKeysAndObservers:存储匹配字符串和观察者(偶数索引为 match,奇数为 Observer)。使用 ArrayList 模拟 Multimap,因为 Android 早期无内置 Multimap。
ArrayList<UEventObserver> mTempObserversToSignal:临时列表,用于分发事件。
运行逻辑:在 run() 中循环调用 native 方法等待事件,解析后分发给匹配的观察者。
startObserving(String match):
- 参数:match 为非空字符串,如 "USB_STATE" 或 "SWITCH_STATE"。
- 逻辑:获取 UEventThread 实例,调用 addObserver(match, this) 添加订阅。如果线程未启动,则启动它。
- 异常:match 为空抛 IllegalArgumentException。
- 源码片段:
public final void startObserving(String match) {
if (match == null || match.isEmpty()) {
throw new IllegalArgumentException("match substring must be non-empty");
}
final UEventThread t = getThread();
t.addObserver(match, this);
}
stopObserving(String match):
- 移除指定 match 的订阅。
- 逻辑:从 mKeysAndObservers 中移除对应条目。
UEventThread 的 run():
调用 nativeSetup() 初始化 native 层。
循环:nativeWaitForNextEvent() 获取事件字符串。
如果事件非空,调用 sendEvent(message) 分发:遍历 mKeysAndObservers,检查 message 是否包含 match。
匹配则解析为 UEvent,添加到临时列表。
遍历临时列表,调用 onUEvent 回调。
源码片段
@Override
public void run() {
nativeSetup();
while (true) {
String message = nativeWaitForNextEvent();
if (message != null) {
if (DEBUG) Log.d(TAG, message);
sendEvent(message);
}
}
}
private void sendEvent(String message) {
// 解析 message 为键值对
HashMap<String, String> map = new HashMap<String, String>();
// ... 解析逻辑 ...
UEvent event = new UEvent(map);
// 查找匹配观察者
for (int i = 0; i < mKeysAndObservers.size(); i += 2) {
String match = (String) mKeysAndObservers.get(i);
if (message.contains(match)) {
mTempObserversToSignal.add((UEventObserver) mKeysAndObservers.get(i + 1));
}
}
// 回调
for (UEventObserver observer : mTempObserversToSignal) {
observer.onUEvent(event);
}
mTempObserversToSignal.clear();
}
- JNI 实现(android_os_UEventObserver.cpp)
Java 层通过 native 方法与内核交互,JNI 代码位于 frameworks/base/core/jni/android_os_UEventObserver.cpp。它使用 uevent.h(来自 system/core/libcutils 或 hardware_legacy)库,该库封装了 netlink 套接字(AF_NETLINK,NETLINK_KOBJECT_UEVENT)。
关键 native 方法
nativeSetup():
调用 uevent_init() 初始化 netlink 套接字。
如果失败,抛 RuntimeException:"Unable to open socket for UEventObserver"。
源码片段:
static void android_os_UEventObserver_native_setup(JNIEnv *env, jclass clazz) {
if (!uevent_init()) {
jniThrowException(env, "java/lang/RuntimeException", "Unable to open socket for UEventObserver");
}
}
- nativeWaitForNextEvent()(Java 中为 String 返回):
实际对应 next_event JNI 方法,使用字节数组 buffer 接收事件。
调用 uevent_next_event(buffer, buf_sz - 1) 阻塞等待内核事件。
返回事件长度,Java 层转换为 String。
源码片段:
static int android_os_UEventObserver_next_event(JNIEnv *env, jclass clazz, jbyteArray jbuffer) {
int buf_sz = env->GetArrayLength(jbuffer);
char *buffer = (char*)env->GetByteArrayElements(jbuffer, NULL);
int length = uevent_next_event(buffer, buf_sz - 1);
env->ReleaseByteArrayElements(jbuffer, (jbyte*)buffer, 0);
return length;
}
- nativeAddMatch(String match) 和 nativeRemoveMatch(String match):
调用 uevent_add_filter() 和 uevent_remove_filter() 添加/移除内核过滤器,提高效率(避免接收无关事件)。
源码类似,未在早期版本中完全实现,但 AOSP 中有。
3. 整体架构与工作流程
初始化:
系统进程(如 SystemServer)启动时,创建 UEventObserver 子类实例。
调用 startObserving(match),启动 UEventThread(如果未启动)。
事件监听:
UEventThread 在后台线程运行,nativeSetup() 打开 netlink 套接字。
内核通过 netlink 广播 UEvent(如 add@/devices/... USB_STATE=1)。
uevent_next_event() 阻塞读取,获取原始字符串。
事件分发:
Java 层解析字符串为键值对(e.g., HashMap<"KEY", "VALUE">)。
检查所有订阅的 match,匹配则创建 UEvent 对象。
回调 onUEvent(UEvent event) – 线程安全(synchronized)。
停止监听:
stopObserving(match) 移除订阅,但线程继续运行(进程只有一个线程)。
资源管理:
线程永不停止,除非进程结束。
常见问题:uevent_init() 失败(权限或套接字冲突),导致 RuntimeException 和 bootloop(系统进程崩溃)。