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;
相关推荐
小光学长5 小时前
基于ssm的膳食健康管理系统e6whl4q7(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·开发语言·数据库·学习·ssm
java1234_小锋5 小时前
Java高频面试题:Redis到底支不支持事务啊?
java·redis·面试
Dapenson5 小时前
腾讯小龙虾WorkBuddy技能与插件深度解析
python·ai
无心水5 小时前
【常见错误】2、Java并发编程避坑指南:从加锁失效到死锁,10个案例教你正确使用锁
java·开发语言·python
我爱学习好爱好爱5 小时前
Kubernetes 1.29集群上部署Java网站项目
java·容器·kubernetes
青衫码上行5 小时前
【项目开发日记 | Java架构】第一天
java·开发语言·spring cloud
私房菜5 小时前
Selinux 及在Android 的使用详解
android·selinux·sepolicy
DJ斯特拉5 小时前
自定义jar包导入maven&&注册第三方bean
java·maven·jar
困死,根本不会5 小时前
Python 连接 iBeacon 蓝牙设备超详细学习笔记
python·蓝牙服务·ibeacon
AI_56785 小时前
基于智优达平台的Python教学实践:从环境搭建到自动评测
开发语言·前端·人工智能·后端·python