我们之前分析了CarAudioZone的比较核心的一个Api,setZoneIdForUid,我们知道通过将uid与zoneId绑定到一起的方式,实现多音区的功能。即不同音区的AudioFocus管理互不影响,我们的媒体也可以想播放在哪个Zone中就播放在哪个zone中,只要我们配置好car_audio_configuration.xml,以及设置对应的setZoneIdForUid即可。上一篇分析了AudioFocus在CarAudioZone中如何实现不同音区管理的,那么今天我们继续分析下播放流又是如何控制的,即播放流的一个路由策略
我们知道在我们对上层的应用调用setZoneIdForUid的时候,我们会调用到mAudioPolicy.setUidDeviceAffinity(uid, mCarAudioZones[zoneId].getAudioDeviceInfos()),关于这个调用的逻辑可查看下Android10.0CarAudioZone(五),这里也是先简单分析一下这几个参数都是做什么的,udi,是上层应用传入的应用的uid,我们知道zoneId也是上层应用传入的,这样我们就可以定位到我们要的CarAudioZone,再看下它的getAudioDeviceInfos
List<AudioDeviceInfo> getAudioDeviceInfos() {
final List<AudioDeviceInfo> devices = new ArrayList<>();
for (CarVolumeGroup group : mVolumeGroups) {
for (int busNumber : group.getBusNumbers()) {
devices.add(group.getCarAudioDeviceInfoForBus(busNumber).getAudioDeviceInfo());
}
}
return devices;
}
其实拿的就是我们解析car_audio_configuration.xml中,每个zone里group下所有的device的集合,拿到这个devices后与uid一同传给了AudioPolicy的setUidDeviceAffinity
public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
if (devices == null) {
throw new IllegalArgumentException("Illegal null list of audio devices");
}
synchronized (mLock) {
if (mStatus != POLICY_STATUS_REGISTERED) {
throw new IllegalStateException("Cannot use unregistered AudioPolicy");
}
final int[] deviceTypes = new int[devices.size()];
final String[] deviceAdresses = new String[devices.size()];
int i = 0;
for (AudioDeviceInfo device : devices) {
if (device == null) {
throw new IllegalArgumentException(
"Illegal null AudioDeviceInfo in setUidDeviceAffinity");
}
deviceTypes[i] =
AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
deviceAdresses[i] = device.getAddress();
i++;
}
final IAudioService service = getService();
try {
调用了AudioServe的setUidDeviceAffinity
final int status = service.setUidDeviceAffinity(this.cb(),
uid, deviceTypes, deviceAdresses);
return (status == AudioManager.SUCCESS);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setUidDeviceAffinity", e);
return false;
}
}
}
我们发现在AudioPolicy了一个封装,主要处理的是deviceTypes,和deviceAdresses,然后继续调用AudioService的setUidDeviceAffinity
public int setUidDeviceAffinity(IAudioPolicyCallback pcb, int uid,
@NonNull int[] deviceTypes, @NonNull String[] deviceAddresses) {
if (DEBUG_AP) {
Log.d(TAG, "setUidDeviceAffinity for " + pcb.asBinder() + " uid:" + uid);
}
synchronized (mAudioPolicies) {
final AudioPolicyProxy app =
checkUpdateForPolicy(pcb, "Cannot change device affinity in audio policy");
if (app == null) {
return AudioManager.ERROR;
}
if (!app.hasMixRoutedToDevices(deviceTypes, deviceAddresses)) {
return AudioManager.ERROR;
}
return app.setUidDeviceAffinities(uid, deviceTypes, deviceAddresses);
}
}
我们发现这里有个hasMixRoutedToDevices的检查
boolean hasMixRoutedToDevices(@NonNull int[] deviceTypes,
@NonNull String[] deviceAddresses) {
for (int i = 0; i < deviceTypes.length; i++) {
boolean hasDevice = false;
for (AudioMix mix : mMixes) {
if (mix.isRoutedToDevice(deviceTypes[i], deviceAddresses[i])) {
hasDevice = true;
break;
}
}
if (!hasDevice) {
return false;
}
}
return true;
}
这个继续判断mix.isRoutedToDevice(deviceTypes[i], deviceAddresses[i]),我们知道deviceType都是bus,那么就在继续看下AudioMix的这个函数
public boolean isRoutedToDevice(int deviceType, @NonNull String deviceAddress) {
if ((mRouteFlags & ROUTE_FLAG_RENDER) != ROUTE_FLAG_RENDER) {
return false;
}
if (deviceType != mDeviceSystemType) {
return false;
}
if (!deviceAddress.equals(mDeviceAddress)) {
return false;
}
return true;
}
从这个isRoutedToDevice的判断看返回ture,那么hasMixRoutedToDevices反回也是ture,回到AudioService中,继续调用AudioPolicyProxy的setUidDeviceAffinities
int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
final Integer Uid = new Integer(uid);
int res;
if (mUidDeviceAffinities.remove(Uid) != null) {
final long identity = Binder.clearCallingIdentity();
res = AudioSystem.removeUidDeviceAffinities(uid);
Binder.restoreCallingIdentity(identity);
if (res != AudioSystem.SUCCESS) {
Log.e(TAG, "AudioSystem. removeUidDeviceAffinities(" + uid + ") failed, "
+ " cannot call AudioSystem.setUidDeviceAffinities");
return AudioManager.ERROR;
}
}
final long identity = Binder.clearCallingIdentity();
res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
Binder.restoreCallingIdentity(identity);
if (res == AudioSystem.SUCCESS) {
mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses));
return AudioManager.SUCCESS;
}
Log.e(TAG, "AudioSystem. setUidDeviceAffinities(" + uid + ") failed");
return AudioManager.ERROR;
}
这里我们看到通过AudioSystem继续向下调用了,jni的部分略过,直接看AudioSystem中的setUidDeviceAffinities
status_t AudioSystem::setUidDeviceAffinities(uid_t uid, const Vector<AudioDeviceTypeAddr>& devices)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setUidDeviceAffinities(uid, devices);
}
调用到了AudioPolicyService
status_t AudioPolicyService::setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices) {
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
return PERMISSION_DENIED;
}
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
AutoCallerClear acc;
return mAudioPolicyManager->setUidDeviceAffinities(uid, devices);
}
在AudioPolicyService中继续调用到AudioPolicyManager
status_t AudioPolicyManager::setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices) {
ALOGV("%s() uid=%d num devices %zu", __FUNCTION__, uid, devices.size());
for (size_t i = 0; i < devices.size(); i++) {
if (!audio_is_output_device(devices[i].mType)) {
ALOGE("setUidDeviceAffinities() device=%08x is NOT an output device",
devices[i].mType);
return BAD_VALUE;
}
}
status_t res = mPolicyMixes.setUidDeviceAffinities(uid, devices);
if (res == NO_ERROR) {
for (size_t i = 0; i < devices.size(); i++) {
sp<DeviceDescriptor> devDesc = mHwModules.getDeviceDescriptor(
devices[i].mType, devices[i].mAddress, String8(),
AUDIO_FORMAT_DEFAULT);
SortedVector<audio_io_handle_t> outputs;
if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
outputs) != NO_ERROR) {
ALOGE("setUidDeviceAffinities() error in checkOutputsForDevice for device=%08x"
" addr=%s", devices[i].mType, devices[i].mAddress.string());
return INVALID_OPERATION;
}
}
}
return res;
}
在AudioPolicyManager中继续调用到mPolicyMixes.setUidDeviceAffinities(uid, devices),我们看下
status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices) {
for (size_t i = 0; i < size(); i++) {
const AudioPolicyMix* mix = itemAt(i).get();
if (!mix->isDeviceAffinityCompatible()) {
continue;
}
if (mix->hasUidRule(true , uid)) {
return INVALID_OPERATION;
}
}
removeUidDeviceAffinities(uid);
for (size_t i = 0; i < size(); i++) {
const AudioPolicyMix *mix = itemAt(i).get();
if (!mix->isDeviceAffinityCompatible()) {
continue;
}
bool deviceMatch = false;
for (size_t j = 0; j < devices.size(); j++) {
if (devices[j].mType == mix->mDeviceType
&& devices[j].mAddress == mix->mDeviceAddress) {
deviceMatch = true;
break;
}
}
if (!deviceMatch && !mix->hasMatchUidRule()) {
if (!mix->hasUidRule(false , uid)) {
mix->setExcludeUid(uid);
}
}
}
return NO_ERROR;
}
最终的mix->setExcludeUid(uid),改变了add了一个crit,我们简单看下这个源码
void AudioMix::setExcludeUid(uid_t uid) const {
AudioMixMatchCriterion crit;
crit.mRule = RULE_EXCLUDE_UID;
crit.mValue.mUid = uid;
mCriteria.add(crit);
}
这样把uid与audiomix就关联到一起了,这个逻辑就像这个单词Exclude,这是一个排除的关联,如果我们把uid关联到zone1中,那么zone2的audiomix会执行setExcludeUid。不是把uid和它对应音区的AudioMIx关联到一起,而是把uid与其对应因音区外的AudioMix关联上,此这块逻辑还需要细细体会理解。
到此AudioMix的部分也就结束了。
我们上层应用在setZoneIdForUid的时候,会调用到setUidDeviceAffinity,进而最终调用到AudioPolicy下的AudioPolicyMix中,最后把uid与AudioMix绑定到一起,实现路由功能。
到此CarAudio关于源码的逻辑就到这里了,可能有些地方说的不是很清楚,接下来会从一个demo在重新梳理下这部分逻辑。