UEvent机制在Android中的应用,就我所知,USB的插拔和耳机的插拔检测都是通过UEvent来实现的。下面的例子,首先说明代码中是如何实现检测的,后面的文章再详细说明UEvent机制。
在Android4.0以上的版本,耳机检测的源文件位于frameworks/base/services/java/com/android/server/WiredAccessoryObserver.java,在android4.0以前是HeadsetObserver.java。从名字可以看出,它主要是用来检测有线的设备连接状态。
USB也是有线设备,但它的检测代码是独立的,位于frameworks/base/services/java/com/android/server/usb/usbdevicemanager.java。
首先,来看耳机检测的机制。
在WiredAccessoryObserver中,主要检测以下几个设备的连接状态(参考函数makeObservedUEventList(),其实就是生成要检测的设备文件节点路径)
1.headset
2.usb_headset
3.hdmi_audio/hdmi
都是与audio相关的设备,一般来说,headset都是支持的,后面的两种设备不是所有平台都支持。
从代码路径可以知道,位于service目录,因此可以猜想它是在android system server初始化的时候实例化的。在system server的serverthread 的run()函数中有如下代码:
try {
Slog.i(TAG, "Wired Accessory Observer");
// Listen for wired headset changes
new WiredAccessoryObserver(context);
} catch (Throwable e) {
reportWtf("starting WiredAccessoryObserver", e);
}
class WiredAccessoryObserver extends UEventObserver,WiredAccessoryObserver继承自UEventObserver。
首先看构造函数:
public WiredAccessoryObserver(Context context) {
mContext = context;
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver");
mWakeLock.setReferenceCounted(false);
mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
context.registerReceiver(new BootCompletedReceiver(),
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
}
从上面的代码中似乎没有看到与耳机检测相关的代码,只是初始化了wakelock用于电源管理,获取audiomanager接口,然后注册了一个广播接收器,用于监听ACTION_BOOT_COMPLETED。
ACTION_BOOT_COMPLETED在系统启动完成后发出,收到intent后:
private final class BootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// At any given time accessories could be inserted
// one on the board, one on the dock and one on HDMI:
// observe three UEVENTs
init(); // set initial status
for (int i = 0; i < uEventInfo.size(); ++i) {
UEventInfo uei = uEventInfo.get(i);
startObserving("DEVPATH="+uei.getDevPath());
}
}
}
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
try {
String devPath = event.get("DEVPATH");
String name = event.get("SWITCH_NAME");
int state = Integer.parseInt(event.get("SWITCH_STATE"));
updateState(devPath, name, state);
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
private synchronized final void updateState(String devPath, String name, int state)
{
for (int i = 0; i < uEventInfo.size(); ++i) {
UEventInfo uei = uEventInfo.get(i);
if (devPath.equals(uei.getDevPath())) {
update(name, uei.computeNewHeadsetState(mHeadsetState, state));
return;
}
}
}
public int computeNewHeadsetState(int headsetState, int switchState) {
int preserveMask = ~(mState1Bits | mState2Bits);
int setBits = ((switchState == 1) ? mState1Bits :
((switchState == 2) ? mState2Bits : 0));
return ((headsetState & preserveMask) | setBits);
}
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
mWakeLock.release();
}
};