目录
背景
外部音频焦点策略
外部音频路由策略
Android P automovie版本支持对外部音频策略的注册,主要包括二部分:外部音频焦点策略 以及 外部音频路由策略。
原因可以直接看google官方说明,大意就是针对车载系统而言,音频焦点需求更复杂 同时 音频路由相比手机版而言更简洁(简洁非简单,车载版本上,很多针对音频路由的策略基本都用不上)。
车载版本上,由对应的Car模块,其中与音频相关的就是CarAudioService,其中setupDynamicRouting实现了注册(CarAudioService.java):
AudioPolicy audioPolicy = getDynamicAudioPolicy();
int r = mAudioManager.registerAudioPolicy(audioPolicy);
if (r != AudioManager.SUCCESS) {
throw new RuntimeException("registerAudioPolicy failed " + r);
}
mAudioPolicy = audioPolicy;
可以看到通过AudioManager来向系统注册策略,而其中创建的AudioPolicy对象,就包含了焦点以及路由策略。流程如下(CarAudioService.java):
//1. 创建policy build对象
AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);
//2. 注册外部音频路由策略
//2.1 创建Mix规则 build对象
AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder();
//2.2 注册路由规则,规则支持多种,见AudioMixingRule。这里采用usage匹配规则,意识是
// 根据应用播发音频时指定的usage/streamType来选择对应的输出设备。
mixingRuleBuilder.addRule(
new AudioAttributes.Builder().setUsage(usage).build(),
AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE);
//2.3 将mix规则与设备关联起来并创建Mix对象
//其中info为设备的相关信息,比如采样率、格式、通道数
AudioFormat mixFormat = new AudioFormat.Builder()
.setSampleRate(info.getSampleRate())
.setEncoding(info.getEncodingFormat())
.setChannelMask(info.getChannelCount())
.build();
//deviceinfo为具体的设备,RouteFlags表示为ROUTE_FLAG_RENDER表示对应输出,同一我们可以针对输入建立规则
AudioMix audioMix = new AudioMix.Builder(mixingRuleBuilder.build())
.setFormat(mixFormat)
.setDevice(info.getAudioDeviceInfo())
.setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
.build();
//2.4 添加Mix规则,可以添加多个
builder.addMix(audioMix);
//3. 注册外部音频焦点策略
builder.setIsAudioFocusPolicy(true);
//mAudioPolicyFocusListener为AudioPolicy.AudioPolicyFocusListener实例
builder.setAudioPolicyFocusListener(mAudioPolicyFocusListener);
//4. 创建AudioPolicy对象
builder.build()
下面看registerAudioPolicy流程,通过AudioManager 实现(AudioManager.java):
//====================================================================
// Audio policy
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public int registerAudioPolicy(@NonNull AudioPolicy policy) {
......
//getConfig 路由策略Mix的封装
//cb 音频焦点回调对象
//hasFocusListener 是否由焦点监听对象,与上文对应
//isVolumeController 音量回调对象,即音量加、减、静音,有兴趣自己查看实现。
String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController());
if (regId == null) {
return ERROR;
} else {
//注册成功,设置状态
policy.setRegistration(regId);
}
// successful registration
......
return SUCCESS;
}
AudioService实现(AudioService.java):
public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb,
boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
//注册回调,当native Mix注册成功时会通知上层状态更新
AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
......
//二次封装,实际注册地方
AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener,isFocusPolicy, isVolumeController);
//标准binder死亡监听
pcb.asBinder().linkToDeath(app, 0/*flags*/);
regId = app.getRegistrationId();
//意味着支持多个策略。
mAudioPolicies.put(pcb.asBinder(), app);
......
return regId;
}
下面是AudioPolicyProxy构造函数(AudioService.java):
AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token,
boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
......
//有外部焦点策略意味着mHasFocusListener不为空
if (mHasFocusListener) {
mMediaFocusControl.addFocusFollower(mPolicyCallback);
// can only ever be true if there is a focus listener
//只有当明确设置外部策略时才会采用外部焦点策略。
if (isFocusPolicy) {
mIsFocusPolicy = true;
mMediaFocusControl.setFocusPolicy(mPolicyCallback);
}
}
//音量控制回调
if (mIsVolumeController) {
setExtVolumeController(mPolicyCallback);
}
//开始注册Mix策略
connectMixes();
}
先看addFocusFollower,它只会被动通知焦点变化,而不会将焦点请求交给外部策略(MediaFocusControl.java):
private ArrayList mFocusFollowers = new ArrayList();
//addFocusFollower将监听加入mFocusFollowers集合
void addFocusFollower(IAudioPolicyCallback ff) {
******
mFocusFollowers.add(ff);
notifyExtPolicyCurrentFocusAsync(ff);
******
}
//应用得到焦点通知
void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
for (IAudioPolicyCallback pcb : mFocusFollowers) {
try {
// oneway
pcb.notifyAudioFocusGrant(afi, requestResult);
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
+ pcb.asBinder(), e);
}
}
}
//应用失去焦点通知
void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
for (IAudioPolicyCallback pcb : mFocusFollowers) {
try {
// oneway
pcb.notifyAudioFocusLoss(afi, wasDispatched);
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
+ pcb.asBinder(), e);
}
}
}
下面看setFocusPolicy,会将焦点赋值给本地mFocusPolicy对象(MediaFocusControl.java):
void setFocusPolicy(IAudioPolicyCallback policy) {
if (policy == null) {
return;
}
synchronized (mAudioFocusLock) {
mFocusPolicy = policy;
}
}
当应用申请焦点时,会判断mFocusPolicy是否为空,如果不为空,则通过外部焦点策略来实现逻辑判断(申请成功 or 失败)(MediaFocusControl.java):
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
int sdk, boolean forceDuck) {
******
synchronized(mAudioFocusLock) {
******
final AudioFocusInfo afiForExtPolicy;
if (mFocusPolicy != null) {
// construct AudioFocusInfo as it will be communicated to audio focus policy
afiForExtPolicy = new AudioFocusInfo(aa, Binder.getCallingUid(),
clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/,
flags, sdk);
} else {
afiForExtPolicy = null;
}
******
// external focus policy?
if (mFocusPolicy != null) {
//调用notifyExtFocusPolicyFocusRequest_syncAf通知应用请求
if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) {
// stop handling focus request here as it is handled by external audio
// focus policy (return code will be handled in AudioManager)
return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY;
} else {
// an error occured, client already dead, bail early
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
}
}
}
boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi,
IAudioFocusDispatcher fd, IBinder cb) {
......
try {
//oneway
//通过notifyAudioFocusRequest调用到外部焦点策略实现
mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
return true;
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
+ mFocusPolicy.asBinder(), e);
}
return false;
}
mFocusPolicy实现在AudioPolicy.java中,这里就补展开了。当外部焦点策略判断完成时,通过如下方式将结果告知给系统,系统在返回给应用(AudioManager.java):
/**
* @hide
* Set the result to the audio focus request received through
* {@link AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
* @param afi the information about the focus requester
* @param requestResult the result to the focus request to be passed to the requester
* @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
@FocusRequestResult int requestResult, @NonNull AudioPolicy ap) {
if (afi == null) {
throw new IllegalArgumentException("Illegal null AudioFocusInfo");
}
if (ap == null) {
throw new IllegalArgumentException("Illegal null AudioPolicy");
}
final IAudioService service = getService();
try {
service.setFocusRequestResultFromExtPolicy(afi, requestResult, ap.cb());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
需要注意的是,此流程有个时间限制,不能超过200ms,定义如下(AudioManager.java):
/**
* Timeout duration in ms when waiting on an external focus policy for the result for a
* focus request
*/
private static final int EXT_FOCUS_POLICY_TIMEOUT_MS = 200
connectMixes简单调用AudioSystem.registerPolicyMixes(mMixes, true),它是native函数,实现在中,下面看registerPolicyMixes实现(android_media_AudioSystem.cpp):
static jint
android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz,
jobject jMixesList, jboolean registration)
{
......
status_t status;
jint jStatus;
jobject jAudioMix = NULL;
Vector mixes;
//将Java的AudioMix转为native对象
for (jint i = 0; i < numMixes; i++) {
jAudioMix = env->GetObjectArrayElement(jMixes, i);
if (!env->IsInstanceOf(jAudioMix, gAudioMixClass)) {
jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
goto exit;
}
AudioMix mix;
jStatus = convertAudioMixToNative(env, &mix, jAudioMix);
env->DeleteLocalRef(jAudioMix);
jAudioMix = NULL;
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
mixes.add(mix);
}
......
//调用AudioSystem::registerPolicyMixes注册,注意此AudioSystem是native而非Java侧
status = AudioSystem::registerPolicyMixes(mixes, registration);
......
return jStatus;
}
通过convertAudioMixToNative函数将Java对象转为native对象,这里先看下AudioMix构造(AudioMix.java):
private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags,
int deviceType, String deviceAddress) {
//封装实际Rule对象
mRule = rule;
//音频 编码格式、采样率、通道数
mFormat = format;
/*
* ROUTE_FLAG_RENDER 与 ROUTE_FLAG_LOOP_BACK 二选一,必须与mMixType对应
*/
mRouteFlags = routeFlags;
/*
* AudioMix.MIX_TYPE_PLAYERS 与 AudioMix.MIX_TYPE_RECORDERS 二选一
* 与Rule(AudioMixingRule#RULE_MATCH)相关:
* players => RULE_MATCH_ATTRIBUTE_USAGE、RULE_MATCH_UID
* recorders => RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET
*/
mMixType = rule.getTargetMixType();
//回调标识,仅内部使用
mCallbackFlags = callbackFlags;
//对应设备类型 AudioSystem.DEVICE_*
mDeviceSystemType = deviceType;
//对应设备地址
mDeviceAddress = (deviceAddress == null) ? new String("") : deviceAddress;
}
native构造如下,完全跟Java对应,这里就不详细说明(AudioPolicy.h):
class AudioMix {
public:
// flag on an AudioMix indicating the activity on this mix (IDLE, MIXING)
// must be reported through the AudioPolicyClient interface
static const uint32_t kCbFlagNotifyActivity = 0x1;
AudioMix() {}
AudioMix(Vector criteria, uint32_t mixType, audio_config_t format,
uint32_t routeFlags, String8 registrationId, uint32_t flags) :
mCriteria(criteria), mMixType(mixType), mFormat(format),
mRouteFlags(routeFlags), mDeviceAddress(registrationId), mCbFlags(flags){}
status_t readFromParcel(Parcel *parcel);
status_t writeToParcel(Parcel *parcel) const;
Vector mCriteria;
uint32_t mMixType;
audio_config_t mFormat;
uint32_t mRouteFlags;
audio_devices_t mDeviceType;
String8 mDeviceAddress;
uint32_t mCbFlags; // flags indicating which callbacks to use, see kCbFlag*
};
AudioSystem调用APS实现(AudioSystem.cpp):
status_t AudioSystem::registerPolicyMixes(const Vector& mixes, bool registration)
{
const sp& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->registerPolicyMixes(mixes, registration);
}
最终实现在APM中(AudioPolicyInterfaceImpl.cpp):
status_t AudioPolicyService::registerPolicyMixes(const Vector& mixes, bool registration)
{
......
AutoCallerClear acc;
//注意之前传递的值为true,表示注册
if (registration) {
return mAudioPolicyManager->registerPolicyMixes(mixes);
} else {
return mAudioPolicyManager->unregisterPolicyMixes(mixes);
}
}
篇幅有限,AudioPolicyManager::registerPolicyMixes实现就不展开了,其本质就是通过循环,分别将LOOP_BACK、RENDER对应的AudioMix注册到mPolicyMixes对象中,方式如下:
//这里的address即设备地址,与之前AudioMix设置的deviceAddress基本一致,但可能会受AudioPatch影响,具体见代码
mPolicyMixes.registerMix(address, mixes[i], desc)
针对输出,会通过如下方式采用我们定义的路由策略:
//当输出设备状态发生变化时,保持策略同步更新,比如连接蓝牙耳机。
status_t AudioPolicyManager::checkOutputsForDevice(const sp& devDesc,
audio_policy_dev_state_t state,
SortedVector& outputs,
const String8& address)
{
******
addOutput(output, desc);
//当设备地址不为空时,判断是否有设置外部策略,如果有,则直接关联此策略
if (device_distinguishes_on_address(device) && address != "0") {
sp policyMix;
if (mPolicyMixes.getAudioPolicyMix(address, policyMix) != NO_ERROR) {
ALOGE("checkOutputsForDevice() cannot find policy for address %s",
address.string());
}
policyMix->setOutput(desc);
desc->mPolicyMix = policyMix;
}
******
}
//在播发音频时,拦截
status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
......
sp desc;
//优先通过mPolicyMixes获取对应arrt的设备
if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) {
ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
if (!audio_has_proportional_frames(config->format)) {
return BAD_VALUE;
}
*stream = streamTypefromAttributesInt(&attributes);
*output = desc->mIoHandle;
ALOGV("getOutputForAttr() returns output %d", *output);
return NO_ERROR;
}
......
}
针对输入:
//在录制音频时,拦截
status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
input_type_t *inputType,
audio_port_handle_t *portId)
{
......
audio_devices_t device;
//针对地址不为空的外部设备
if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
status = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
if (status != NO_ERROR) {
goto error;
}
*inputType = API_INPUT_MIX_EXT_POLICY_REROUTE;
device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
address = String8(attr->tags + strlen("addr="));
} else {
//一般来说,走这里,其它情况
device = getDeviceAndMixForInputSource(inputSource, &policyMix);
}
......
}
audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource,
sp *policyMix)
{
audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
audio_devices_t selectedDeviceFromMix =
mPolicyMixes.getDeviceAndMixForInputSource(inputSource, availableDeviceTypes, policyMix);
//如果有定义策略,直接返回
if (selectedDeviceFromMix != AUDIO_DEVICE_NONE) {
return selectedDeviceFromMix;
}
return getDeviceForInputSource(inputSource);
}
注意策略不一定需要通过Java注册,也可以直接native方式,比如现在有个需求是当插入USB扬声器时(当前前提是设备必须支持),指定的音源类型(比如Media)需要通过USB扬声器来播发,而其它的不受影响,则可以在添加如下代码增加动态策略(因为上层粗初始化时没有USB设备,所以不能直接通过上层方式来配置):
status_t AudioPolicyManager::checkOutputsForDevice(const sp& devDesc,
audio_policy_dev_state_t state,
SortedVector& outputs,
const String8& address)
{
******
//注意这里看必须与USB识别的设备类型匹配
} else if(FORCE_USB_SPEAKER && (device & AUDIO_DEVICE_OUT_USB_HEADSET)){
AudioMix audioMix;
audioMix.mMixType = MIX_TYPE_PLAYERS;//AudioMix.MIX_TYPE_PLAYERS
audioMix.mRouteFlags = MIX_ROUTE_FLAG_RENDER;//AudioMix.ROUTE_FLAG_RENDER;
audioMix.mDeviceType = desc->device();
audioMix.mDeviceAddress = address;
audioMix.mCbFlags = 0;//AudioMix.mCallbackFlags
audioMix.mFormat.sample_rate = desc->mSamplingRate;
audioMix.mFormat.channel_mask = desc->mChannelMask;
audioMix.mFormat.format = desc->mFormat;
AudioMixMatchCriterion criterion1;
criterion1.mRule = RULE_MATCH_ATTRIBUTE_USAGE;
criterion1.mValue.mUsage = AUDIO_USAGE_MEDIA;
AudioMixMatchCriterion criterion2;
criterion2.mRule = RULE_MATCH_ATTRIBUTE_USAGE;
criterion2.mValue.mUsage = AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
audioMix.mCriteria.add(criterion1);
audioMix.mCriteria.add(criterion2);
if (mPolicyMixes.registerMix(address, audioMix, desc) == NO_ERROR) {
ALOGD("Success to registerMix for tbox output device");
}else{
ALOGD("Failed to registerMix for tbox output device");
}
}
******
}
当然,当设备被移除时,也需要删除此策略,读者可以自行实现。