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;
相关推荐
小羊没烦恼!1 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
默_笙1 天前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
俊昭喜喜里1 天前
java中的继承和多态的区别
java
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
qq_426003961 天前
启动playwright录制codegen生成自动化测试脚本
python·自动化
譕痕1 天前
JSONObject与JSONArray封装数据格式区别
java·json
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
虎头金猫1 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
胡写代码1 天前
别再前后端各写一套表单校验了
java·后端