欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 艺术 > Android audio(2)-audioservice

Android audio(2)-audioservice

2025/4/18 5:52:37 来源:https://blog.csdn.net/weixin_52370850/article/details/143477458  浏览:    关键词:Android audio(2)-audioservice

AudioService是Android的系统服务(systemservice),由SystemServer负责启动。提供Android APK 所需的非数据通路(playback/capture)相关的audio 功能实现,是binder通信中的server端,与之对应的 Client 端是应用进程中的AudioManager。两者之间通过binder进行通信。注意audioService没有自己独立的进程,这点和后面涉及的audioserver有所不同。

我们要注意Android系统本身不是个系统,没有内存管理,进程管理,设备管理等系统功能。android可以理解为linux内核+定制服务。

一、audioservice服务启动流程

1、SystemServer

源码路径:/frameworks/base/services/java/com/android/server/SystemServer.java

boolean isArc = context.getPackageManager().hasSystemFeature("org.chromium.arc");private void startOtherServices(@NonNull TimingsTraceAndSlog t) {t.traceBegin("StartAudioService");if (!isArc) {0mSystemServiceManager.startService(AudioService.Lifecycle.class);//启动audioservice服务} else {String className = context.getResources().getString(R.string.config_deviceSpecificAudioService);try {mSystemServiceManager.startService(className + "$Lifecycle");} catch (Throwable e) {reportWtf("starting " + className, e);}}t.traceEnd();
}

SystemServer在startOtherServices中启动了AudioService。

2、AudioService

源码位置:/frameworks/base/services/core/java/com/android/server/audio/AudioService.java

public class AudioService extends IAudioService.Stub implements AccessibilityManager.TouchExplorationStateChangeListener, AccessibilityManager.AccessibilityServicesStateChangeListener {public static final class Lifecycle extends SystemService {//父类就是我们所说的系统服务private AudioService mService;public Lifecycle(Context context) {super(context);mService = new AudioService(context);//创建audioservice实例}@Overridepublic void onStart() {publishBinderService(Context.AUDIO_SERVICE, mService);//向系统注册audioservice服务}@Overridepublic void onBootPhase(int phase) {if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {mService.systemReady();}}}
}

从上面代码可以看出,SystemServer实际启动的是 AudioService 中的 Lifecycle 类,该类继承自SystemService,在构造函数中创建 AudioService实例。为什么audioflinger和audiopolicyservice直接继承自binderservice而不是定义一个内部类继承?这个问题我还没想明白。

二、AudioService功能概述

AudioService 继承自 IAudioService.Stub。IAudioService.Stub 类是通过 IAudioService.aidl 生成。AudioService 位于 Binder Native 端。AudioManager 持有 AudioService 的 Binder Proxy 端,是 AudioService 在客户端的一个代理。几乎所有客户端对 AudioManager 进行的请求,最终都会交由 AudioService 实现。
AudioService 是整个音频系统在java层面的关键类,它的功能非常多,各功能在audioservice内部也通过类进行了封装,功能内部具有较高的内聚性,下面介绍 AudioService 的主要功能。观察audioservice代码的演变历史,可以看出google对代码还是有持续的重构动作。这点很值得我们学习。

1、音量调节

在 Android 手机上有两种改变系统音量的方式。一,通过手机的音量键进行音量调整,二,从设置界面中调整某一种类型音频的音量。在AndroidTV中还支持用红外/蓝牙遥控器调节音量。另外,应用程序可以随时将某种类型的音频静音。这些功能都是通过 AudioService 实现。

// 注意这里的suggest,表示调用者期望调整的音量类型
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, String callingPackage, String caller, int uid, boolean hasModifyAudioSettings, int keyEventMode)

2、音频设备管理

AudioService 除了提供音量设置功能外,还接收音频设备的插拔通知,并通过JNI层通知到native服务,以检测耳机的插入过程为例。

有线设备管理器
在 SystemServer 的 startOtherServices 方法中启动了有线设备的监听服务WiredAccessoryManager。

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {t.traceBegin("StartWiredAccessoryManager");try {// 监听有线耳机的变化inputManager.setWiredAccessoryCallbacks(new WiredAccessoryManager(context, inputManager));} catch (Throwable e) {reportWtf("starting WiredAccessoryManager", e);}t.traceEnd();
}

WiredAccessoryManager 中通过 WiredAccessoryObserver 来监听有线设备的插拔。
源码位置:/frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.java

private final WiredAccessoryObserver mObserver;public WiredAccessoryManager(Context context, InputManagerService inputManager) {......mObserver = new WiredAccessoryObserver();
}class WiredAccessoryObserver extends UEventObserver {private final List<UEventInfo> mUEventInfo;......private List<UEventInfo> makeObservedUEventList() {if (!mUseDevInputEventForAudioJack) {uei = new UEventInfo(NAME_H2W, BIT_HEADSET, BIT_HEADSET_NO_MIC, BIT_LINEOUT);if (uei.checkSwitchExists()) {retVal.add(uei);} else {......}}uei = new UEventInfo(NAME_USB_AUDIO, BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL, 0);if (uei.checkSwitchExists()) {retVal.add(uei);} else {......}uei = new UEventInfo(NAME_HDMI_AUDIO, BIT_HDMI_AUDIO, 0, 0);if (uei.checkSwitchExists()) {retVal.add(uei);} else {uei = new UEventInfo(NAME_HDMI, BIT_HDMI_AUDIO, 0, 0);if (uei.checkSwitchExists()) {retVal.add(uei);} else {......}}return retVal;}@Overridepublic 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"));synchronized (mLock) {updateStateLocked(devPath, name, state);}} catch (NumberFormatException e) {......}}private void updateStateLocked(String devPath, String name, int state) {for (int i = 0; i < mUEventInfo.size(); ++i) {UEventInfo uei = mUEventInfo.get(i);if (devPath.equals(uei.getDevPath())) {updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state));return;}}}
private void updateLocked(String newName, int newState) {Log.i(TAG, "MSG_NEW_DEVICE_STATE");Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,mHeadsetState, "");mHandler.sendMessage(msg);//这个消息的处理就会调用AudioService的setWiredDeviceConnectionStatemHeadsetState = headsetState;
}......
}

上面 WiredAccessoryObserver 会通过监听 /devices/virtual/switch/ 的节点变化,并根据变化调用AudioService的setWiredDeviceConnectionState通知 AudioService进行耳机插播事件的处理。

3、音频焦点管理

常见的音频焦点管理功能有如下两种。
1:手机正在播放音乐,突然电话来电,这时候音乐播放声音会停止,而只留电话声音。
2:手机正在播放音乐,这时候如果导航应用播报,音乐播放音量会减小,等待导航播报结束后,音乐播放音量会恢复。
上面两个场景便用到了android的音频焦点管理,音频焦点策略就是拿到焦点的应用才能播放声音,每个音频实例播放之前都应该向 AudioService 申请焦点,申请成功才开始播放;当一个音频实例正在播放的过程中,此时焦点被其他音频播放实例抢占,这时候正在播放的的音频实例会丢失焦点,失去焦点的音频播放实例应该根据实际情况来进行静音,暂停播放或者适当减小音量等操作,等被抢占的焦点被归还的时候再把之前的音频播放状态恢复。
音频焦点策略只是android提供的一个机制,并且建议APK开发者遵守,如果应用都没有采用音频焦点策略管理机制,那么所有应用一起混合播放出来的音频声音,最终输出的声音内容将不可预料。通话相关的音频模块也会申请音频焦点,音频焦点的优先级是最高的,可以从任何拥有音频焦点的音频播放实例中抢走音频焦点。

关键流程/步骤:
1.申请焦点
2.失去焦点
3.恢复焦点

总结:
1.AM/AS是个C/S架构。AS的承载进程是systemserver。(进程名system_server)
2.AS三大功能:音量,设备,焦点。
3.焦点三大核心:申请,失去,恢复。(记忆口诀:生石灰)申失恢

启发:
焦点可以看做一类资源,仔细思考焦点的三大核心,我们不难发现和进程的CPU调度有相似之处。
进程要想运行就必须获得CPU的调度,类似焦点的申请,当更高优先级的进程出现或者时间片用完进程就会失去CPU调度。
类似失去焦点。重新进入等待队列。当进程在等待队列中重新获得调度后,又可以执行了,这就类似恢复焦点。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词