Android R CarAudio行为变更

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);
    }
  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值