ARC1.4-通过安卓设备遥控器调节功放设备的音量与静音状态

通过遥控器调节功放设备的音量和静音状态,以及通过安卓TV设备音量条UI设置静音状态的接口都是 adjustStreamVolume方法,该方法的代码在2-2章节中有写,接下来主要分析一下改方法中与ARC功放设备音量设置相关的代码。

protected void adjustStreamVolume(int streamType, int direction, int flags,  
        String callingPackage, String caller, int uid, int pid, String attributionTag,  
        boolean hasModifyAudioSettings, int keyEventMode) {  

	......

	//获取音频流的别名
    int streamTypeAlias = mStreamVolumeAlias[streamType];  
	//通过别名获取streamState对象
    VolumeStreamState streamState = mStreamStates[streamTypeAlias];  
	//传入音量流的别名`streamTypeAlias`,以确定应该将音量调整应用于哪个音频设备

> [!代码1]
>     final int device = getDeviceForStream(streamTypeAlias);  

    //获取音频设备当前的音量值
    int aliasIndex = streamState.getIndex(device);  
    //表示默认情况下音量需要被调整
	boolean adjustVolume = true;

	......
	//当接入HDMI矩阵的时候走的这里
	if (isAbsoluteVolumeDevice(device)  
        && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {  
    AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);  
	    if (info.mHandlesVolumeAdjustment) {  
        dispatchAbsoluteVolumeAdjusted(streamType, info, oldIndex, direction,  
                keyEventMode);  
        return;    
        }  
	}
	......

	if (adjustVolume && (direction != AudioManager.ADJUST_SAME)  
        && (keyEventMode != AudioDeviceVolumeManager.ADJUST_MODE_END)) {  
    mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);  
	//判断声音是否需要调整//判断是否是满音量设备

> [!代码2]
>     if (isMuteAdjust && !mFullVolumeDevices.contains(device)) {  

        boolean state;  
        if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {  
            state = !streamState.mIsMuted;  
        } else {  
            state = direction == AudioManager.ADJUST_MUTE;  
        }  
        for (int stream = 0; stream < mStreamStates.length; stream++) {  
            if (streamTypeAlias == mStreamVolumeAlias[stream]) {  
                if (!(readCameraSoundForced()  
                            && (mStreamStates[stream].getStreamType()  
                                == AudioSystem.STREAM_SYSTEM_ENFORCED))) { 
                    //这里调用静音方法 

> [!代码3]
>                     mStreamStates[stream].mute(state);  

                }  
            }  
        }  
    } else if ((direction == AudioManager.ADJUST_RAISE) &&  
            !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {  
        Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);  
        mVolumeController.postDisplaySafeVolumeWarning(flags);  

> [!代码4]
>     } else if (!isFullVolumeDevice(device)  

		    //判断是否是满音量设备//调整音量值
            && (streamState.adjustIndex(direction * step, device, caller,  
                    hasModifyAudioSettings)  
                    || streamState.mIsMuted)) {  
        // Post message to set system volume (it in turn will post a  
        // message to persist).        if (streamState.mIsMuted) {  
            // Unmute the stream if it was previously muted  
            if (direction == AudioManager.ADJUST_RAISE) {  
                // unmute immediately for volume up  
                streamState.mute(false);  
            } else if (direction == AudioManager.ADJUST_LOWER) {  
                if (mIsSingleVolume) {  
                    sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,  
                            streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);  
                }  
            }  
        }  
        //发送MSG_SET_DEVICE_VOLUME消息更新设备的声音

> [!代码5]
>         sendMsg(mAudioHandler,  
>                 MSG_SET_DEVICE_VOLUME,  
>                 SENDMSG_QUEUE,  
>                 device,  
>                 0,  
>                 streamState,  
>                 0);  

    }  
  
    ......

	}

	......
	if (adjustVolume) {  
    synchronized (mHdmiClientLock) {  
        if (mHdmiManager != null) {  
            // At most one of mHdmiPlaybackClient and mHdmiTvClient should be non-null  
            HdmiClient fullVolumeHdmiClient = mHdmiPlaybackClient;  
            if (mHdmiTvClient != null) {  
                fullVolumeHdmiClient = mHdmiTvClient;  
            }  
			//判断是否是ARC设备//判断音频流别名是否是STREAM_MUSIC
            if (fullVolumeHdmiClient != null  
                    && mHdmiCecVolumeControlEnabled  
                    && streamTypeAlias == AudioSystem.STREAM_MUSIC  
                    // vol change on a full volume device  
                    && isFullVolumeDevice(device)) {  
                int keyCode = KeyEvent.KEYCODE_UNKNOWN;  
                //通过音量调节的方向获取键值
                switch (direction) {  
                    case AudioManager.ADJUST_RAISE:  
                        keyCode = KeyEvent.KEYCODE_VOLUME_UP;  
                        break;                    
                    case AudioManager.ADJUST_LOWER:  
                        keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;  
                        break;                    
                    case AudioManager.ADJUST_TOGGLE_MUTE:  
                    case AudioManager.ADJUST_MUTE:  
                    case AudioManager.ADJUST_UNMUTE:  
                        // Many CEC devices only support toggle mute. Therefore, we send the  
                        // same keycode for all three mute options.                        
                        keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;  
                        break;                    
                    default:  
                        break;  
                }  
                if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {  
                    final long ident = Binder.clearCallingIdentity();  
                    try {  
	                    //发送键值到音频功放设备,功放设备接收到键值调整自身的音量
                        switch (keyEventMode) {  
                            case AudioDeviceVolumeManager.ADJUST_MODE_NORMAL:  

> [!代码6]
>                                 fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);  

                                fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);  
                                break;                            
                            case AudioDeviceVolumeManager.ADJUST_MODE_START:  
                                fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);  
                                break;                            
                            case AudioDeviceVolumeManager.ADJUST_MODE_END:  
                                fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);  
                                break;                            
                            default:  
                                Log.e(TAG, "Invalid keyEventMode " + keyEventMode);  
                        }  
                    } finally {  
                        Binder.restoreCallingIdentity(ident);  
                    }  
                }  
            }  
  
            if (streamTypeAlias == AudioSystem.STREAM_MUSIC  
                    && (oldIndex != newIndex || isMuteAdjust)) {  
                maybeSendSystemAudioStatusCommand(isMuteAdjust);  
            }  
        }  
    }  
}  
//通知系统和用户界面音量更新
sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);
}
  
   
代码1
@VisibleForTesting  
public int getDeviceForStream(int stream) {  
    return selectOneAudioDevice(getDeviceSetForStream(stream));  
}  
  
/*  
 * Must match native apm_extract_one_audio_device() used in getDeviceForVolume() * or the wrong device volume may be adjusted. */
 private int selectOneAudioDevice(Set<Integer> deviceSet) {  
    if (deviceSet.isEmpty()) {  
        return AudioSystem.DEVICE_NONE;  
    } else if (deviceSet.size() == 1) {  
        return deviceSet.iterator().next();  
    } else {  
        // Multiple device selection is either:  
        //  - speaker + one other device: give priority to speaker in this case.        
        //  - one A2DP device + another device: happens with duplicated output. In this case        
        // retain the device on the A2DP output as the other must not correspond to an active        
        // selection if not the speaker.        
        //  - HDMI-CEC system audio mode only output: give priority to available item in order.  
        if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER)) {  
            return AudioSystem.DEVICE_OUT_SPEAKER;  
        } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER_SAFE)) {  
            // Note: DEVICE_OUT_SPEAKER_SAFE not present in getDeviceSetForStreamDirect  
            return AudioSystem.DEVICE_OUT_SPEAKER_SAFE;  
        } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_HDMI_ARC)) {  
            return AudioSystem.DEVICE_OUT_HDMI_ARC;  
        } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_HDMI_EARC)) {  
            return AudioSystem.DEVICE_OUT_HDMI_EARC;  
        } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_AUX_LINE)) {  
            return AudioSystem.DEVICE_OUT_AUX_LINE;  
        } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPDIF)) {  
            return AudioSystem.DEVICE_OUT_SPDIF;  
        } else {  
            // At this point, deviceSet should contain exactly one A2DP device;  
            // regardless, return the first A2DP device in numeric order.            
            // If there is no A2DP device, this falls through to log an error.            
            for (int deviceType : deviceSet) {  
                if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(deviceType)) {  
                    return deviceType;  
                }  
            }  
        }  
    }  
    Log.w(TAG, "selectOneAudioDevice returning DEVICE_NONE from invalid device combination "  
            + AudioSystem.deviceSetToString(deviceSet));  
    return AudioSystem.DEVICE_NONE;  
}
代码2

这里可以看到mFullVolumeDevices集合里面包含了DEVICE_OUT_HDMI_ARC和DEVICE_OUT_HDMI_EARC
这俩设备的ID
public static final int DEVICE_OUT_HDMI_ARC = 0x40000; (转为十进制:262144)
public static final int DEVICE_OUT_HDMI_EARC = 0x40001; (转为十进制:262145)
机器内置的扬声器设备ID:public static final int DEVICE_OUT_SPEAKER = 0x2;

// Devices for which the volume is always max, no volume panel  
Set<Integer> mFullVolumeDevices = new HashSet<>(Arrays.asList(  
        AudioSystem.DEVICE_OUT_HDMI_ARC,  
        AudioSystem.DEVICE_OUT_HDMI_EARC  
));
代码3

//更新了静音状态并发送MSG_SET_ALL_VOLUMES消息将所有设备的音量设置为0

public boolean mute(boolean state) {  
    boolean changed = false;  
    synchronized (VolumeStreamState.class) {  
        if (state != mIsMuted) {  
            changed = true;  
            mIsMuted = state;  
  
            // Set the new mute volume. This propagates the values to  
            // the audio system, otherwise the volume won't be changed            
            // at the lower level.            
            sendMsg(mAudioHandler,  
                    MSG_SET_ALL_VOLUMES,  
                    SENDMSG_QUEUE,  
                    0,  
                    0,  
                    this, 0);  
        }  
    }  
    if (changed) {  
        // Stream mute changed, fire the intent.  
        Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);  
        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);  
        intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);  
        sendBroadcastToAll(intent);  
    }  
    return changed;  
}

case MSG_SET_ALL_VOLUMES:  
    setAllVolumes((VolumeStreamState) msg.obj);  
    break;

//调用了mStreamStates[streamType].applyAllVolumes()方法,这个方法在章节2-3中有分析

private void setAllVolumes(VolumeStreamState streamState) {  
  
    // Apply volume  
    streamState.applyAllVolumes();  
  
    // Apply change to all streams using this one as alias  
    int numStreamTypes = AudioSystem.getNumStreamTypes();  
    for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {  
        if (streamType != streamState.mStreamType &&  
                mStreamVolumeAlias[streamType] == streamState.mStreamType) {  
            mStreamStates[streamType].applyAllVolumes();  
        }  
    }  
}

//判断是否静音接口

public boolean isStreamMute(int streamType) {  
    if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {  
        streamType = getActiveStreamType(streamType);  
    }  
    synchronized (VolumeStreamState.class) {  
        ensureValidStreamType(streamType);  
        return mStreamStates[streamType].mIsMuted;  
    }  
}
代码4

//这里方法里面返回的是mFullVolumeDevices.contains(deviceType

private boolean isFullVolumeDevice(int deviceType) {  
    if (deviceType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX  
            && mRecordMonitor.isLegacyRemoteSubmixActive()) {  
        return false;  
    }  
    return mFullVolumeDevices.contains(deviceType);  
}
代码5

//接收MSG_SET_DEVICE_VOLUME消息更新设备音量

case MSG_SET_DEVICE_VOLUME:  
    setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);  
    break;
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {  
  
    synchronized (VolumeStreamState.class) {  
        // Apply volume  

> [!代码5-1]
>         streamState.applyDeviceVolume_syncVSS(device);  

  
        // Apply change to all streams using this one as alias  
        int numStreamTypes = AudioSystem.getNumStreamTypes();  
        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {  
            if (streamType != streamState.mStreamType &&  
                    mStreamVolumeAlias[streamType] == streamState.mStreamType) {  
                // Make sure volume is also maxed out on A2DP device for aliased stream  
                // that may have a different device selected                
                int streamDevice = getDeviceForStream(streamType);  
                if ((device != streamDevice)  
                        && (isAbsoluteVolumeDevice(device)  
                            || isA2dpAbsoluteVolumeDevice(device)  
                            || AudioSystem.isLeAudioDeviceType(device))) {  
                    mStreamStates[streamType].applyDeviceVolume_syncVSS(device);  
                }  
                mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);  
            }  
        }  
    }  
    // Post a persist volume msg  

> [!代码5-2]
>     sendMsg(mAudioHandler,  
>             MSG_PERSIST_VOLUME,  
>             SENDMSG_QUEUE,  
>             device,  
>             0,  
>             streamState,  
>             PERSIST_DELAY);  

}
代码5-1

//这个方法最后也是调用的setStreamVolumeIndex方法去设置设备的音量
//可以通过命令dumpsys audio查看设备音量相关信息

/*package*/ void applyDeviceVolume_syncVSS(int device) {  
    int index;  
    if (isFullyMuted()) {  
        index = 0;  
    } else if (isAbsoluteVolumeDevice(device)  
            || isA2dpAbsoluteVolumeDevice(device)  
            || AudioSystem.isLeAudioDeviceType(device)) {  
        index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);  
    } else if (isFullVolumeDevice(device)) {  
        index = (mIndexMax + 5)/10;  
    } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {  
        index = (mIndexMax + 5)/10;  
    } else {  
        index = (getIndex(device) + 5)/10;  
    }  
    if (isTablet() && isSyncAjustVolumeDevice(device) && mStreamType == AudioSystem.STREAM_MUSIC) {  
        for (Integer item : mSyncAjustVolumeDevices) {  
            index = (getIndex(item) + 5)/10;  
            setStreamVolumeIndex(index, item);  
        }  
    } else {  
        setStreamVolumeIndex(index, device);  
    }  
}
代码5-2

//MSG_PERSIST_VOLUME消息通过handleMessage进入persistVolume,最终调用System.putIntForUser将用户设置的内容设置到Settings.system中,设备声音初始化的时候通过这个属性去获取音量的初始值

可以通过指令:settings list system 查看该值
或者通过命令: settings get system volume_music_speaker 单独获取某个设备的值

case MSG_PERSIST_VOLUME:  
    persistVolume((VolumeStreamState) msg.obj, msg.arg1);  
    break;

private void persistVolume(VolumeStreamState streamState, int device) {  
    if (mUseFixedVolume) {  
        return;  
    }  
    if (mIsSingleVolume && (streamState.mStreamType != AudioSystem.STREAM_MUSIC)) {  
        return;  
    }  
    if (streamState.hasValidSettingsName()) {  
        mSettings.putSystemIntForUser(mContentResolver,  
                streamState.getSettingNameForDevice(device),  
                (streamState.getIndex(device) + 5)/ 10,  
                UserHandle.USER_CURRENT);  
        if(isTablet())  
            streamState.restoreSyncDevicesIndex();  
    }  
}
代码6

//调用的HdmiControlService.java的sendVolumeKeyEvent方法发送键值更新功放设备的音量,这部分的代码不追下去了

@Override  
public void sendVolumeKeyEvent(  
    final int deviceType, final int keyCode, final boolean isPressed) {  
    initBinderCall();  
    runOnServiceThread(new Runnable() {  
        @Override  
        public void run() {  
            if (mCecController == null) {  
                Slog.w(TAG, "CEC controller not available to send volume key event.");  
                return;            }  
            HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(deviceType);  
            if (localDevice == null) {  
                Slog.w(TAG, "Local device " + deviceType  
                      + " not available to send volume key event.");  
                return;            }  
            localDevice.sendVolumeKeyEvent(keyCode, isPressed);  
        }  
    });  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值