在前面的小节中,我们编写了一个驱动程序,模拟耳机的插拔事件,其可以上报耳机的拔插事件,并且修改了android的源代码,可以根据耳机的拔插事件,在状态栏上现实或者消除耳麦的图标,这节视频我们讲解耳麦插拔事件导致的程序调用流程。

前面提到过,有两种上报插拔事件的方式,一种是使用输入子系统,另外一种是使用swith dev(实质是使用uevent,通过网络上报事件),那么我们的anroid系统最终是使用哪种方式呢?我们可以去配置android系统,可以去配置SDK/frameworks/base/core/res/res/values/config.xml
文件,或者:
SDK/device/rockchip/common/overlay/frameworks/base/core/res/res/values/config.xml
第二个文件会覆盖第一个文件,修改文件中的config_useDevInputEventForAudioJack变量,该值为true时使用input子系统, 为false时使用uevent机制。

下面是详细的调用流程:

input系统方法

我们先来讲解使用input子系统上报流程,后面再讲解uevent的调用流程:
1.input驱动上报
2.input系统上报给Audio系统。
3.Audio系统把事件发送给Activity(感兴趣的应用程序)
4.最终发送给某个APP(前面的小节为状态栏APP)


其上input系统是获取事件,然后上报事件,在输入子系统章节我们讲解过,有一个InputReader线程,存在成员EventHub,其会监测多个节点(使用poll机制),InputReader获得事件之后,发送给InputDispatcher线程,InputDispatcher程处理之后上报给InputManagerService,InputManagerService调用WiredAccessoryManager中CallBack(回调函数)函数,WiredAccessoryManager上报Andio系统了。

首先我们要确定CallBack函数,这样在输入子系统过得事件之后,才能进入音频子系统,根据时序图我们打开SystemServer.java文件:

private void startOtherServices() {// Listen for wired headset changesinputManager.setWiredAccessoryCallbacks(new WiredAccessoryManager(context, inputManager));/*回调函数,当输入系统获得数据之后,调用callbacks函数*/mWiredAccessoryCallbacks = callbacks;

可以看到其中创建了一个WiredAccessoryManager对象,传递给inputManager.setWiredAccessoryCallbacks函数(设置回调函数)。

现在我们打开InputReader.cpp:

void InputReader::loopOnce() {/*获得事件,如果没有事件,则会进入休眠状态*/size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);/*得到事件之后进行处理*/processEventsLocked(mEventBuffer, count);/*对于每一个输入设备都会创建一个InputDevice对象,还函数会调用InputDevice中的process方法*/processEventsForDeviceLocked(deviceId, rawEvent, batchSize);/*调用InputDevice中的process*/device->process(rawEvents, count);/*InputDevice中存在InputMapper成员,调用该成员的process函数*/mapper->process(rawEvent);

对于我们的开关事件,其对应的mapper->process(rawEvent)函数就是SwitchInputMapper::process函数:

/*对收到的事件分别进行处理*/
void SwitchInputMapper::process(const RawEvent* rawEvent) {/*记录驱动程序上报的code值(松开或者按下)*/processSwitch(rawEvent->code, rawEvent->value);/*当接受到同步信号之后,进行处理*/sync(rawEvent->when);/*获得监听器,调用监听器中的notifySwitch函数*/getListener()->notifySwitch(&args);

其中进行监听的为InputDispatcher线程,所以我们进入InputDispacher.cpp找到:

void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {mPolicy->notifySwitch(args->eventTime,args->switchValues, args->switchMask, policyFlags);

其上mPolicy->notifySwitch实现与com_android_server_input_InputManagerService.cpp文件:

void NativeInputManager::notifySwitch()/*调用java中的同名函数notifySwitch*/env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifySwitch,when, switchValues, switchMask);

打开InputManagerService.java文件,找到其中的notifySwitch函数:

private void notifySwitch(long whenNanos, int switchValues, int switchMask) {/*这个变量就是之前我们修改xml设置的变量*/if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,switchMask);

其上的notifyWiredAccessoryChanged实现与WiredAccessoryManager.java:

public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask)mSwitchValues = (mSwitchValues & ~switchMask) | switchValues;/*根据驱动程序上报的值确定headset变量*/......case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT:headset = BIT_HEADSET;....../*更新状态*/updateLocked(NAME_H2W,(mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);

在updateLocked函数中,其将会进入audio系统,暂时我们不去分析该函数,我们先回过头UEvent方法。

UEvent方法

UEvent机制就是通过网络传输,把状态上报给应用程序,那么在android的UEnvet中必定有一个线程,这个线程称为UEventThread,其工作为1.等待Socket数据。2,收到数据之后发送数据/SendEvent(发送给感兴趣的进程:WiredAccessoryObserver)。下面是UEevent与Inpout系统对比的简单图示:

其中WiredAccessoryObserver属于文件WiredAccessoryManager.java,那么我们猜测WiredAccessoryManager.java文件中,其会去注册一个WiredAccessoryObserver,进入WiredAccessoryManager.java:

public WiredAccessoryManager(Context context, InputManagerService inputManager) {/*创建一个观察者*/mObserver = new WiredAccessoryObserver();

其中WiredAccessoryObserver的构造函数:

public WiredAccessoryObserver() {/*创建一个观察事件的列表,可以猜测到其回去关注多个文件*/mUEventInfo = makeObservedUEventList();// Monitor h2wif (!mUseDevInputEventForAudioJack) {uei = new UEventInfo(NAME_H2W, BIT_HEADSET, BIT_HEADSET_NO_MIC, BIT_LINEOUT);/*系统装备就绪之后,该函数会被调用*/void init() {/*开始观察*/startObserving("DEVPATH="+uei.getDevPath());final UEventThread t = getThread();/*往UEventThread线程中添加观察者*/t.addObserver(match, this);

所以UEventThread线程接受到数据之后,应该就上报给WiredAccessoryObserver,我们在UEventThreadObserver.java中可以找到方法:

private static final class UEventThread extends Thread {public void run() {/*调用C++中的函数,初始化网络*/nativeSetup();/*等待下一个事件,调用C++的同名函数,读取到数据时返回*/String message = nativeWaitForNextEvent(); /*发送数据*/sendEvent(message);/*获得observer*/final UEventObserver observer =(UEventObserver)mKeysAndObservers.get(i + 1);mTempObserversToSignal.add(observer);/*调用observer中的onUEvent方法,在这里我们的观察者是WiredAccessoryObserver*/observer.onUEvent(event);

其上的nativeSetup会调用C++文件中的android_os_UEventObserver.cpp文件中的:

/*网络初始化与绑定*/
static void nativeSetup(JNIEnv *env, jclass clazz) {uevent_init()s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);bind(s, (struct sockaddr *) &addr, sizeof(addr))

nativeWaitForNextEvent也会调用C++的同名函数:

static jstring nativeWaitForNextEvent(JNIEnv *env, jclass clazz) {/*等待下一个事件*/length = uevent_next_event(buffer, sizeof(buffer) - 1)/*进入休眠。如果返回,代表接收到了数据*/nr = poll(&fds, 1, -1);/*读取数据*/int count = recv(fd, buffer, buffer_length, 0);

所以其最终会调用WiredAccessoryObserver中的onUEvent方法(WiredAccessoryManager.java中实现),

public void onUEvent(UEventObserver.UEvent event) {/*获取状态*/int state = Integer.parseInt(event.get("SWITCH_STATE"));/*更新状态*/updateStateLocked(devPath, name, state);

从这里我们可以知道,input系统方法与UEvent方法其最终都会调用到updateStateLocked函数去更新状态,

updateStateLocked

现在我们来查看一下updateStateLocked方法:

private void updateStateLocked(String devPath, String name, int state) {updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state));/*构造一个Message,其中含有headsetState*/Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,mHeadsetState, "");/*发送消息*/mHandler.sendMessage(msg);

发送消息之后,会导致消息的处理函数被调用,在该文件搜索MSG_NEW_DEVICE_STATE:

private final Handler mHandler = new Handler(Looper.myLooper(), null, true)case MSG_NEW_DEVICE_STATE:setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName);/*一系列的设置*/......./*最终设置有线设备的连接状态*/mAudioManager.setWiredDeviceConnectionState();

最看到mAudioManager我们知道,其进入Audio系统了,我们打开AudioManager文件:

public void setWiredDeviceConnectionState(int type, int state, String address, String name) {/**/IAudioService service = getService();/*执行AudioService.java中的同名函数*/service.setWiredDeviceConnectionState(type, state, address, name,mApplicationContext.getOpPackageName());/**/int delay = checkSendBecomingNoisyIntent(type, state);/*构造一个消息,放入队列*/queueMsgUnderWakeLock(mAudioHandler,MSG_SET_WIRED_DEVICE_CONNECTION_STATE,0,0,new WiredDeviceConnectionState(type, state, address, name, caller),delay);

我们搜索MSG_SET_WIRED_DEVICE_CONNECTION_STATE,找到处理消息的方法:

public void handleMessage(Message msg) {case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:onSetWiredDeviceConnectionState(connectState.mType, connectState.mState,
connectState.mAddress, connectState.mName, connectState.mCaller);sendDeviceConnectionIntent(device, state, address, deviceName);/*创建一个intent */Intent intent = new Intent();/*把状态放入intent*/intent.putExtra(CONNECT_INTENT_KEY_STATE, state);intent.putExtra(CONNECT_INTENT_KEY_ADDRESS, address);intent.putExtra(CONNECT_INTENT_KEY_PORT_NAME, deviceName);......一系列的判断,如有没有mic等等....../*发送给应用程序的管理者,进行广播*/ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);

在前面的小节我们修改了状态栏的程序,添加的代码如下:

/*对耳机插拔感兴趣,当接受到ActivityManagerNative的广播的时候*/
filter.addAction(Intent.ACTION_HEADSET_PLUG);/*取出广播,如果为ACTION_HEADSET_PLUG,执行updateHeadset函数*/
} else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {updateHeadset(intent);private final void updateHeadset(Intent intent) {final String action = intent.getAction();final int state = intent.getIntExtra("state", 4);final int mic = intent.getIntExtra("microphone", 4);/*从intent取出状态值,判断是拔出还是插入*/switch (state) {case 0:/*消除图标*/mIconController.setIconVisibility("headset", false);break;case 1:if (mic == 1) {mIconController.setIcon("headset", R.drawable.stat_sys_headset_with_mic, null);} else {mIconController.setIcon("headset", R.drawable.stat_sys_headset_without_mic, null);}/*显示图标*/mIconController.setIconVisibility("headset", true);break;}
}

这样我们就讲解完成了,再次贴出模块的框图:

08.音频系统:第003课_Linux音频驱动程序:第003节_耳麦拔插事件调用流程分析相关推荐

  1. 08.音频系统:第003课_Linux音频驱动程序:第001节_alsa音频驱动框架

    在上小节我们分析了Adndroid系统音频的框架,这么一个复杂的系统我们怎么去学习呢?我们从下往上学,先分析音频的驱动程序,看看linux系统中驱动程序是怎么编写的,他的结构是怎么样的,然后在琢磨Ti ...

  2. 08.音频系统:第003课_Linux音频驱动程序:第002节_ASoC音频驱动框架

    通过上小节alsa音频驱动框架的分析,知道如果要去写一个声卡驱动,我们需要分配,设置,注册snd_card结构体: 定义一个struct snd_card *card; snd_card_new // ...

  3. 08.音频系统:第003课_Linux音频驱动程序:第005节_DAPM_widget_route_path

    DAPM是Dynamic Audio PowerManagement的缩写,译过来就是动态音频电源管理的意思.DAPM是为了使基于linux的移动设备上的音频驱动子系统,在任何时候都工作在最小功耗状态 ...

  4. 08.音频系统:第003课_Linux音频驱动程序:第003节_RK3399声卡驱动移植_combine

    该小节我们讲解一下开发板RK3399声卡rt5651的移植,主要分为4个部分,platfrom,codec,machine,dts(设备树). 首先我们从设备树开始讲起,当然在讲解之前,我们先来体验下 ...

  5. android音频系统(5):AudioService之音频焦点

    前言:上一节我们分析了AudioService对音量的管理,这一节来看下AudioService对音频焦点的处理,也就是音频系统中的AudioFocus机制,它用来处理多个音频不合理的同时播放的糟糕后 ...

  6. Android中实现系统声音录制(内置声源的录制)-音频通道及framework调用流程分析

    推荐阅读 Android Audio音频系统 Android Framework学习路线

  7. 如何避免音频爆音/杂响?音频爆音常见的解决办法

    如果CPU负载过高,并且无法在所选择的缓冲速率内缓冲音频,则在播放过程中可能会出现音频故障或音频掉线(播放时出现停顿)等问题.如何解决这一问题呢?本文详细讲解了音频爆音常见的解决办法 要解决音频爆音/ ...

  8. 高通平台环境搭建,编译,系统引导流程分析 .

    1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通开发板上烧录文件系统 建立高通平台开发环境 高通平台,android和 modem 编译流程分析 高通平台 7620 启动流 ...

  9. Android系统开机到Launcher启动流程分析

    本文基于Android10.0的源码. 由于google团队在对framework层代码进行大量重构,所以代码变动还是挺大的. 常见基础问题: SystemServer系统服务进程是如何创建的?Lau ...

最新文章

  1. 报名 | 2019年社会计算机国际会议
  2. 为何生命进化的方向是衰老,而不是永生?
  3. 2015 11月30日 一周工作计划与执行
  4. word List20
  5. javescript的内置对象
  6. 【技术文档】jeecg3.7.1-maven版本搭建环境手把手入门-eclipse
  7. 南京计算机审计行业工资,南京最新各行业平均工资曝光!看完分分钟想跳槽!...
  8. 计算机开放电子书归档 2018
  9. Java容器Set接口
  10. 梁勇:展望 2017年商业智能BI 发展的趋势
  11. “开闭原则”实现图书售卖简单实现
  12. SPSS正态分布,泊松分布,指数分布,均匀分布检验
  13. 谷歌SRE运维模式解读
  14. 论文笔记:Connectionist Temporal Classification: Labelling Unsegmented Sequence
  15. 到此一游︱2022 Google 开发者大会
  16. Java每日一练(1)
  17. 怎么登录163vip邮箱,登录方式有哪些?
  18. Android 天气APP(九)细节优化、必应每日一图
  19. office修复找不到msi_office2013安装出错,老是出现找不到officeMUI.msi或则officeMUI.xml等,是什么原 - Microsoft Community...
  20. 漫谈程序员系列:怎么告别“混日子”

热门文章

  1. 查看Linux中DMA的大小
  2. 关于未来生物科学实验室株式会社正品鉴定系统
  3. 【Socket网络编程进阶与实战】-----简易聊天室案例
  4. 《你最美》换发型应用源码
  5. 基于MQTT的智能家居程序框架day4
  6. Amazon SageMaker助力行者AI实现游戏内容过滤准确率96%
  7. 谷歌Chrome播放RTSP监控视频流,支持VUE、抓图、录像、回放、倍速等
  8. 东莞市上市后备企业名单 (金融工作局)
  9. 春运网络购票诈骗频发 消费者慎防钓鱼陷阱
  10. ubuntu14.04下载源码