AudioService音量服务

本篇文章基于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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容