Android Audio 广播之 ACTION_AUDIO_BECOMING_NOISY

一、判断广播是否发出

  1. checkSendBecomingNoisyIntentInt

用于判断在音频设备状态改变时(特别是断开连接时),是否需要向媒体应用发送 ACTION_AUDIO_BECOMING_NOISY广播。

主要职责是评估断开连接的设备是否是当前活跃的音频输出设备,以及当前系统状态(是否有音乐播放、是否处于通话中等),

并最终决定是否需要发送广播,如果需要,则返回一个延迟时间(通常用于等待蓝牙设备的短暂重连机会)。

java 复制代码
// 创建一个 MediaMetrics 对象用于记录该方法的执行指标。
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
        + "checkSendBecomingNoisyIntentInt")
        // 设置属性:正在评估的设备名称。
        .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(device))
        // 设置属性:设备的连接状态(已连接或已断开)。
        .set(MediaMetrics.Property.STATE,
                state == AudioService.CONNECTION_STATE_CONNECTED
                        ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED);

// 情况1:如果设备状态不是"断开连接",则无需发送广播,直接返回。
if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
    Log.i(TAG, "not sending NOISY: state=" + state);
    mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // 记录延迟时间为0,然后返回。
    return 0;
}

// 情况2:如果断开连接的设备类型不在预设的"需要发送NOISY广播的设备集合"中,也无需发送,直接返回。
if (!BECOMING_NOISY_INTENT_DEVICES_SET.contains(device)) {
    Log.i(TAG, "not sending NOISY: device=0x" + Integer.toHexString(device)
            + " not in set " + BECOMING_NOISY_INTENT_DEVICES_SET);
    mmi.set(MediaMetrics.Property.DELAY_MS, 0).record();
    return 0;
}

// 延迟时间变量,默认0。
int delay = 0;
// 创建一个集合,用于存储当前所有已连接且应触发NOISY广播的**输出设备**类型。
Set<Integer> devices = new HashSet<>();
// 遍历所有已连接的设备。
for (DeviceInfo di : mConnectedDevices.values()) {
    // 筛选条件:1. 是输出设备;2. 设备类型在上述集合中。
    if (!AudioSystem.isInputDevice(di.mDeviceType)
            && BECOMING_NOISY_INTENT_DEVICES_SET.contains(di.mDeviceType)) {
        // 满足条件的设备类型加入集合。
        devices.add(di.mDeviceType);
        Log.i(TAG, "NOISY: adding 0x" + Integer.toHexString(di.mDeviceType));
    }
}

// 如果传入的 `musicDevice` 参数表示"未指定",则查询系统当前实际用于音乐播放(STREAM_MUSIC)的设备。
if (musicDevice == AudioSystem.DEVICE_NONE) {
    musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC);
    Log.i(TAG, "NOISY: musicDevice changing from NONE to 0x"
            + Integer.toHexString(musicDevice));
}

// **核心逻辑判断**:决定是否发送广播。
// 准备判断所需的状态变量:
// 1. 系统是否正在通信中(例如,通话或语音识别)。
final boolean inCommunication = mDeviceBroker.isInCommunication();
// 2. 判断断开连接的设备是否是集合 `devices` 中唯一或最后一个符合NOISY触发条件的音频设备类型。
final boolean singleAudioDeviceType = AudioSystem.isSingleAudioDeviceType(devices, device);
// 3. 是否有动态音频策略正在影响媒体路由。
final boolean hasMediaDynamicPolicy = mDeviceBroker.hasMediaDynamicPolicy();

// 判断条件全部满足时,才需要发送广播:
// 1. 条件A:断开连接的设备是当前音乐播放设备 **或者** 系统正处于通信模式。
// 2. 条件B:该断开连接的设备是唯一或最后一个应触发NOISY的音频设备类型。
// 3. 条件C:没有动态音频策略影响媒体路由。
// 4. 条件D:当前音乐播放设备不是远程子混音设备(`DEVICE_OUT_REMOTE_SUBMIX`,通常用于屏幕录制或投屏)。
if (((device == musicDevice) || inCommunication)
        && singleAudioDeviceType
        && !hasMediaDynamicPolicy
        && (musicDevice != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX)) {
    
    // **进一步检查**:即使满足以上条件,如果系统当前既没有播放音乐,也没有持有音频焦用的用户,则不应视为"即将嘈杂",无需打断远程播放的应用。
    if (!mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0 /*not looking in past*/)
            && !mDeviceBroker.hasAudioFocusUsers()) {
        AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                "dropping ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG));
        mmi.set(MediaMetrics.Property.DELAY_MS, 0).record();
        return 0;
    }
    // **发送广播**:所有条件满足,提交一个延迟发送"即将嘈杂"广播的任务。
    mDeviceBroker.postBroadcastBecomingNoisy();
    // 设置返回的延迟时间(通常是预定义的常量,例如 `AudioService.BECOMING_NOISY_DELAY_MS`)。
    delay = AudioService.BECOMING_NOISY_DELAY_MS;
} else {
    // 如果以上条件不满足,则记录详细的日志,说明不发送广播的原因。
    Log.i(TAG, "not sending NOISY: device:0x" + Integer.toHexString(device)
            + " musicDevice:0x" + Integer.toHexString(musicDevice)
            + " inComm:" + inCommunication
            + " mediaPolicy:" + hasMediaDynamicPolicy
            + " singleDevice:" + singleAudioDeviceType);
}

// 最终记录本次调用计算的延迟时间指标,并返回该值。
mmi.set(MediaMetrics.Property.DELAY_MS, delay).record();
return delay;
相关推荐
invicinble2 小时前
这里对java的知识体系做一个全域的介绍
java·开发语言·python
wbs_scy2 小时前
【Linux 线程进阶】进程 vs 线程资源划分 + 线程控制全详解
java·开发语言
ss2732 小时前
食谱推荐系统功能测试如何写?
java·数据库·spring boot·功能测试
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题】【Java基础篇】第15题:JDK1.7中HashMap扩容为什么会发生死循环?如何解决
java·开发语言·数据结构·后端·面试·哈希算法
m0_674294643 小时前
如何编写SQL存储过程性能对比_记录执行时间评估优化效果
jvm·数据库·python
try2find3 小时前
打印ascii码报错问题
java·linux·前端
014-code3 小时前
CompletableFuture 实战模板(超时、组合、异常链处理)
java·数据库
运气好好的3 小时前
怎样开启phpMyAdmin的操作审计日志_记录每条执行的SQL
jvm·数据库·python
Nicander3 小时前
多数据源下@transcation事务踩坑
java·后端
それども4 小时前
DELETE 和 TRUNCATE TABLE区别
java·数据库·mysql