美文网首页
AudioService音量服务

AudioService音量服务

作者: isLJli | 来源:发表于2023-08-27 14:04 被阅读0次

    本篇文章基于Android11源码分析,本篇文章的源码均在frameworks目录下

    1. 调整音量的方式:

    在学习AudioService源码服务之前,我们看一下在应用层如何调节音量的增加、减小、静音、非静音示例:

       private AudioManager audioManager;
    
       @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.checkout_main);
            audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
            //.....
         }
    
        /**
         * 增加音频流为STREAM_MUSIC的音量
         */
        private void increaseVolume(){
            int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
            int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
            Log.d(TAG,"当前音量大小 = "+ currentVolume);
            if (currentVolume < maxVolume){
                audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_RAISE,FLAG_SHOW_UI | FLAG_PLAY_SOUND);
            }
            int currentIncreaseVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
            Log.d(TAG,"增加后的音量大小 = "+ currentIncreaseVolume);
        }
    
        /**
         * 减少音频流为STREAM_MUSIC的音量
         */
        private void decreaseVolume(){
            int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
            Log.d(TAG,"当前音量大小 = "+ currentVolume);
            if (currentVolume > 0 ){
                audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_LOWER,FLAG_SHOW_UI);
            }
            int currentDecreaseVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
    
            Log.d(TAG,"减小后的音量大小 = "+ currentDecreaseVolume);
        }
    
        /**
         * 使频流为STREAM_MUSIC的静音
         */
        private void muteVolume() {
            Log.d(TAG,"muteVolume ");
            previousVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
            audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_MUTE, FLAG_SHOW_UI);
        }
    
        /**
         * 使频流为STREAM_MUSIC的关闭静音
         */
        private void unmuteVolume() {
            Log.d(TAG,"unmuteVolume ");
            if (previousVolume > 0) {
                audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_UNMUTE, FLAG_SHOW_UI);
            }
        }
    
    

    应用层可以通过AudioManager.adjustStreamVolume(int streamType, int direction, int flags)AudioManager.setStreamVolume(int streamType, int index, int flags)其中streamType是音频流,direction是表示操作的描述如增加/减少/静音/非静音。
    其中streamType类型有:

        /**
         * Increase the ringer volume.
         */
        public static final int ADJUST_RAISE = 1;
    
        /**
         * Decrease the ringer volume.
         */
        public static final int ADJUST_LOWER = -1;
    
        /**
         * Maintain the previous ringer volume. This may be useful when needing to
         * show the volume toast without actually modifying the volume.
         */
        public static final int ADJUST_SAME = 0;
    
        /**
         * Mute the volume. Has no effect if the stream is already muted.
         */
        public static final int ADJUST_MUTE = -100;
    
        /**
         * Unmute the volume. Has no effect if the stream is not muted.
         */
        public static final int ADJUST_UNMUTE = 100;
    
        /**
         * Toggle the mute state. If muted the stream will be unmuted. If not muted
         * the stream will be muted.
         */
        public static final int ADJUST_TOGGLE_MUTE = 101;
    

    在Android中有两种方式来控制音量,一种是通过代码来调整音量、一种是通过音量键来控制音量。

    如下我们看下通过音量键是如何调整音量的:
    在按下音量键的时候,会先经过PhoneWindowManager的处理是否拦截:

    // PhoneWindowManager
    
     @Override
        public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
                int policyFlags) {
    
             //......
             else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
                    || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                    || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
                if (mUseTvRouting || mHandleVolumeKeysInWM) {
                    // On TVs or when the configuration is enabled, volume keys never
                    // go to the foreground app.
                     // 调用此方法
                    dispatchDirectAudioEvent(event);
                  }
                    return -1;
                }
      
        private void dispatchDirectAudioEvent(KeyEvent event) {
            //....
            try {
                //  调用AudioService.handleVolumeKey()方法
                getAudioService().handleVolumeKey(event, mUseTvRouting,
                        mContext.getOpPackageName(), TAG);
            } catch (Exception e) {
                Log.e(TAG, "Error dispatching volume key in handleVolumeKey for event:"
                        + event, e);
            }
    
        }
    
    
        // AudioService
    
        public void handleVolumeKey(@NonNull KeyEvent event, boolean isOnTv,
                @NonNull String callingPackage, @NonNull String caller) {
            int keyEventMode = VOL_ADJUST_NORMAL;
            if (isOnTv) {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    keyEventMode = VOL_ADJUST_START;
                } else { // may catch more than ACTION_UP, but will end vol adjustement
                    // the vol key is either released (ACTION_UP), or multiple keys are pressed
                    // (ACTION_MULTIPLE) and we don't know what to do for volume control on CEC, end
                    // the repeated volume adjustement
                    keyEventMode = VOL_ADJUST_END;
                }
            } else if (event.getAction() != KeyEvent.ACTION_DOWN) {
                return;
            }
    
            int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
                    | AudioManager.FLAG_FROM_KEY;
    
    
            // 调用adjustSuggestedStreamVolume()方法
            switch (event.getKeyCode()) {
                case KeyEvent.KEYCODE_VOLUME_UP:
                        adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
                                AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
                                Binder.getCallingUid(), true, keyEventMode);
                    break;
                case KeyEvent.KEYCODE_VOLUME_DOWN:
                        adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
                                AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
                                Binder.getCallingUid(), true, keyEventMode);
                    break;
                case KeyEvent.KEYCODE_VOLUME_MUTE:
                    if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
                        adjustSuggestedStreamVolume(AudioManager.ADJUST_TOGGLE_MUTE,
                                AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
                                Binder.getCallingUid(), true, VOL_ADJUST_NORMAL);
                    }
                    break;
                default:
                    Log.e(TAG, "Invalid key code " + event.getKeyCode() + " sent by " + callingPackage);
                    return; // not needed but added if code gets added below this switch statement
            }
        }
    
        public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
                String callingPackage, String caller) {
            boolean hasModifyAudioSettings =
                    mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
                    == PackageManager.PERMISSION_GRANTED;
            adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
                    caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
        }
    
        private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
                String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
                int keyEventMode) {
           // ....;
           // 最终也是调用到adjustStreamVolume()方法
            adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid,
                    hasModifyAudioSettings, keyEventMode);
        }
    
    

    音量按键在PhoneWindowManager的时候最终会调用dispatchDirectAudioEvent()方法,此方法又调用AudioService.handleVolumeKey()方法,在AudioService最终还是调用adjustStreamVolume()方法进行音量的调整。

    接下来将看adjustStreamVolume()方法源码是如何控制音量的。在讲源码之前,先讲讲音频流和设备。

    2. 音频流和设备:

    在android11中工定义了12中音频流类型,在AudioSystem中有相关定义:

    public class AudioSystem
    {
    
        /**电话 */
        public static final int STREAM_VOICE_CALL = 0;
        /** 系统 */
        public static final int STREAM_SYSTEM = 1;
        /** 响铃和消息 */
        public static final int STREAM_RING = 2;
        /**  音乐 */
        public static final int STREAM_MUSIC = 3;
        /**  闹钟 */
        public static final int STREAM_ALARM = 4;
        /**  通知 */
        public static final int STREAM_NOTIFICATION = 5;
        /**  蓝牙 */
        public static final int STREAM_BLUETOOTH_SCO = 6;
        /**  强制系统声音 */
        @UnsupportedAppUsage
        public static final int STREAM_SYSTEM_ENFORCED = 7;
        /** 双音多频 */
        public static final int STREAM_DTMF = 8;
        /**  语音 */
        public static final int STREAM_TTS = 9;
        /**  辅助功能*/
        public static final int STREAM_ACCESSIBILITY = 10;
        /**  助手*/
        public static final int STREAM_ASSISTANT = 11;
    
        private static final int NUM_STREAM_TYPES = 12;
    
    }
    

    一个或多个音频流共享一个音量:

    public class AudioService extends IAudioService.Stub
            implements AccessibilityManager.TouchExplorationStateChangeListener,
                AccessibilityManager.AccessibilityServicesStateChangeListener {
    
        // 多种音频流对应一种音量,系统音频流,响铃与消息音频流,通知音频流,强制声音音频流,DTMF这五种音频流共用一个音量
        private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
             AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALL
            AudioSystem.STREAM_RING,            // STREAM_SYSTEM
            AudioSystem.STREAM_RING,            // STREAM_RING
            AudioSystem.STREAM_MUSIC,           // STREAM_MUSIC
            AudioSystem.STREAM_ALARM,           // STREAM_ALARM
            AudioSystem.STREAM_RING,            // STREAM_NOTIFICATION
            AudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCO
            AudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCED
            AudioSystem.STREAM_RING,            // STREAM_DTMF
            AudioSystem.STREAM_MUSIC,           // STREAM_TTS
            AudioSystem.STREAM_MUSIC,           // STREAM_ACCESSIBILITY
            AudioSystem.STREAM_MUSIC            // STREAM_ASSISTANT
        };
        // 电视的
        private final int[] STREAM_VOLUME_ALIAS_TELEVISION = new int[] {
            AudioSystem.STREAM_MUSIC,       // STREAM_VOICE_CALL
            AudioSystem.STREAM_MUSIC,       // STREAM_SYSTEM
            AudioSystem.STREAM_RING,       // STREAM_RING
            AudioSystem.STREAM_MUSIC,       // STREAM_MUSIC
            AudioSystem.STREAM_ALARM,       // STREAM_ALAR
            AudioSystem.STREAM_MUSIC,       // STREAM_NOTIFICATION
            AudioSystem.STREAM_BLUETOOTH_SCO,       // STREAM_BLUETOOTH_SCO
            AudioSystem.STREAM_MUSIC,       // STREAM_SYSTEM_ENFORCED
            AudioSystem.STREAM_MUSIC,       // STREAM_DTMF
            AudioSystem.STREAM_MUSIC,       // STREAM_TTS
            AudioSystem.STREAM_MUSIC,       // STREAM_ACCESSIBILITY
            AudioSystem.STREAM_MUSIC        // STREAM_ASSISTANT
        };
        private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {
            AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALL
            AudioSystem.STREAM_RING,            // STREAM_SYSTEM
            AudioSystem.STREAM_RING,            // STREAM_RING
            AudioSystem.STREAM_MUSIC,           // STREAM_MUSIC
            AudioSystem.STREAM_ALARM,           // STREAM_ALARM
            AudioSystem.STREAM_RING,            // STREAM_NOTIFICATION
            AudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCO
            AudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCED
            AudioSystem.STREAM_RING,            // STREAM_DTMF
            AudioSystem.STREAM_MUSIC,           // STREAM_TTS
            AudioSystem.STREAM_MUSIC,           // STREAM_ACCESSIBILITY
            AudioSystem.STREAM_MUSIC            // STREAM_ASSISTANT
        };
        // 存储多种音频流对应一个音量
        protected static int[] mStreamVolumeAlias;
    
    
    }
    

    STREAM_VOLUME_ALIAS_VOICE代表的是具有语音功能的设备如手机。
    STREAM_VOLUME_ALIAS_TELEVISION代表电视或机顶盒的设备。
    STREAM_VOLUME_ALIAS_DEFAULT代表其他的设备。
    STREAM_VOLUME_ALIAS_VOICE中统音频流,响铃与消息音频流,通知音频流,强制声音音频流,DTMF音频流就和响铃与消息音频流共享音量,他们的音量是一致的,每当STREAM_RING音量发生改变时,其他的与之共享音量的音频流也要对应的发生改变。

    其中mStreamVolumeAlias数组变量就是就是设置为对应的变量。

    • 最大最小音量
      每种音频流都有自己的最大最小的音量值,定义在AudioService的两个数组变量中:
    public class AudioService extends IAudioService.Stub
            implements AccessibilityManager.TouchExplorationStateChangeListener,
                AccessibilityManager.AccessibilityServicesStateChangeListener {
    
    
     //每种流的最小音量
        protected static int[] MAX_STREAM_VOLUME = new int[] {
            5,  // STREAM_VOICE_CALL
            7,  // STREAM_SYSTEM
            7,  // STREAM_RING
            15, // STREAM_MUSIC
            7,  // STREAM_ALARM
            7,  // STREAM_NOTIFICATION
            15, // STREAM_BLUETOOTH_SCO
            7,  // STREAM_SYSTEM_ENFORCED
            15, // STREAM_DTMF
            15, // STREAM_TTS
            15, // STREAM_ACCESSIBILITY
            15  // STREAM_ASSISTANT
        };
    
        /** Minimum volume index values for audio streams */
        //每种流的最小音量
        protected static int[] MIN_STREAM_VOLUME = new int[] {
            1,  // STREAM_VOICE_CALL
            0,  // STREAM_SYSTEM
            0,  // STREAM_RING
            0,  // STREAM_MUSIC
            1,  // STREAM_ALARM
            0,  // STREAM_NOTIFICATION
            0,  // STREAM_BLUETOOTH_SCO
            0,  // STREAM_SYSTEM_ENFORCED
            0,  // STREAM_DTMF
            0,  // STREAM_TTS
            1,  // STREAM_ACCESSIBILITY
            0   // STREAM_ASSISTANT
        };
    
    }
    
    • 输出设备
      音频的输出设备有很多种,一种音频流有多种输出设备,而且每种设备对应的音量不一定相同,比如插入耳机时是另一种音量大小。
    AUDIO_DEVICE_OUT_EARPIECE: 听筒,用于通话时将声音传递到耳朵。
    
    AUDIO_DEVICE_OUT_WIRED_HEADSET: 有线耳机/耳麦,通过耳机插孔连接。
    
    AUDIO_DEVICE_OUT_WIRED_HEADPHONE: 有线普通耳机,通过耳机插孔连接。
    
    AUDIO_DEVICE_OUT_BLUETOOTH_SCO: 单声道蓝牙耳机,用于通话。
    
    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: 蓝牙电话耳机,用于通话。
    
    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: 蓝牙立体声耳机。
    
    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: 蓝牙立体声耳机。
    
    AUDIO_DEVICE_OUT_USB_HEADSET: USB 耳机/耳麦。
    
    AUDIO_DEVICE_OUT_HEARING_AID: 助听器,用于助听设备。
    
    AUDIO_DEVICE_OUT_LINE: 线级输出。
    
    AUDIO_DEVICE_OUT_AUX_DIGITAL: 数字辅助输出,例如 HDMI。
    
    AUDIO_DEVICE_OUT_USB_DEVICE: USB 设备。
    
    AUDIO_DEVICE_OUT_SPEAKER: 扬声器,外部设备,通常是手机的内置扬声器。
    
    AUDIO_DEVICE_OUT_SPEAKER_SAFE: 安全扬声器,与扬声器类似。
    
    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: 车载蓝牙免提设备。
    
    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: 蓝牙 A2DP 扬声器。
    
    AUDIO_DEVICE_OUT_USB_ACCESSORY: USB 附件。
    
    AUDIO_DEVICE_OUT_REMOTE_SUBMIX: 远程混音输出。
    
    • VolumeStreamState
      在AudioService中如何去管理这个复杂的音频流和输出设备间的音量大小关系呢?其通过一个内部类VolumeStreamState,每个音频流都有一个VolumeStreamState类存储着当前音频流的相关信息:
    public class AudioService extends IAudioService.Stub
            implements AccessibilityManager.TouchExplorationStateChangeListener,
                AccessibilityManager.AccessibilityServicesStateChangeListener {
    
        // 所有音频流对应的VolumeStreamState
        private VolumeStreamState[] mStreamStates;
    
        private void createStreamStates() {
            // 音频流的总数
            int numStreamTypes = AudioSystem.getNumStreamTypes();
            // 赋值mStreamStates数组大小为音频流的总数
            VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
    
            for (int i = 0; i < numStreamTypes; i++) {
                // 为每个音频流创建VolumeStreamState实例
                streams[i] =
                        new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
            }
    
        }
    
        private class VolumeStreamState {
            private final int mStreamType; // 指示哪个音频流
            private int mIndexMin; // 对应的最小音量值
            // min index when user doesn't have permission to change audio settings
            private int mIndexMinNoPerm;
            private int mIndexMax; // 对应的最大音量值
    
            private boolean mIsMuted;
            private boolean mIsMutedInternally;
            private String mVolumeIndexSettingName;
            private int mObservedDevices;
    
            // 保存每种设备的音量值
            private final SparseIntArray mIndexMap = new SparseIntArray(8) {
                @Override
                public void put(int key, int value) {
                    super.put(key, value);
                    record("put", key, value);
                }
                @Override
                public void setValueAt(int index, int value) {
                    super.setValueAt(index, value);
                    record("setValueAt", keyAt(index), value);
                }
            };
    
    }
    

    1. adjustStreamVolume()

    按键或代码设置都可以通过adjustStreamVolume()去调整音量大小:

       // 设置音量API之一
        public void adjustStreamVolume(int streamType, int direction, int flags,
                String callingPackage) {
            if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
                Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
                        + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
                return;
            }
            final boolean hasModifyAudioSettings =
                    mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
                            == PackageManager.PERMISSION_GRANTED;
            sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
                    direction/*val1*/, flags/*val2*/, callingPackage));
            adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
                    Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
        }
    
        protected void adjustStreamVolume(int streamType, int direction, int flags,
                String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
                int keyEventMode) {
           
            ensureValidDirection(direction);
            ensureValidStreamType(streamType);
    
            boolean isMuteAdjust = isMuteAdjust(direction);
            //....
            if (adjustVolume
                    && (direction != AudioManager.ADJUST_SAME) && (keyEventMode != VOL_ADJUST_END)) {
                mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);
                if (isMuteAdjust) {
                    
                    for (int stream = 0; stream < mStreamStates.length; stream++) {
                        if (streamTypeAlias == mStreamVolumeAlias[stream]) {
                            if (!(readCameraSoundForced()
                                        && (mStreamStates[stream].getStreamType()
                                            == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
                                // 调用VolumeStreamState.mute()去设置静音/非静音
                                mStreamStates[stream].mute(state);
                            }
                        }
                    }
                } else if (!isInVolumePassthrough()
                        && (streamState.adjustIndex(direction * step, device, caller,
                                hasModifyAudioSettings)
                                || streamState.mIsMuted)) {
                    // 去设置音量
                    sendMsg(mAudioHandler,
                            MSG_SET_DEVICE_VOLUME,
                            SENDMSG_QUEUE,
                            device,
                            0,
                            streamState,
                            0);
                }
    
                int newIndex = mStreamStates[streamType].getIndex(device);
             
            // 更新UI
             sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);
         
        }
    

    adjustStreamVolume方法中代码中有很多,这里挑出主要的步骤:

    • 如果是传入的direction是ADJUST_MUTE、ADJUST_UNMUTE静音的属性,则会调用VolumeStreamState.mute()方法,其相关执行后面在讲,大致流程都差不多。
    • 通过VolumeStreamState.adjustIndex设置Index的大小
    • 发送AudioHandler. MSG_SET_DEVICE_VOLUME消息去执行音量的设置
    • 通过sendVolumeUpdate()方法回调给SystemUI中的VolumeUI去展示对应的UI通知。

    VolumeStreamState.adjustIndex

         // AudioService
        public void adjustStreamVolume(int streamType, int direction, int flags,
                String callingPackage) {
    
        //....
               // 音频流是音乐且是固定音量设备
            // 计算step为音量增加或较少的单位
            if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
                flags |= AudioManager.FLAG_FIXED_VOLUME;
    
                if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
                        mSafeMediaVolumeDevices.contains(device)) {
                    step = safeMediaVolumeIndex(device);
                } else {
                    step = streamState.getMaxIndex();
                }
                if (aliasIndex != 0) {
                    aliasIndex = step;
                }
            } else {
                // 一般走这里,内部单元值,即增加或减少一个音量的单元值是多少 
                step = rescaleStep(10, streamType, streamTypeAlias);
            }
        //...
         // direction 就是1,-1,100,-100,刚好*单元值后就是index要设置的选项
         else if ( (streamState.adjustIndex(direction * step, device, caller, hasModifyAudioSettings)) 
       }
    
    
        private int rescaleStep(int step, int srcStream, int dstStream) {
            // 源的最大值-最小值
            int srcRange = getIndexRange(srcStream);
            // 最大值-最小值
            int dstRange = getIndexRange(dstStream);
            // 错误
            if (srcRange == 0) {
                Log.e(TAG, "rescaleStep : index range should not be zero");
                return 0;
            }
            // 转换后的布值
            return ((step * dstRange + srcRange / 2) / srcRange);
        }
    
    }
    
    // AudioManager
        public static final int ADJUST_RAISE = 1;
        public static final int ADJUST_LOWER = -1;
        public static final int ADJUST_MUTE = -100;
        public static final int ADJUST_UNMUTE = 100;
    
    // VolumeStreamState 
        private class VolumeStreamState {
       
            public boolean adjustIndex(int deltaIndex, int device, String caller,
                    boolean hasModifyAudioSettings) {
                return setIndex(getIndex(device) + deltaIndex, device, caller,
                        hasModifyAudioSettings);
            }
    
            public boolean setIndex(int index, int device, String caller,
                    boolean hasModifyAudioSettings) {
                boolean changed;
                int oldIndex;
                synchronized (mSettingsLock) {
                    synchronized (VolumeStreamState.class) {
                        oldIndex = getIndex(device);
                        // 检查有效值,不超过最小最大值
                        index = getValidIndex(index, hasModifyAudioSettings);
                        if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
                            index = mIndexMax;
                        }
                        // 将对应的设备设置到对应的新值
                        mIndexMap.put(device, index);
    
                        changed = oldIndex != index;
                        final boolean isCurrentDevice = (device == getDeviceForStream(mStreamType));
                        // 同时也要更新与之共享音量的其他音频流的VolumeStreamState中的对应device的新值
                        final int numStreamTypes = AudioSystem.getNumStreamTypes();
                        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                            final VolumeStreamState aliasStreamState = mStreamStates[streamType];
    
                            if (streamType != mStreamType &&
                                    mStreamVolumeAlias[streamType] == mStreamType &&
                                    (changed || !aliasStreamState.hasIndexForDevice(device))) {
                                final int scaledIndex = rescaleIndex(index, mStreamType, streamType);
                                aliasStreamState.setIndex(scaledIndex, device, caller,
                                        hasModifyAudioSettings);
                                if (isCurrentDevice) {
                                    aliasStreamState.setIndex(scaledIndex,
                                            getDeviceForStream(streamType), caller,
                                            hasModifyAudioSettings);
                                }
                            }
                        }
                        // Mirror changes in SPEAKER ringtone volume on SCO when
                        if (changed && mStreamType == AudioSystem.STREAM_RING
                                && device == AudioSystem.DEVICE_OUT_SPEAKER) {
                            for (int i = 0; i < mIndexMap.size(); i++) {
                                int otherDevice = mIndexMap.keyAt(i);
                                if (AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(otherDevice)) {
                                    mIndexMap.put(otherDevice, index);
                                }
                            }
                        }
                    }
                }
                if (changed) {
                  // 发送广播
                }
                return changed;
            }
    
        }
    

    在设置Index中主要操作是

    • 通过rescaleStep()方法计算出单元值step
    • 通过direction * step可以得到新值的大小,在AudioManager中direction在+/-时被巧妙的设置了1/-1,然后传给VolumeStreamState.adjustIndex
    • adjustIndex后又调用setIndex()方法,首先对新值进行有效转换,不超过最大最小值,然后通过mIndexMap存储到对应的设备和新值,并且也更新与之共享音量的音频流的对应的mIndexMap值,并且发送广播。

    AudioHandler. MSG_SET_DEVICE_VOLUME

    private class AudioHandler extends Handler {
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
    
                    case MSG_SET_DEVICE_VOLUME:
                        // 执行setDeviceVolume方法
                        setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
                        break;
           }
    }
    
     void setDeviceVolume(VolumeStreamState streamState, int device) {
    
            synchronized (VolumeStreamState.class) {
                //调用VolumeStreamState.applyDeviceVolume_syncVSS方法
                streamState.applyDeviceVolume_syncVSS(device);
    
                // 其他与之共享音量的音频流的也随之设置之对应设备的音量大小
                int numStreamTypes = AudioSystem.getNumStreamTypes();
                for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                    if (streamType != streamState.mStreamType &&
                            mStreamVolumeAlias[streamType] == streamState.mStreamType) {
                        int streamDevice = getDeviceForStream(streamType);
                        if ((device != streamDevice) && mAvrcpAbsVolSupported
                                && AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) {
                            mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
                        }
                        mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
                    }
                }
            }
         
    
        }
    
    //  VolumeStreamState 
    
           void applyDeviceVolume_syncVSS(int device) {
                int index;
                if (isFullyMuted()) {
                    // 如果整个音频流都被静音,index 被设置为 0
                    index = 0;
                } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                        && mAvrcpAbsVolSupported) {
                    //A2DP 设备集合并且支持绝对音量控制
                    index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
                } else if (isFullVolumeDevice(device)) {
                    //如果设备是全音量设备(如扬声器),index 被设置为最大音量索引的 1/10
                    index = (mIndexMax + 5)/10;
                } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
                   //如果设备是听力辅助设备,同样将 index 设置为最大音量索引的 1/10
                    index = (mIndexMax + 5)/10;
                } else {
                    //其他,当前值+5/10
                    index = (getIndex(device) + 5)/10;
                }
    
                setStreamVolumeIndex(index, device);
            }
    
            private void setStreamVolumeIndex(int index, int device) {
                if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0
                        && !isFullyMuted()) {
                    index = 1;
                }
                 //通过AudioSystem.setStreamVolumeIndexAS底层设置音量值
                AudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
            }
    
    

    MSG_SET_DEVICE_VOLUME消息主要是真正的设置音量值的新值,主要操作有:

    • MSG_SET_DEVICE_VOLUME消息执行setDeviceVolume方法,setDeviceVolume方法给音频流和与之共享的音频流调用VSS的applyDeviceVolume_syncVSS进行音量值设置
    • applyDeviceVolume_syncVSS将新值index进行转换,并通过setStreamVolumeIndex方法交给AudioSystem.setStreamVolumeIndexAS进行真正音量值设置。

    sendVolumeUpdate

        protected void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags, int device)
        {
            streamType = mStreamVolumeAlias[streamType];
    
            if (streamType == AudioSystem.STREAM_MUSIC) {
                flags = updateFlagsForTvPlatform(flags);
                // The volume bar ui shows depends on whether the device is in passthrough mode.
                if (isInVolumePassthrough()) {
                    Slog.d(TAG, "no volume bar");
                    flags &= ~AudioManager.FLAG_SHOW_UI;
                }
            }
            mVolumeController.postVolumeChanged(streamType, flags);
        }
    
    
        public static class VolumeController {
            private static final String TAG = "VolumeController";
    
            private IVolumeController mController;
    
            // 设置IVolumeController
            public void setController(IVolumeController controller) {
                mController = controller;
                mVisible = false;
            }
    
            public void postVolumeChanged(int streamType, int flags) {
                if (mController == null)
                    return;
                try {
                    // 调用IVolumeController的volumeChanged方法
                    mController.volumeChanged(streamType, flags);
                } catch (RemoteException e) {
                    Log.w(TAG, "Error calling volumeChanged", e);
                }
            }
    
        }
    
    

    通过IVolumeController.volumeChanged进行回调,给SystemUi那边进行UI的更新,那么这个IVolumeController是谁设置的呢?
    在AudioService中有个setVolumeController()方法给设置VolumeController.setController

    
      private final VolumeController mVolumeController = new VolumeController();
    
       @Override
        public void setVolumeController(final IVolumeController controller) {
           //...
            mVolumeController.setController(controller);
            if (DEBUG_VOL) Log.d(TAG, "Volume controller: " + mVolumeController);
        }
    

    setVolumeController()这个方法又是谁设置的呢?这就需要看一下VolumeUI的启动流程:

    public class VolumeUI extends SystemUI {
    
     private VolumeDialogComponent mVolumeComponent;
    
        // 启动会调用start()
        @Override
        public void start() {
            //....
            setDefaultVolumeController();
        }
    
        private void setDefaultVolumeController() {
            DndTile.setVisible(mContext, true);
            if (LOGD) Log.d(TAG, "Registering default volume controller");
            // 调用VolumeDialogComponent.register()
            mVolumeComponent.register();
        }
    
    }
    
    // VolumeDialogComponent
    public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable,
            VolumeDialogControllerImpl.UserActivityListener{
    
        private final VolumeDialogControllerImpl mController;
        private VolumeDialog mDialog;
    
        // UI显示的Dialog是VolumeDialogImpl
        protected VolumeDialog createDefault() {
            VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
            impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
            impl.setAutomute(true);
            impl.setSilentMode(false);
            return impl;
        }
    
        @Override
        public void register() {
            // 调用VolumeDialogControllerImpl.register()
            mController.register();
        }
    
    }
    
    // VolumeDialogControllerImpl
    public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
    
        protected final VC mVolumeController = new VC();
    
        public void register() {
            // 设置VolumeController
            setVolumeController();
        }
    
        protected void setVolumeController() {
            try {
                // 调用AudioService.setVolumeController将VC设置为VolumeController
                mAudio.setVolumeController(mVolumeController);
            } catch (SecurityException e) {
                Log.w(TAG, "Unable to set the volume controller", e);
                return;
            }
        }
    
      private final class VC extends IVolumeController.Stub {
        
            @Override
            public void volumeChanged(int streamType, int flags) throws RemoteException {
                if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
                        + " " + Util.audioManagerFlagsToString(flags));
                if (mDestroyed) return;
                // 发送VOLUME_CHANGED消息
                mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
            }
      }
    
    }
    
    

    VolumeUI开始的时候会调用start()方法,最后通过VolumeDialogControllerImplVC设置VolumeController。之后AudioService于VC通信,而VC则通过与VolumeDialogImpl进行UI的展示。

    AdjustStreamVolume小结

    主要分为三大步骤:

    1. 通过adjustIndex()更新index的新值
    2. 发送MSG_SET_DEVICE_VOLUME消息,通过AudioSystem设置新的音量值
    3. sendVolumeUpdate发送通知UI消息

    2. setStreamVolume

    代码调整音量时,可以通过setStreamVolume方法进行调节,所以它也是设置音量的入口之一。

     // 设置音量API之一
        private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
                String caller, int uid, boolean hasModifyAudioSettings) {
    
            ensureValidStreamType(streamType);  // 这里先判断一下流类型这个参数的有效性
            // 对应共享音量的音频流
            int streamTypeAlias = mStreamVolumeAlias[streamType];
            // 对应音频流的VolumeStreamState
            VolumeStreamState streamState = mStreamStates[streamTypeAlias];
    
            // 根据音频流确定输出的设备
            final int device = getDeviceForStream(streamType);
            int oldIndex;
    
             //...权限判断
    
            synchronized (mSafeMediaVolumeStateLock) {
                // reset any pending volume command
                mPendingVolumeCommand = null;
    
                // 获取流当前的音量
                oldIndex = streamState.getIndex(device);
                // 将原流类型下的音量值映射到目标流类型下的音量值
                // 因为不同流类型的音量值刻度不一样,所以需要进行这个转换
                index = rescaleIndex(index * 10, streamType, streamTypeAlias);
                //...
              // 调用setStreamVolumeInt()
             onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings);
             // 获取设置的结果
            index = mStreamStates[streamType].getIndex(device);
            // 广播通知
            sendVolumeUpdate(streamType, oldIndex, index, flags, device);
        }
    
    
        private void onSetStreamVolume(int streamType, int index, int flags, int device,
                String caller, boolean hasModifyAudioSettings) {
            final int stream = mStreamVolumeAlias[streamType];
            // 调用setStreamVolumeInt()
            setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);
            //....
        }
    
        private void setStreamVolumeInt(int streamType,
                                        int index,
                                        int device,
                                        boolean force,
                                        String caller, boolean hasModifyAudioSettings) {
            // 获取保存音量信息的VolumeStreamState对象
            VolumeStreamState streamState = mStreamStates[streamType];
    
            // 为什么还要判断streamState.setIndex的返回值呢?
            // 因为如果音量值在setIndex之后并没有发生变化,比如说达到了最大值,就不需要继续后面的操作了
            // 或者force参数为true的话
            if (streamState.setIndex(index, device, caller, hasModifyAudioSettings) || force) {
    
                // 通过MSG_SET_DEVICE_VOLUME去设置音量值
                sendMsg(mAudioHandler,
                        MSG_SET_DEVICE_VOLUME,
                        SENDMSG_QUEUE,
                        device,
                        0,
                        streamState,
                        0);
            }
        }
     
    

    setStreamVolume方法的执行流程跟adjustStreamVolume方法大同小异,都是先更新index值,然后通过MSG_SET_DEVICE_VOLUME消息去设置音量值,最后通过sendVolumeUpdate去回调通知Ui。

    总结

    本篇文章主要介绍了AudioService调整音量大小的源码,主要在java层,真正的设置是通过的AudioSystem去调用底层的native去完成。VolumeStreamState存储着音频流的所有关系,包括最大最小值,device对应的index值。调整音量有两个API:adjustStreamVolumesetStreamVolume它们的执行流程都大概是先更新对应device的index值,然后设置音量值,最后通知UI更新。

    参考文章:
    https://juejin.cn/post/6983977417173893128
    https://wizardforcel.gitbooks.io/deepin-android-vol3/content/2.html

    相关文章

      网友评论

          本文标题:AudioService音量服务

          本文链接:https://www.haomeiwen.com/subject/ofximdtx.html