Android R CarAudio行为变更
本文主要梳理CarAudio模块,从Q升到R的行为变更,如有错误或者理解偏差,欢迎指正
文章目录
一、CarAudioManager行为变更
1.1 成员变量
INVALID_AUDIO_ZONE用于CarOccupantService的初始化和判定,INVALID_VOLUME_GROUP_ID用于usage找groupId时,找不到对应groupId的情况,之前返回的-1。
/**
* Zone id of the invalid audio zone.
* @hide
*/
@SystemApi
public static final int INVALID_AUDIO_ZONE = 0xffffffff;
/**
* Volume Group ID when volume group not found.
* @hide
*/
public static final int INVALID_VOLUME_GROUP_ID = -1;
1.2 新增API
getOutputDeviceForUsage
通过usage拿到对应的device address 如BUS media,然后通过audiomanger拿到所有的outputdevice后进行address匹配,匹配成功返回AudioDeviceInfo
/**
* Gets the output device for a given {@link AudioAttributes} usage in zoneId.
*
* <p><b>Note:</b> To be used for routing to a specific device. Most applications should
* use the regular routing mechanism, which is to set audio attribute usage to
* an audio track.
*
* @param zoneId zone id to query for device
* @param usage usage where audio is routed
* @return Audio device info, returns {@code null} if audio device usage fails to map to
* an active audio device. This is different from the using an invalid value for
* {@link AudioAttributes} usage. In the latter case the query will fail with a
* RuntimeException indicating the issue.
*
* @hide
*/
@SystemApi
@Nullable
@RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
public AudioDeviceInfo getOutputDeviceForUsage(int zoneId,
@AudioAttributes.AttributeUsage int usage) {
try {
String deviceAddress = mService.getOutputDeviceAddressForUsage(zoneId, usage);
if (deviceAddress == null) {
return null;
}
AudioDeviceInfo[] outputDevices =
mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
for (AudioDeviceInfo info : outputDevices) {
if (info.getAddress().equals(deviceAddress)) {
return info;
}
}
return null;
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, null);
}
}
getInputDevicesForZoneId
先通过CarAudioService拿到对应zoneId的所有的inputDevices,然后通过audioManger拿到所有的InputDevice然后匹配对应的address,成功加入到list后一起返回
/**
* Gets the input devices for an audio zone
*
* @return list of input devices
* @hide
*/
@SystemApi
@RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
public @NonNull List<AudioDeviceInfo> getInputDevicesForZoneId(int zoneId) {
try {
return convertInputDevicesToDeviceInfos(
mService.getInputDevicesForZoneId(zoneId),
AudioManager.GET_DEVICES_INPUTS);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, new ArrayList<>());
}
}
private List<AudioDeviceInfo> convertInputDevicesToDeviceInfos(
List<AudioDeviceAttributes> devices, @AudioDeviceRole int flag) {
int addressesSize = devices.size();
Set<String> deviceAddressMap = new HashSet<>(addressesSize);
for (int i = 0; i < addressesSize; ++i) {
AudioDeviceAttributes device = devices.get(i);
deviceAddressMap.add(device.getAddress());
}
List<AudioDeviceInfo> deviceInfoList = new ArrayList<>(devices.size());
AudioDeviceInfo[] inputDevices = mAudioManager.getDevices(flag);
for (int i = 0; i < inputDevices.length; ++i) {
AudioDeviceInfo info = inputDevices[i];
if (info.isSource() && deviceAddressMap.contains(info.getAddress())) {
deviceInfoList.add(info);
}
}
return deviceInfoList;
}
1.3 移除API
getZoneIdForDisplay
getZoneIdForDisplayPortId
两个关于displayId和zoneId来回转换的API被移除了,通样的car_audio_configuration.xml里zones里绑定的display也被去掉了。
移除原因:carService里新增了CarOccupantZoneManger和service用来管理display和users,
/**
* API to get information on displays and users in the car.
*/
该api移除后,需要进行适配到getAudioZoneIdForOccupant和getOccupantForAudioZoneId API上
/**
* Get the zone id for the display
*
* @param display display to query
* @return zone id for display or
* CarAudioManager.PRIMARY_AUDIO_ZONE if no match is found.
* @hide
*/
@RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
public int getZoneIdForDisplay(Display display) {}
/**
* Get the zone id for the display port id passed in
*
* @param displayPortId display port id to query
* @return zone id for display port id or
* CarAudioManager.PRIMARY_AUDIO_ZONE if no match is found.
* @hide
*/
@RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
public int getZoneIdForDisplayPortId(byte displayPortId) {}
//======================================================================>
/**
* Gets the audio zone id for the occupant, or returns
* {@code CarAudioManager.INVALID_AUDIO_ZONE} if no audio zone matches the requirements.
* throws InvalidArgumentException if occupantZone does not exist.
*
* @hide
*/
@SystemApi
@RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
public int getAudioZoneIdForOccupant(@NonNull OccupantZoneInfo occupantZone) {
assertNonNullOccupant(occupantZone);
try {
return mService.getAudioZoneIdForOccupant(occupantZone.zoneId);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, null);
}
}
/**
* Gets occupant for the audio zone id, or returns {@code null}
* if no audio zone matches the requirements.
*
* @hide
*/
@Nullable
@SystemApi
@RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
public OccupantZoneInfo getOccupantForAudioZoneId(int audioZoneId) {
try {
return mService.getOccupantForAudioZoneId(audioZoneId);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, null);
}
}
二、CarAudioService的行为变更
Q的目录结构
src\com\android\car\audio\CarAudioDeviceInfo.java
src\com\android\car\audio\CarAudioDynamicRouting.java
src\com\android\car\audio\CarAudioFocus.java
src\com\android\car\audio\CarAudioService.java
src\com\android\car\audio\CarAudioZone.java
src\com\android\car\audio\CarAudioZonesHelper.java
src\com\android\car\audio\CarAudioZonesHelperLegacy.java
src\com\android\car\audio\CarVolumeGroup.java
src\com\android\car\audio\CarZonesAudioFocus.java
R的目录结构
src\com\android\car\audio\hal\AudioControlFactory.java
src\com\android\car\audio\hal\AudioControlWrapper.java
src\com\android\car\audio\hal\AudioControlWrapperV1.java
src\com\android\car\audio\hal\AudioControlWrapperV2.java
src\com\android\car\audio\hal\HalAudioFocus.java
src\com\android\car\audio\CarAudioContext.java
src\com\android\car\audio\CarAudioDeviceInfo.java
src\com\android\car\audio\CarAudioDynamicRouting.java
src\com\android\car\audio\CarAudioFocus.java
src\com\android\car\audio\CarAudioService.java
src\com\android\car\audio\CarAudioSettings.java
src\com\android\car\audio\CarAudioZone.java
src\com\android\car\audio\CarAudioZonesHelper.java
src\com\android\car\audio\CarAudioZonesHelperLegacy.java
src\com\android\car\audio\CarAudioZonesValidator.java
src\com\android\car\audio\CarVolume.java
src\com\android\car\audio\CarVolumeCallbackHandler.java
src\com\android\car\audio\CarVolumeGroup.java
src\com\android\car\audio\CarZonesAudioFocus.java
src\com\android\car\audio\FocusEntry.java
src\com\android\car\audio\FocusInteraction.java
src\com\android\car\audio\OWNERS
对比之后多了好多小模块,好多模块也做了优化
2.1 CarAudioZonesHelper解析car_audio_configuration 2.0
car_audio_configuration新增字段,新增字段后version也变成了2,如果使用了audioZoneId occupantZoneId inputDevices字段但是version没有生成2,会在解析阶段报异常。
<?xml version="1.0" encoding="utf-8"?>
<carAudioConfiguration version="2">
<zones>
<zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="1">
<volumeGroups>
<group>
<device address="bus0_media_out">
<context context="music"/>
</device>
<device address="bus3_call_ring_out">
<context context="call_ring"/>
</device>
</group>
<group>
<device address="bus1_navigation_out">
<context context="navigation"/>
<context context="emergency"/>
<context context="safety"/>
<context context="vehicle_status"/>
<context context="announcement"/>
</device>
</group>
</volumeGroups>
<inputDevices>
<inputDevice address="fm_tuner"/>
<inputDevice address="Built-In Mic"/>
</inputDevices>
</zone>
<zone name="rear seat zone" audioZoneId="2">
<volumeGroups>
<group>
<device address="bus100_rear_seat">
<context context="music"/>
<context context="navigation"/>
<context context="voice_command"/>
<context context="call_ring"/>
<context context="call"/>
<context context="alarm"/>
<context context="system_sound"/>
<context context="notification"/>
<context context="emergency"/>
<context context="safety"/>
<context context="vehicle_status"/>
<context context="announcement"/>
</device>
</group>
</volumeGroups>
<inputDevices>
<inputDevice address="bus_1000_input"/>
<inputDevice address="Built-In Back Mic"/>
</inputDevices>
</zone>
</zones>
</carAudioConfiguration>
inputDevices
为了支持getInputDeviceForZoneId,CarAudioZones初始化时也会去读取xml里的inputDevice,如官方示例中
<inputDevices>
<inputDevice address="bus_1000_input"/>
<inputDevice address="Built-In Back Mic"/>
</inputDevices>
在解析过程中会放到对应的
class CarAudioZone {
private final int mId;
private final String mName;
private final List<CarVolumeGroup> mVolumeGroups;
//AudioDeviceAttributes也是R上 framework新增的类
private List<AudioDeviceAttributes> mInputAudioDevice;
void addInputAudioDevice(AudioDeviceAttributes device) {
mInputAudioDevice.add(device);
}
List<AudioDeviceAttributes> getInputAudioDevices() {
return mInputAudioDevice;
}
AudioDeviceAttributes是R上新增的设备构建类,可用于R上AudioManager的setDeviceVolumeBehavior和getDeviceVolumeBehavior,之前是使用的deviceType和deviceAddress,现在用AudioDeviceAttributes替代
@SystemApi
public AudioDeviceAttributes(@NonNull AudioDeviceInfo deviceInfo) {
Objects.requireNonNull(deviceInfo);
mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
mType = deviceInfo.getType();
mAddress = deviceInfo.getAddress();
}
//AudioManager
@IntDef({
DEVICE_VOLUME_BEHAVIOR_UNSET,
DEVICE_VOLUME_BEHAVIOR_VARIABLE,
DEVICE_VOLUME_BEHAVIOR_FULL,
DEVICE_VOLUME_BEHAVIOR_FIXED,
DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceVolumeBehaviorState {}
/**
* @hide
* Sets the volume behavior for an audio output device.
* @param device the device to be affected. Currently only supports devices of type
* {@link AudioDeviceInfo#TYPE_HDMI}, {@link AudioDeviceInfo#TYPE_HDMI_ARC},
* {@link AudioDeviceInfo#TYPE_LINE_DIGITAL} and {@link AudioDeviceInfo#TYPE_AUX_LINE}
* @param deviceVolumeBehavior one of the device behaviors
*/
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@DeviceVolumeBehavior int deviceVolumeBehavior) {
}
/**
* @hide
* Returns the volume device behavior for the given audio device
* @param device the audio device
* @return the volume behavior for the device
*/
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public @DeviceVolumeBehaviorState int getDeviceVolumeBehavior(
@NonNull AudioDeviceAttributes device) {
}
audioZoneId
version 2 支持自定义zoneId的值了,当然和isPrimary有所关联,Primary只能设置为0,或者audioZoneId字段省略。而非primary的zone,audioZoneId不能设置为0,且不能缺省。
Objects.requireNonNull(audioZoneIdString, () ->
"Requires " + ATTR_ZONE_ID + " for all audio zones.");
if (isPrimary) {
Preconditions.checkArgument(zoneId == CarAudioManager.PRIMARY_AUDIO_ZONE,
"Primary zone %s must be %d or it can be left empty.",
ATTR_ZONE_ID, CarAudioManager.PRIMARY_AUDIO_ZONE);
} else {
Preconditions.checkArgument(zoneId != CarAudioManager.PRIMARY_AUDIO_ZONE,
"%s can only be %d for primary zone.",
ATTR_ZONE_ID, CarAudioManager.PRIMARY_AUDIO_ZONE);
}
occupantZoneId
occupantZoneId在xml中定义,最后和zoneId形成map关系,提供给CarService使用,逻辑详见2.5
int occupantZoneId = parsePositiveIntAttribute(ATTR_OCCUPANT_ZONE_ID, occupantZoneIdString);
validateOccupantZoneIdIsUnique(occupantZoneId);
mZoneIdToOccupantZoneIdMapping.put(audioZoneId, occupantZoneId);
2.2 CarAudioSetting
carAudioSetting是为了应对多个user的不同设置的存储模块
在CarAudioService构造的时候,创建
public CarAudioService(Context context) {
mCarAudioSettings = new CarAudioSettings(mContext.getContentResolver());
}
里面封装里一些静音和音量的接口
int getStoredVolumeGainIndexForUser(int userId, int zoneId, int id) {
return Settings.System.getIntForUser(mContentResolver,
getVolumeSettingsKeyForGroup(zoneId, id), -1, userId);
}
void storeVolumeGainIndexForUser(int userId, int zoneId, int id, int gainIndex) {
Settings.System.putIntForUser(mContentResolver,
getVolumeSettingsKeyForGroup(zoneId, id),
gainIndex, userId);
}
void storeMasterMute(Boolean masterMuteValue) {
Settings.Global.putInt(mContentResolver,
VOLUME_SETTINGS_KEY_MASTER_MUTE,
masterMuteValue ? 1 : 0);
}
boolean getMasterMute() {
return Settings.Global.getInt(mContentResolver,
VOLUME_SETTINGS_KEY_MASTER_MUTE, 0) != 0;
}
还有一个新增的设置就是是否允许导航出声在通话过程中,由此R上的仲裁矩阵有了动态更新的逻辑,详见2.4 矩阵动态更新
public boolean isRejectNavigationOnCallEnabledInSettings(@UserIdInt int userId) {
return Settings.Secure.getIntForUser(mContentResolver,
CarSettings.Secure.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL,
/*disabled by default*/ 0, userId) == 1;
}
2.3 SystemUsages
R上新增了5中systemUsage,用于 来电语音播报,警报提示音,安全带音,车辆状态音,车辆通知音。
/**
* @hide
* Usage value to use for assistant voice interaction with remote caller on Cell and VoIP calls.
*/
@SystemApi
@RequiresPermission(allOf = {
android.Manifest.permission.MODIFY_PHONE_STATE,
android.Manifest.permission.MODIFY_AUDIO_ROUTING
})
public static final int USAGE_CALL_ASSISTANT = 17;
private static final int SYSTEM_USAGE_OFFSET = 1000;
/**
* @hide
* Usage value to use when the usage is an emergency.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public static final int USAGE_EMERGENCY = SYSTEM_USAGE_OFFSET;
/**
* @hide
* Usage value to use when the usage is a safety sound.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public static final int USAGE_SAFETY = SYSTEM_USAGE_OFFSET + 1;
/**
* @hide
* Usage value to use when the usage is a vehicle status.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public static final int USAGE_VEHICLE_STATUS = SYSTEM_USAGE_OFFSET + 2;
/**
* @hide
* Usage value to use when the usage is an announcement.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public static final int USAGE_ANNOUNCEMENT = SYSTEM_USAGE_OFFSET + 3;
但这些usage没有对三方app开放,属于系统usage。即在builder里setUsage这些系统usage不能使用,只能在setSystemUsage中使用,但是在build构建AudioAttribute时mSystemUsage和mUsage(不允许同时赋值)都会赋值给AudioAttribute的mUsage。
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public @NonNull Builder setSystemUsage(@AttributeSystemUsage int systemUsage) {
if (isSystemUsage(systemUsage)) {
mSystemUsage = systemUsage;
} else {
throw new IllegalArgumentException("Invalid system usage " + systemUsage);
}
return this;
}
@SystemApi
public static boolean isSystemUsage(@AttributeSystemUsage int usage) {
return (usage == USAGE_CALL_ASSISTANT
|| usage == USAGE_EMERGENCY
|| usage == USAGE_SAFETY
|| usage == USAGE_VEHICLE_STATUS
|| usage == USAGE_ANNOUNCEMENT);
}
public AudioAttributes build() {
AudioAttributes aa = new AudioAttributes();
aa.mContentType = mContentType;
if (mUsage == USAGE_INVALID) {
if (mSystemUsage == USAGE_INVALID) {
aa.mUsage = USAGE_UNKNOWN;
} else {
aa.mUsage = mSystemUsage;
}
} else {
if (mSystemUsage == USAGE_INVALID) {
aa.mUsage = mUsage;
} else {
throw new IllegalArgumentException(
"Cannot set both usage and system usage on same builder");
}
}
}
CarAudioService在init时会setSupportSystemUsage,这样其他模块就可以通过getSupportSystemUsages获取当前可用系统usage,如果没有CarAudioService 设置这个系统级的usages,默认framework只会支持USAGE_CALL_ASSISTANT
//CarAudioService
private static final @AttributeSystemUsage int[] SYSTEM_USAGES = new int[] {
AudioAttributes.USAGE_CALL_ASSISTANT,
AudioAttributes.USAGE_EMERGENCY,
AudioAttributes.USAGE_SAFETY,
AudioAttributes.USAGE_VEHICLE_STATUS,
AudioAttributes.USAGE_ANNOUNCEMENT
};
@Override
public void init() {
mAudioManager.setSupportedSystemUsages(SYSTEM_USAGES);
}
//AudioService
private @AttributeSystemUsage int[] mSupportedSystemUsages =
new int[]{AudioAttributes.USAGE_CALL_ASSISTANT};
public void setSupportedSystemUsages(@NonNull @AttributeSystemUsage int[] systemUsages) {
enforceModifyAudioRoutingPermission();
verifySystemUsages(systemUsages);
synchronized (mSupportedSystemUsagesLock) {
AudioSystem.setSupportedSystemUsages(systemUsages);
mSupportedSystemUsages = systemUsages;
}
}
/**
* @see AudioManager#getSupportedSystemUsages()
*/
public @NonNull @AttributeSystemUsage int[] getSupportedSystemUsages() {
enforceModifyAudioRoutingPermission();
synchronized (mSupportedSystemUsagesLock) {
return Arrays.copyOf(mSupportedSystemUsages, mSupportedSystemUsages.length);
}
}
当然设置的systemUsage也会通过AudioSystem设置到AudioPolicy里
//AudioPolicyInterfaceImpl.cpp
const std::vector<audio_usage_t>& SYSTEM_USAGES = {
AUDIO_USAGE_CALL_ASSISTANT,
AUDIO_USAGE_EMERGENCY,
AUDIO_USAGE_SAFETY,
AUDIO_USAGE_VEHICLE_STATUS,
AUDIO_USAGE_ANNOUNCEMENT
};
bool isSystemUsage(audio_usage_t usage) {
return std::find(std::begin(SYSTEM_USAGES), std::end(SYSTEM_USAGES), usage)
!= std::end(SYSTEM_USAGES);
}
bool AudioPolicyService::isSupportedSystemUsage(audio_usage_t usage) {
return std::find(std::begin(mSupportedSystemUsages), std::end(mSupportedSystemUsages), usage)
!= std::end(mSupportedSystemUsages);
}
status_t AudioPolicyService::setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages) {
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
return PERMISSION_DENIED;
}
bool areAllSystemUsages = std::all_of(begin(systemUsages), end(systemUsages),
[](audio_usage_t usage) { return isSystemUsage(usage); });
if (!areAllSystemUsages) {
return BAD_VALUE;
}
mSupportedSystemUsages = systemUsages;
return NO_ERROR;
}
2.4 CarAudioFocus 之 FocusInteraction
新增仲裁
R上新增了一个CarAudioContext用于管理usage和context之间关系,包括新增的4中car的systemUsage,但是USAGE_CALL_ASSISTANT并没有放到这个connext map里,不知道是落下了,还是故意这么设计。安装当前的逻辑(android11-release last in carService 2020 07 19)来说USAGE_CALL_ASSISTANT会归到无效的connext里,实际使用时建议新增一个CallTts的connext来区别普通的tts,如果有该项需求的话。
static {
CONTEXT_TO_USAGES.put(MUSIC,
new int[]{
AudioAttributes.USAGE_UNKNOWN,
AudioAttributes.USAGE_GAME,
AudioAttributes.USAGE_MEDIA
});
CONTEXT_TO_USAGES.put(NAVIGATION,
new int[]{
AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
});
CONTEXT_TO_USAGES.put(VOICE_COMMAND,
new int[]{
AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY,
AudioAttributes.USAGE_ASSISTANT
});
CONTEXT_TO_USAGES.put(CALL_RING,
new int[]{
AudioAttributes.USAGE_NOTIFICATION_RINGTONE
});
CONTEXT_TO_USAGES.put(CALL,
new int[]{
AudioAttributes.USAGE_VOICE_COMMUNICATION,
AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING
});
CONTEXT_TO_USAGES.put(ALARM,
new int[]{
AudioAttributes.USAGE_ALARM
});
CONTEXT_TO_USAGES.put(NOTIFICATION,
new int[]{
AudioAttributes.USAGE_NOTIFICATION,
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
AudioAttributes.USAGE_NOTIFICATION_EVENT
});
CONTEXT_TO_USAGES.put(SYSTEM_SOUND,
new int[]{
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION
});
CONTEXT_TO_USAGES.put(EMERGENCY,
new int[]{
AudioAttributes.USAGE_EMERGENCY
});
CONTEXT_TO_USAGES.put(SAFETY,
new int[]{
AudioAttributes.USAGE_SAFETY
});
CONTEXT_TO_USAGES.put(VEHICLE_STATUS,
new int[]{
AudioAttributes.USAGE_VEHICLE_STATUS
});
CONTEXT_TO_USAGES.put(ANNOUNCEMENT,
new int[]{
AudioAttributes.USAGE_ANNOUNCEMENT
});
CONTEXT_TO_USAGES.put(INVALID,
new int[]{
AudioAttributes.USAGE_VIRTUAL_SOURCE
});
}
矩阵优化
之前在R之前,焦点矩阵是个二维数组,在添加和修改时需要很耐心的一点点的数对应关系,对于修改和扩展都很麻烦,在R上矩阵被单独放在一个小模块里了,便于修改和扩展。
矩阵结果还是之前的三种
// Values for the internal interaction matrix we use to make focus decisions
@VisibleForTesting
static final int INTERACTION_REJECT = 0; // Focus not granted
@VisibleForTesting
static final int INTERACTION_EXCLUSIVE = 1; // Focus granted, others loose focus
@VisibleForTesting
static final int INTERACTION_CONCURRENT = 2; // Focus granted, others keep focus
二维数组更新初始化结构,数组还是二维数组只是便于修改和观察了。
private static final int[][] sInteractionMatrix = {
// Each Row represents CarAudioContext of current focus holder
// Each Column represents CarAudioContext of incoming request (labels along the right)
// Cell value is one of INTERACTION_REJECT, INTERACTION_EXCLUSIVE,
// or INTERACTION_CONCURRENT
// Focus holder: INVALID
{
INTERACTION_REJECT, // INVALID
INTERACTION_REJECT, // MUSIC
INTERACTION_REJECT, // NAVIGATION
INTERACTION_REJECT, // VOICE_COMMAND
INTERACTION_REJECT, // CALL_RING
INTERACTION_REJECT, // CALL
INTERACTION_REJECT, // ALARM
INTERACTION_REJECT, // NOTIFICATION
INTERACTION_REJECT, // SYSTEM_SOUND,
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_EXCLUSIVE, // SAFETY
INTERACTION_REJECT, // VEHICLE_STATUS
INTERACTION_REJECT, // ANNOUNCEMENT
},
// Focus holder: MUSIC
{
INTERACTION_REJECT, // INVALID
INTERACTION_EXCLUSIVE, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_EXCLUSIVE, // VOICE_COMMAND
INTERACTION_EXCLUSIVE, // CALL_RING
INTERACTION_EXCLUSIVE, // CALL
INTERACTION_EXCLUSIVE, // ALARM
INTERACTION_CONCURRENT, // NOTIFICATION
INTERACTION_CONCURRENT, // SYSTEM_SOUND
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_EXCLUSIVE, // ANNOUNCEMENT
},
// Focus holder: NAVIGATION
{
INTERACTION_REJECT, // INVALID
INTERACTION_CONCURRENT, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_EXCLUSIVE, // VOICE_COMMAND
INTERACTION_CONCURRENT, // CALL_RING
INTERACTION_EXCLUSIVE, // CALL
INTERACTION_CONCURRENT, // ALARM
INTERACTION_CONCURRENT, // NOTIFICATION
INTERACTION_CONCURRENT, // SYSTEM_SOUND
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_CONCURRENT, // ANNOUNCEMENT
},
// Focus holder: VOICE_COMMAND
{
INTERACTION_REJECT, // INVALID
INTERACTION_CONCURRENT, // MUSIC
INTERACTION_REJECT, // NAVIGATION
INTERACTION_CONCURRENT, // VOICE_COMMAND
INTERACTION_EXCLUSIVE, // CALL_RING
INTERACTION_EXCLUSIVE, // CALL
INTERACTION_REJECT, // ALARM
INTERACTION_REJECT, // NOTIFICATION
INTERACTION_REJECT, // SYSTEM_SOUND
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_REJECT, // ANNOUNCEMENT
},
// Focus holder: CALL_RING
{
INTERACTION_REJECT, // INVALID
INTERACTION_REJECT, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_CONCURRENT, // VOICE_COMMAND
INTERACTION_CONCURRENT, // CALL_RING
INTERACTION_CONCURRENT, // CALL
INTERACTION_REJECT, // ALARM
INTERACTION_REJECT, // NOTIFICATION
INTERACTION_CONCURRENT, // SYSTEM_SOUND
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_REJECT, // ANNOUNCEMENT
},
// Focus holder: CALL
{
INTERACTION_REJECT, // INVALID
INTERACTION_REJECT, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_REJECT, // VOICE_COMMAND
INTERACTION_CONCURRENT, // CALL_RING
INTERACTION_CONCURRENT, // CALL
INTERACTION_CONCURRENT, // ALARM
INTERACTION_CONCURRENT, // NOTIFICATION
INTERACTION_REJECT, // SYSTEM_SOUND
INTERACTION_CONCURRENT, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_REJECT, // ANNOUNCEMENT
},
// Focus holder: ALARM
{
INTERACTION_REJECT, // INVALID
INTERACTION_CONCURRENT, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_EXCLUSIVE, // VOICE_COMMAND
INTERACTION_EXCLUSIVE, // CALL_RING
INTERACTION_EXCLUSIVE, // CALL
INTERACTION_CONCURRENT, // ALARM
INTERACTION_CONCURRENT, // NOTIFICATION
INTERACTION_CONCURRENT, // SYSTEM_SOUND
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_REJECT, // ANNOUNCEMENT
},
// Focus holder: NOTIFICATION
{
INTERACTION_REJECT, // INVALID
INTERACTION_CONCURRENT, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_EXCLUSIVE, // VOICE_COMMAND
INTERACTION_EXCLUSIVE, // CALL_RING
INTERACTION_EXCLUSIVE, // CALL
INTERACTION_CONCURRENT, // ALARM
INTERACTION_CONCURRENT, // NOTIFICATION
INTERACTION_CONCURRENT, // SYSTEM_SOUND
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_CONCURRENT, // ANNOUNCEMENT
},
// Focus holder: SYSTEM_SOUND
{
INTERACTION_REJECT, // INVALID
INTERACTION_CONCURRENT, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_EXCLUSIVE, // VOICE_COMMAND
INTERACTION_EXCLUSIVE, // CALL_RING
INTERACTION_EXCLUSIVE, // CALL
INTERACTION_CONCURRENT, // ALARM
INTERACTION_CONCURRENT, // NOTIFICATION
INTERACTION_CONCURRENT, // SYSTEM_SOUND
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_CONCURRENT, // ANNOUNCEMENT
},
// Focus holder: EMERGENCY
{
INTERACTION_REJECT, // INVALID
INTERACTION_REJECT, // MUSIC
INTERACTION_REJECT, // NAVIGATION
INTERACTION_REJECT, // VOICE_COMMAND
INTERACTION_REJECT, // CALL_RING
INTERACTION_CONCURRENT, // CALL
INTERACTION_REJECT, // ALARM
INTERACTION_REJECT, // NOTIFICATION
INTERACTION_REJECT, // SYSTEM_SOUND
INTERACTION_CONCURRENT, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_REJECT, // VEHICLE_STATUS
INTERACTION_REJECT, // ANNOUNCEMENT
},
// Focus holder: SAFETY
{
INTERACTION_REJECT, // INVALID
INTERACTION_CONCURRENT, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_CONCURRENT, // VOICE_COMMAND
INTERACTION_CONCURRENT, // CALL_RING
INTERACTION_CONCURRENT, // CALL
INTERACTION_CONCURRENT, // ALARM
INTERACTION_CONCURRENT, // NOTIFICATION
INTERACTION_CONCURRENT, // SYSTEM_SOUND
INTERACTION_CONCURRENT, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_CONCURRENT, // ANNOUNCEMENT
},
// Focus holder: VEHICLE_STATUS
{
INTERACTION_REJECT, // INVALID
INTERACTION_CONCURRENT, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_CONCURRENT, // VOICE_COMMAND
INTERACTION_CONCURRENT, // CALL_RING
INTERACTION_CONCURRENT, // CALL
INTERACTION_CONCURRENT, // ALARM
INTERACTION_CONCURRENT, // NOTIFICATION
INTERACTION_CONCURRENT, // SYSTEM_SOUND
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_CONCURRENT, // ANNOUNCEMENT
},
// Focus holder: ANNOUNCEMENT
{
INTERACTION_REJECT, // INVALID
INTERACTION_EXCLUSIVE, // MUSIC
INTERACTION_CONCURRENT, // NAVIGATION
INTERACTION_EXCLUSIVE, // VOICE_COMMAND
INTERACTION_EXCLUSIVE, // CALL_RING
INTERACTION_EXCLUSIVE, // CALL
INTERACTION_EXCLUSIVE, // ALARM
INTERACTION_CONCURRENT, // NOTIFICATION
INTERACTION_CONCURRENT, // SYSTEM_SOUND
INTERACTION_EXCLUSIVE, // EMERGENCY
INTERACTION_CONCURRENT, // SAFETY
INTERACTION_CONCURRENT, // VEHICLE_STATUS
INTERACTION_EXCLUSIVE, // ANNOUNCEMENT
},
};
矩阵动态更新
在FocusInteraction里通过setRejectNavigationOnCallLocked方法里,可以动态修改二维数组中Call时request NAV的结果,这里建议在适配时顺手也把Nav时request Call时的结果改了,即nav 不会被call打断在开关打开时。
public void setRejectNavigationOnCallLocked(boolean navigationRejectedWithCall) {
mInteractionMatrix[CarAudioContext.CALL][CarAudioContext.NAVIGATION] =
navigationRejectedWithCall ? INTERACTION_REJECT :
sInteractionMatrix[CarAudioContext.CALL][CarAudioContext.NAVIGATION];
}
setRejectNavigationOnCallLocked会在user发生变化或SettingProvider里的字段发生变化时调用
KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL =
"android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL"
void setUserIdForSettings(@UserIdInt int userId) {
synchronized (mLock) {
mUserId = userId;
if (mContentObserver != null) {
mCarAudioFocusSettings.getContentResolver()
.unregisterContentObserver(mContentObserver);
mContentObserver = null;
}
if (mUserId == UserHandle.USER_NULL) {
setRejectNavigationOnCallLocked(false);
return;
}
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (uri.equals(AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL_URI)) {
navigationOnCallSettingChanged();
}
}
};
mCarAudioFocusSettings.getContentResolver()
.registerContentObserver(AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL_URI,
false, mContentObserver, userId);
setRejectNavigationOnCallLocked(isRejectNavigationOnCallEnabledInSettings(mUserId));
}
}
通过这个逻辑可以扩展nav是压低media还是打断media的开关等等一些其他需要动态修改矩阵的需求
2.5 HalAudioFocus
新增的4中car的system usage实际使用者是AudioControl 2.0,之前的chime音、转向音等非android侧声音都是在hal层或hal以下进行仲裁,现在2.0可以拿到CarAudioService里来一起仲裁了。
AudioControlFactory
使用工厂模式里管理AudioControl 1.0和2.0,优先2.0
public static AudioControlWrapper newAudioControl() {
android.hardware.automotive.audiocontrol.V2_0.IAudioControl audioControlV2 =
AudioControlWrapperV2.getService();
if (audioControlV2 != null) {
return new AudioControlWrapperV2(audioControlV2);
}
Log.i(TAG, "IAudioControl@V2.0 not in the manifest");
android.hardware.automotive.audiocontrol.V1_0.IAudioControl audioControlV1 =
AudioControlWrapperV1.getService();
if (audioControlV1 != null) {
Log.w(TAG, "IAudioControl V1.0 is deprecated. Consider upgrading to V2.0");
return new AudioControlWrapperV1(audioControlV1);
}
throw new IllegalStateException("No version of AudioControl HAL in the manifest");
}
2.0和1.0的区别就是2.0增加焦点模块,1.0不支持。
HalAudioFocus机制
在CarAudioService init时会setupAudioFocusListenerLocked
@Override
public void init() {
setupHalAudioFocusListenerLocked();
}
private void setupHalAudioFocusListenerLocked() {
AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked();
//目前只有2.0支持
if (!audioControlWrapper.supportsHalAudioFocus()) {
Log.d(CarLog.TAG_AUDIO, "HalAudioFocus is not supported on this device");
return;
}
mHalAudioFocus = new HalAudioFocus(mAudioManager, mAudioControlWrapper, getAudioZoneIds());
mHalAudioFocus.registerFocusListener();
}
HalAudioFocus是IFocusListener的远端实现端
public final class HalAudioFocus extends IFocusListener.Stub {
@Override
public void requestAudioFocus(@AttributeUsage int usage, int zoneId, int focusGain) {}
@Override
public void abandonAudioFocus(int usage, int zoneId) throws RemoteException {}
}
而客户端是AudioControl2.0,IFocusListener.hal定义hidl的接口。
package android.hardware.automotive.audiocontrol@2.0;
import android.hardware.audio.common@6.0::AudioUsage;
/**
* Callback interface for audio focus listener.
*
* For typical configuration, the listener the car audio service.
*/
interface IFocusListener {
oneway requestAudioFocus(bitfield<AudioUsage> usage, int32_t zoneId,
bitfield<AudioFocusChange> focusGain);
oneway abandonAudioFocus(bitfield<AudioUsage> usage, int32_t zoneId);
};
在HalAudioFocus创建之后会紧接着registerFocusListener,在CarAudioService release时会unregisterFocusListener。r然后通过AudioControlWrapperV2到达AudioControl2.0里。
//HalAudioFocus.java
public void registerFocusListener() {
mAudioControlWrapper.registerFocusListener(this);
}
/**
* Unregisters {@code IFocusListener} from {@code AudioControlWrapper}.
*/
public void unregisterFocusListener() {
mAudioControlWrapper.unregisterFocusListener();
}
//AudioControlWrapperV2
@Override
public void registerFocusListener(IFocusListener focusListener) {
Log.d(TAG, "Registering focus listener on AudioControl HAL");
try {
mCloseHandle = mAudioControlV2.registerFocusListener(focusListener);
} catch (RemoteException e) {
Log.e(TAG, "Failed to register focus listener");
throw new IllegalStateException("IAudioControl#registerFocusListener failed", e);
}
}
@Override
public void unregisterFocusListener() {
if (mCloseHandle != null) {
try {
mCloseHandle.close();
} catch (RemoteException e) {
Log.e(TAG, "Failed to close focus listener", e);
} finally {
mCloseHandle = null;
}
}
}
在AudioControl.cpp里有对IAudioControl.hal里registerFocusListener的实现,讲传递过来的mFocusListener暂存,并创建一个CloseHandle返回到HalAudioControl里,用于在unregisterFocusListener时调用CloseHandle的close方法
Return<sp<ICloseHandle>> AudioControl::registerFocusListener(const sp<IFocusListener>& listener) {
LOG(DEBUG) << "registering focus listener";
sp<ICloseHandle> closeHandle(nullptr);
if (listener) {
mFocusListener = listener;
closeHandle = new CloseHandle([this, listener]() {
if (mFocusListener == listener) {
mFocusListener = nullptr;
}
});
} else {
LOG(ERROR) << "Unexpected nullptr for listener resulting in no-op.";
}
return closeHandle;
}
CloseHandle.cpp里有对ICloseHandle.hal的close的实现
CloseHandle::CloseHandle(Callback callback) : mCallback(callback) {}
CloseHandle::~CloseHandle() {
close();
}
Return<void> CloseHandle::close() {
const auto wasClosed = mIsClosed.exchange(true);
if (wasClosed) return {};
if (mCallback) mCallback();
return {};
}
AudioControl.cpp里对于焦点申请给出了示例,cmdRequestFocus通过mFocusListener->requestAudioFocus来申请焦点
void AudioControl::cmdRequestFocus(int fd, const hidl_vec<hidl_string>& options) {
if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 3)) return;
hidl_bitfield<AudioUsage> usage;
if (!safelyParseInt(options[1], &usage)) {
dprintf(fd, "Non-integer usage provided with request: %s\n", options[1].c_str());
return;
}
int zoneId;
if (!safelyParseInt(options[2], &zoneId)) {
dprintf(fd, "Non-integer zoneId provided with request: %s\n", options[2].c_str());
return;
}
hidl_bitfield<AudioFocusChange> focusGain;
if (!safelyParseInt(options[3], &focusGain)) {
dprintf(fd, "Non-integer focusGain provided with request: %s\n", options[3].c_str());
return;
}
if (mFocusListener == nullptr) {
dprintf(fd, "Unable to request focus - no focus listener registered\n");
return;
}
//通过hidl 到达HalAudioFocus里进行处理
mFocusListener->requestAudioFocus(usage, zoneId, focusGain);
dprintf(fd, "Requested focus for usage %d, zoneId %d, and focusGain %d\n", usage, zoneId,
focusGain);
}
在收到焦点处理结果,(request和abando都是异步的在这收到结果)
Return<void> AudioControl::onAudioFocusChange(hidl_bitfield<AudioUsage> usage, int zoneId,
hidl_bitfield<AudioFocusChange> focusChange) {
LOG(INFO) << "Focus changed: " << static_cast<int>(focusChange) << " for usage "
<< static_cast<int>(usage) << " in zone " << zoneId;
return Void();
}
放弃焦点
void AudioControl::cmdAbandonFocus(int fd, const hidl_vec<hidl_string>& options) {
if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 2)) return;
hidl_bitfield<AudioUsage> usage;
if (!safelyParseInt(options[1], &usage)) {
dprintf(fd, "Non-integer usage provided with abandon: %s\n", options[1].c_str());
return;
}
int zoneId;
if (!safelyParseInt(options[2], &zoneId)) {
dprintf(fd, "Non-integer zoneId provided with abandon: %s\n", options[2].c_str());
return;
}
if (mFocusListener == nullptr) {
dprintf(fd, "Unable to abandon focus - no focus listener registered\n");
return;
}
mFocusListener->abandonAudioFocus(usage, zoneId);
dprintf(fd, "Abandoned focus for usage %d and zoneId %d\n", usage, zoneId);
}
hal AudioFocus处理逻辑
requsetAudioFocus
当AudioControl申请焦点时,通过
mFocusListener->requestAudioFocus(usage, zoneId, focusGain);
申请焦点,HalAudioFocus收到回调requestAudioFocus的回调,会先去mHalFocusRequestsByZoneAndUsage找一下有没有存过仲裁结果(目前逻辑只存可以混音的usage仲裁结果),如果有的话直接通过onAudioFocusChange将mFocusStatus通知到AudioControl里。
@Override
public void requestAudioFocus(@AttributeUsage int usage, int zoneId, int focusGain) {
Preconditions.checkArgument(mHalFocusRequestsByZoneAndUsage.contains(zoneId),
"Invalid zoneId %d provided in requestAudioFocus", zoneId);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Requesting focus gain " + focusGain + " with usage "
+ AudioAttributes.usageToString(usage) + " and zoneId " + zoneId);
}
synchronized (mLock) {
//获取保存过的处理结果
HalAudioFocusRequest currentRequest = mHalFocusRequestsByZoneAndUsage.get(zoneId).get(
usage);
if (currentRequest != null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "A request already exists for zoneId " + zoneId + " and usage "
+ usage);
}
mAudioControlWrapper.onAudioFocusChange(usage, zoneId, currentRequest.mFocusStatus);
} else {
makeAudioFocusRequestLocked(usage, zoneId, focusGain);
}
}
}
如果没有存过话就去重新申请通过makeAudioFocusRequestLocked方法,hal层第一次申请的usage肯定是要创建一个的。
private void makeAudioFocusRequestLocked(@AttributeUsage int usage, int zoneId, int focusGain) {
AudioFocusRequest audioFocusRequest = generateFocusRequestLocked(usage, zoneId, focusGain);
在generateFocusRequestLocked中将hal层传过来的usage zoneId 和请求焦点的类型focusGain,做成一个AudioFocusRequest,并setOnAudioFocusChangeListener回调onAudioFocusChange,即监听该usage被别的usage打断的情况。
private AudioFocusRequest generateFocusRequestLocked(int usage, int zoneId, int focusGain) {
AudioAttributes attributes = generateAudioAttributes(usage, zoneId);
return new AudioFocusRequest.Builder(focusGain)
.setAudioAttributes(attributes)
.setOnAudioFocusChangeListener((int focusChange) -> {
//焦点变化监听 放入AudioFocusRequest中
onAudioFocusChange(usage, zoneId, focusChange);
})
.build();
}
然后通过AudioManger申请焦点,根据外置焦点处理策略,会通过CarAudioFocus进行仲裁处理,默认优先级见2.4 新增仲裁。最后拿到requestResult,如果是成功获取AUDIOFOCUS_REQUEST_GRANTED,就创建一个HalAudioFocusRequest暂存在mHalFocusRequestsByZoneAndUsage里。
其他的结果AUDIOFOCUS_REQUEST_FAILED和AUDIOFOCUS_REQUEST_DELAYED(不支持hal层延时获取)都不存,直接通知hal层AUDIOFOCUS_LOSS。
即mHalFocusRequestsByZoneAndUsage会暂存当前获取到焦点的usage和其audioFocusRequester以及focusGain焦点取得情况
int requestResult = mAudioManager.requestAudioFocus(audioFocusRequest);
int resultingFocusGain = focusGain;
if (requestResult == AUDIOFOCUS_REQUEST_GRANTED) {
HalAudioFocusRequest halAudioFocusRequest = new HalAudioFocusRequest(audioFocusRequest,
focusGain);
mHalFocusRequestsByZoneAndUsage.get(zoneId).append(usage, halAudioFocusRequest);
} else if (requestResult == AUDIOFOCUS_REQUEST_FAILED) {
resultingFocusGain = AUDIOFOCUS_LOSS;
} else if (requestResult == AUDIOFOCUS_REQUEST_DELAYED) {
Log.w(TAG, "Delayed result for request with usage "
+ AudioAttributes.usageToString(usage) + ", zoneId " + zoneId
+ ", and focusGain " + focusGain);
resultingFocusGain = AUDIOFOCUS_LOSS;
}
//通过mAudioControlWrapper 直接将结果通知到hal层
mAudioControlWrapper.onAudioFocusChange(usage, zoneId, resultingFocusGain);
}
onAudioFocusChange
在看一下FocuschangeListener,就是在申请焦点时的焦点变化通知,只会处理焦点还处于暂存状态的usage,已经丢失或者申请不成功的不会处理。
会先取暂存状态如果是AUDIOFOCUS_LOSS,就将暂存的状态移除。最后调用mAudioControlWrapper.onAudioFocusChange通知到Hal层。
private void onAudioFocusChange(int usage, int zoneId, int focusChange) {
synchronized (mLock) {
HalAudioFocusRequest currentRequest = mHalFocusRequestsByZoneAndUsage.get(zoneId).get(
usage);
if (currentRequest != null) {
if (focusChange == AUDIOFOCUS_LOSS) {
mHalFocusRequestsByZoneAndUsage.get(zoneId).remove(usage);
} else {
currentRequest.mFocusStatus = focusChange;
}
mAudioControlWrapper.onAudioFocusChange(usage, zoneId, focusChange);
}
}
}
abandonAudioFocus
放弃焦点逻辑就简单些了,先去暂存的SparseArray里找一下,如果为空(没有找到),说明之前也没有成功不用处理。
如果存在,通过mAudioManager.abandonAudioFocusRequest(存下AudioFocusRequester),走一下abandon流程,最后将结果通知到hal层。
private void abandonAudioFocusLocked(int usage, int zoneId) {
HalAudioFocusRequest currentRequest = mHalFocusRequestsByZoneAndUsage.get(zoneId)
.removeReturnOld(usage);
if (currentRequest == null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "No focus to abandon for usage " + AudioAttributes.usageToString(usage)
+ " and zoneId " + zoneId);
}
return;
}
int result = mAudioManager.abandonAudioFocusRequest(currentRequest.mAudioFocusRequest);
if (result == AUDIOFOCUS_REQUEST_GRANTED) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Abandoned focus for usage " + AudioAttributes.usageToString(usage)
+ "and zoneId " + zoneId);
}
mAudioControlWrapper.onAudioFocusChange(usage, zoneId, AUDIOFOCUS_LOSS);
} else {
Log.w(TAG,
"Failed to abandon focus for usage " + AudioAttributes.usageToString(usage)
+ " and zoneId " + zoneId);
}
}
2.6 CarVolume getSuggestedAudioContext
R上还有一个针对getSuggestedAudioContext小改动,之前策略都是通话状态优先,之后是获取最后一个活跃的playback。显然这个和复杂的需求对不上,方控调节音量或者mute音量也应该有自己的优先级的。R上就增加了这个逻辑。
首先定义出优先级来
private static final int[] AUDIO_CONTEXT_VOLUME_PRIORITY = {
CarAudioContext.NAVIGATION,
CarAudioContext.CALL,
CarAudioContext.MUSIC,
CarAudioContext.ANNOUNCEMENT,
CarAudioContext.VOICE_COMMAND,
CarAudioContext.CALL_RING,
CarAudioContext.SYSTEM_SOUND,
CarAudioContext.SAFETY,
CarAudioContext.ALARM,
CarAudioContext.NOTIFICATION,
CarAudioContext.VEHICLE_STATUS,
CarAudioContext.EMERGENCY,
// CarAudioContext.INVALID is intentionally not prioritized as it is not routed by
// CarAudioService and is not expected to be used.
};
之后通过遍历查找当前获取的playback,并根据优先最后拿到一个suggest的context。
static @AudioContext int getSuggestedAudioContext(
List<AudioPlaybackConfiguration> configurations, @CallState int callState) {
int currentContext = DEFAULT_AUDIO_CONTEXT;
int currentPriority = AUDIO_CONTEXT_VOLUME_PRIORITY.length;
//还是通话状态优先。
if (callState == TelephonyManager.CALL_STATE_RINGING) {
currentContext = CarAudioContext.CALL_RING;
currentPriority = VOLUME_PRIORITY_BY_AUDIO_CONTEXT.get(CarAudioContext.CALL_RING);
} else if (callState == TelephonyManager.CALL_STATE_OFFHOOK) {
currentContext = CarAudioContext.CALL;
currentPriority = VOLUME_PRIORITY_BY_AUDIO_CONTEXT.get(CarAudioContext.CALL);
}
//遍历活跃playback
for (AudioPlaybackConfiguration configuration : configurations) {
if (!configuration.isActive()) {
continue;
}
@AttributeUsage int usage = configuration.getAudioAttributes().getSystemUsage();
@AudioContext int context = CarAudioContext.getContextForUsage(usage);
int priority = VOLUME_PRIORITY_BY_AUDIO_CONTEXT.get(context, CONTEXT_NOT_PRIORITIZED);
if (priority == CONTEXT_NOT_PRIORITIZED) {
Log.w(TAG, "Usage " + AudioAttributes.usageToString(usage) + " mapped to context "
+ CarAudioContext.toString(context) + " which is not prioritized");
continue;
}
if (priority < currentPriority) {
currentContext = context;
currentPriority = priority;
}
}
return currentContext;
}
2.7 CarOccupant模块联动
getzoneIdForDisplay API的取消,就是因为R上新增了这个模块。CarOccupant是用于管理Display和user的模块。
occupantZone占用着Zone类似视图Zone,具体如何使用还待后续调查分析,这里不做深入。
通过CarOccupantZoneServiceTest中可以看到zone的分区(occupantZone),occupantZoneId,occupantType等成员变量的示例,猜测是用于管理仪表屏,中控,后排的模块。
private static final String[] DEFAULT_OCCUPANT_ZONES = {
"occupantZoneId=0,occupantType=DRIVER,seatRow=1,seatSide=driver",
"occupantZoneId=1,occupantType=FRONT_PASSENGER,seatRow=1,seatSide=oppositeDriver",
"occupantZoneId=2,occupantType=REAR_PASSENGER,seatRow=2,seatSide=left",
"occupantZoneId=3,occupantType=REAR_PASSENGER,seatRow=2,seatSide=right"
};
然后也有displayPort和displayType occupantZoneId的map关系,比较复杂,就不细看了。
private static final String[] DEFAULT_OCCUPANT_DISPLAY_MAPPING = {
"displayPort=10,displayType=MAIN,occupantZoneId=0",
"displayPort=11,displayType=INSTRUMENT_CLUSTER,occupantZoneId=0",
"displayPort=12,displayType=MAIN,occupantZoneId=1",
"displayPort=13,displayType=MAIN,occupantZoneId=2",
"displayPort=14,displayType=MAIN,occupantZoneId=3"
};
只看看和CarAudio的联动,CarAudioService在设置动态路由的时候,也setup了OccupantZoneInfo。
private OccupantZoneConfigChangeListener
mOccupantZoneConfigChangeListener = new CarAudioOccupantConfigChangeListener();
/*
mZoneIdToOccupantZoneIdMapping是在解析xml时,获取的audioZoneId和occupantZoneId的关系
mZoneIdToOccupantZoneIdMapping.put(audioZoneId, occupantZoneId);
//CarLocalService 可以更方便的获取本地Car的其他服务模块了
mOccupantZoneService = CarLocalServices.getService(CarOccupantZoneService.class);
Car car = new Car(mContext, (service=) null, (handler= ) null);
mOccupantZoneManager = new CarOccupantZoneManager(car, mOccupantZoneService);
*/
private void setupOccupantZoneInfo() {
CarOccupantZoneService occupantZoneService;
CarOccupantZoneManager occupantZoneManager;
SparseIntArray audioZoneIdToOccupantZoneMapping;
OccupantZoneConfigChangeListener listener;
synchronized (mImplLock) {
audioZoneIdToOccupantZoneMapping = mAudioZoneIdToOccupantZoneIdMapping;
occupantZoneService = mOccupantZoneService;
occupantZoneManager = mOccupantZoneManager;
listener = mOccupantZoneConfigChangeListener;
}
occupantZoneService.setAudioZoneIdsForOccupantZoneIds(audioZoneIdToOccupantZoneMapping);
occupantZoneManager.registerOccupantZoneConfigChangeListener(listener);
}
看下CarAudioOccupantConfigChangeListener,CarOccupant模块联动CarAudio的入口。
private class CarAudioOccupantConfigChangeListener implements OccupantZoneConfigChangeListener {
@Override
public void onOccupantZoneConfigChanged(int flags) {
if (Log.isLoggable(CarLog.TAG_AUDIO, Log.DEBUG)) {
Log.d(CarLog.TAG_AUDIO,
"onOccupantZoneConfigChanged(" + flags + ")");
}
//当用户user或者display发生变化时,对OccupantZone的变化进行处理
if (((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER)
== CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER)
|| ((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY)
== CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY)) {
handleOccupantZoneUserChanged();
}
}
}
handleOccupantZoneUserChanged
处理OccupantZoneUserChanged
private boolean isOccupantZoneMappingAvailable() {
return mAudioZoneIdToOccupantZoneIdMapping.size() > 0;
}
private void handleOccupantZoneUserChanged() {
int driverUserId = mOccupantZoneService.getDriverUserId();
synchronized (mImplLock) {
//如果audioZone和OccupantZoneId没有关联,只重新设置下user的setting
if (!isOccupantZoneMappingAvailable()) {
//No occupant zone to audio zone mapping, re-adjust to settings driver.
for (int index = 0; index < mCarAudioZones.length; index++) {
CarAudioZone zone = mCarAudioZones[index];
zone.updateVolumeGroupsForUser(driverUserId);
//根据user的设置 动态更新矩阵 详见 2.4矩阵动态更新
mFocusHandler.updateUserForZoneId(zone.getId(), driverUserId);
}
return;
}
如果AudioZone和OccupantZone有关联,逻辑就比较复杂了,先要getOccupantZoneIdForDriver
//Method handleOccupantZoneUserChanged 2
int occupantZoneForDriver = getOccupantZoneIdForDriver();
/*
通过CarOccupantZoneManager遍历所有的ZoneId,拿到OCCUPANT_TYPE_DRIVER驾驶员的occupantZone
*/
private int getOccupantZoneIdForDriver() {
List<CarOccupantZoneManager.OccupantZoneInfo> occupantZoneInfos =
mOccupantZoneManager.getAllOccupantZones();
for (CarOccupantZoneManager.OccupantZoneInfo info: occupantZoneInfos) {
if (info.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) {
return info.zoneId;
}
}
return CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID;
}
然后遍历mAudioZoneIdToOccupantZoneIdMapping,update一下audioZoneId,occupantZoneId,driverUserId,occupantZoneForDriver四者的关系。
//Method handleOccupantZoneUserChanged 3
for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) {
int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index);
int occupantZoneId = mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId);
updateUserForOccupantZoneLocked(occupantZoneId, audioZoneId, driverUserId,
occupantZoneForDriver);
}
}
}
updateUserForOccupantZoneLocked
主要更新三点
1.如果userId发生了变化,先移除AudioPolicyMixer里的关于 userId的match。然后如果新的userId和driverId不相等(且新的不为空),设置该zone里的AudioDevice和userId绑定,在AudioPolicyManager里getOutput通过RULE_MATCH_USERID match。
(猜测:由于在设置动态路由的时候,audioMixer的Rule设置的是RULE_MATCH_ATTRIBUTE_USAGE,且CarService里没有RULE_MATCH_USERID 和RULE_EXCLUDE_USERID使用的地方。所以目前没有太大的作用,可能是用于录音设备的match,给出的car_audio_configuration 示例中有些zone里没有对应的InputDevice,以后用到了再看)
//method setupAudioDynamicRoutingForGroup
for (int usage : usages) {
AudioAttributes attributes = buildAttributesWithUsage(usage);
mixingRuleBuilder.addRule(attributes,
AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE);
}
2.如果新的user为空,resetZoneToDefaultUser重新设置动态路由设置和zone的音量设置到默认用户设置
3.如果新的user不为空,会跟新到新的user的动态路由设置和zone的音量设置
private void updateUserForOccupantZoneLocked(int occupantZoneId, int audioZoneId,
@UserIdInt int driverUserId, int occupantZoneForDriver) {
CarAudioZone zone = getAudioZoneForZoneIdLocked(audioZoneId);
int userId = mOccupantZoneService.getUserForOccupant(occupantZoneId);
int prevUserId = getUserIdForZoneLocked(audioZoneId);
Objects.requireNonNull(zone, () ->
"setUserIdDeviceAffinity for userId " + userId
+ " in zone " + audioZoneId + " Failed, invalid zone.");
// user in occupant zone has not changed
if (userId == prevUserId) {
return;
}
// If the user has changed, be sure to remove from current routing
// This would be true even if the new user is UserHandle.USER_NULL,
// as that indicates the user has logged out.
removeUserIdDeviceAffinitiesLocked(prevUserId);
if (userId == UserHandle.USER_NULL) {
// Reset zone back to driver user id
resetZoneToDefaultUser(zone, driverUserId);
return;
}
// Only set user id device affinities for driver when it is the driver's occupant zone
if (userId != driverUserId || occupantZoneId == occupantZoneForDriver) {
setUserIdDeviceAffinitiesLocked(zone, userId, audioZoneId);
mAudioZoneIdToUserIdMapping.put(audioZoneId, userId);
}
zone.updateVolumeGroupsForUser(userId);
mFocusHandler.updateUserForZoneId(audioZoneId, userId);
}