在项目中,遇到问题:插入不带麦耳机,状态栏显示仍然是带麦图标。
解决此问题涉及到耳机的拔插事件传递流程,在此分析一下Android系统,耳机拔插流程源码分析。
Android系统的耳机目前可以实现拍照、暂停/播放、打电话等功能,这一切的基础是耳机拔插成功,结合InputManagerService的相关知识,主要从framework层面分析耳机插拔事件的传递。
一、驱动层的事件上传
使用adb 命令可以查看当前手机插入的耳机状态,命令为:adb shell cat /sys/class/switch/h2w/state
- 插入带麦耳机
ubuntu@ubuntu:~/headset$ adb shell cat /sys/class/switch/h2w/state
11
- 插入不带麦耳机
ubuntu@ubuntu:~/headset$ adb shell cat /sys/class/switch/h2w/state
9
通过这个命令,我们可以初步判断,状态栏耳机显示异常问题是出现在驱动层还是上层。
二、framework层代码分析
前文提到,耳机拔插主要涉及到InputManagerService这个系统重量级服务。关于IMS(inoutManagerService)的具体介绍,请参考此文章:http://blog.csdn.net/jinzhuojun/article/details/41909159 ,作者很详尽的介绍了IPM的事件读取和分发过程。 本文章只挑拣IPM中与耳机事件相关的内容分析。
Android系统的事件主要分为两类:
按键事件(KeyEvent)
由物理按键产生的事件。 对于嵌入式设备,通常不会保留太多的物理按键,手机一般有Home, Back, Menu, Volume Down, Volume Up,我们讨论的耳机事件也归类在此。触摸事件(TouchEvent)
在手机屏幕上面的点击、拖动事件,以及它们的组合产生的各种事件。
2.1 涉及到的类
InputManagerService.java
/framework/base/services/core/java/com/android/server/input/InputManagerService.javaWiredAccessoryManager.java
/framework/base/services/core/java/com/android/server/WiredAccessoryManager.javaconfig.xml
/framework/base/core/res/res/values/config.xmlSystemServer.java
/framework/base/services/java/com/android/server/SystemServer.javaAudioManager.java
/framework/base/media/java/android/media/AudioManager.javaAudioService.java
framework/base/media/java/android/media/AudioService.java
2.2 设置Event上传方式
Android中有两种Event上传方式:InputEvent
(linux的 /dev/input/event subsystem),和UEvent
(framework下的比较老的event方式),两种方式的切换是通过属性配置实现的,在config.xml
文件中,有如下代码:
<!-- When true use the linux /dev/input/event subsystem to detect the switch changes on the headphone/microphone jack.
When false use the older uevent framework. -->
<bool name="config_useDevInputEventForAudioJack">false</bool>
public InputManagerService(Context context) {
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
String doubleTouchGestureEnablePath = context.getResources().getString(
R.string.config_doubleTouchGestureEnableFile);
mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
new File(doubleTouchGestureEnablePath);
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
useDevInputEventForAudioJack设置为true使用/dev/input/event,FALSE时设置为UEvent.
在这里我们可以看到,当前使用的是UEvent方式。(UEvent方式支持热插拔,是一种适合耳机拔插事件的方式。)
2.2 IMS 耳机事件传递
插入耳机后,驱动层会将耳机事件首先,传递到IMS的notifySwitch()
函数,驱动层的检测与向上传递在此分析。
@2.2.1 notifySwitch()
// Native callback.
private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
if (DEBUG) {
Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues)
+ ", mask=" + Integer.toHexString(switchMask));
}
.....
.....
//此处是最重要的内容,在这里,会将耳机拔插时间传递给mWiredAccessoryCallbacks回调
if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
switchMask);
}
......
...