Android audio(2)-audioservice

发布于:2025-04-09 ⋅ 阅读:(30) ⋅ 点赞:(0)

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) {0
        mSystemServiceManager.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实例
        }
 
        @Override
        public void onStart() {
            publishBinderService(Context.AUDIO_SERVICE, mService);//向系统注册audioservice服务
        }
 
        @Override
       public 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;
    }
 
    @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"));
            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的setWiredDeviceConnectionState

	mHeadsetState = 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调度。
类似失去焦点。重新进入等待队列。当进程在等待队列中重新获得调度后,又可以执行了,这就类似恢复焦点。


网站公告

今日签到

点亮在社区的每一天
去签到