android13-settings[sound&vibration]

258 阅读18分钟

1.简介

主要看下Ring&notification volume ,支持语音通话的时候显示的是这个,不支持语音通话的话显示的是另外一个Notification volume(如果显示的话,在Alarm volume下),具体见小节2.1布局

1.1.截图

image.png

1.2.top_level_settings.xml

    <com.android.settings.widget.HomepagePreference
        android:fragment="com.android.settings.notification.SoundSettings"
        android:icon="@drawable/ic_volume_up_24dp"
        android:key="top_level_sound"
        android:order="-90"
        android:title="@string/sound_settings"
        android:summary="@string/sound_dashboard_summary"
        settings:highlightableMenuKey="@string/menu_key_sound"/>

2.SoundSettings

public class SoundSettings extends DashboardFragment implements OnActivityResultListener {

    protected int getPreferenceScreenResId() {
        return R.xml.sound_settings;
    }

2.1.sound_settings.xml

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:title="@string/sound_settings"
    android:key="sound_settings"
    settings:keywords="@string/keywords_sounds">

    <!-- Remote volume group -->
    <PreferenceCategory
        android:key="remote_media_group"
        android:title=""
        android:order="-185"
        settings:allowDividerBelow="true"
        settings:controller="com.android.settings.notification.RemoteVolumeGroupController">
    </PreferenceCategory>

    <!-- Media volume -->
    <com.android.settings.notification.VolumeSeekBarPreference
        android:key="media_volume"
        android:icon="@drawable/ic_media_stream"
        android:title="@string/media_volume_option_title"
        android:order="-180"
        settings:controller="com.android.settings.notification.MediaVolumePreferenceController"/>

    <!-- Media output switcher -->
    <Preference
        android:key="media_output"
        android:title="@string/media_output_title"
        android:dialogTitle="@string/media_output_title"
        android:order="-175"
        settings:searchable="false"
        settings:controller="com.android.settings.sound.MediaOutputPreferenceController"/>

    <!-- Call volume -->
    <com.android.settings.notification.VolumeSeekBarPreference
        android:key="call_volume"
        android:icon="@drawable/ic_local_phone_24_lib"
        android:title="@string/call_volume_option_title"
        android:order="-170"
        settings:controller="com.android.settings.notification.CallVolumePreferenceController"/>

    <!-- Hands free profile output switcher -->
    <ListPreference
        android:key="take_call_on_output"
        android:title="@string/take_call_on_title"
        android:dialogTitle="@string/take_call_on_title"
        android:order="-165"
        settings:searchable="false"
        settings:controller="com.android.settings.sound.HandsFreeProfileOutputPreferenceController"/>

    <!-- Ring & Notification volume -->
    <com.android.settings.notification.VolumeSeekBarPreference
        android:key="ring_volume"
        android:icon="@drawable/ic_notifications"
        android:title="@string/ring_volume_option_title"
        android:order="-160"
        settings:controller="com.android.settings.notification.RingVolumePreferenceController"/>


    <!-- Alarm volume -->
    <com.android.settings.notification.VolumeSeekBarPreference
        android:key="alarm_volume"
        android:icon="@*android:drawable/ic_audio_alarm"
        android:title="@string/alarm_volume_option_title"
        android:order="-150"
        settings:controller="com.android.settings.notification.AlarmVolumePreferenceController"/>

    <!-- Notification volume -->
    <com.android.settings.notification.VolumeSeekBarPreference
        android:key="notification_volume"
        android:icon="@drawable/ic_notifications"
        android:title="@string/notification_volume_option_title"
        android:order="-140"
        settings:controller="com.android.settings.notification.NotificationVolumePreferenceController"/>

    <!-- DO Not Disturb 勿扰模式 -->
    <!-- Interruptions -->
    <com.android.settingslib.RestrictedPreference
        android:key="zen_mode"
        android:title="@string/zen_mode_settings_title"
        android:fragment="com.android.settings.notification.zen.ZenModeSettings"
        android:order="-130"
        settings:useAdminDisabledSummary="true"
        settings:keywords="@string/keywords_sounds_and_notifications_interruptions"
        settings:controller="com.android.settings.notification.zen.ZenModePreferenceController"/>

    <!-- Phone ringtone ,手机铃声-->
    <com.android.settings.DefaultRingtonePreference
        android:key="phone_ringtone"
        android:title="@string/ringtone_title"
        android:dialogTitle="@string/ringtone_title"
        android:summary="@string/summary_placeholder"
        android:ringtoneType="ringtone"
        android:order="-120"
        settings:keywords="@string/sound_settings"/>

    <!-- Live Caption -110 and Now Playing -105-->

    <!-- Spatial audio -->
    <Preference
        android:key="spatial_audio_summary"
        android:title="@string/spatial_audio_title"
        android:fragment="com.android.settings.notification.SpatialAudioSettings"
        android:order="-107"
        settings:controller="com.android.settings.notification.SpatialAudioParentPreferenceController"/>

    <Preference
        android:key="media_controls_summary"
        android:title="@string/media_controls_title"
        android:fragment="com.android.settings.sound.MediaControlsSettings"
        android:order="-100"
        settings:controller="com.android.settings.sound.MediaControlsParentPreferenceController"
        settings:keywords="@string/keywords_media_controls"/>

    <!-- Also vibration -->
    <Preference
        android:fragment="com.android.settings.accessibility.VibrationSettings"
        android:key="vibration_and_haptics"
        android:title="@string/accessibility_vibration_settings_title"
        android:order="-90"
        settings:controller="com.android.settings.accessibility.VibrationPreferenceController"
        settings:keywords="@string/keywords_vibration"/>

    <com.android.settingslib.PrimarySwitchPreference
        android:key="gesture_prevent_ringing_sound"
        android:title="@string/gesture_prevent_ringing_sound_title"
        android:order="-80"
        android:fragment="com.android.settings.gestures.PreventRingingGestureSettings"
        settings:controller="com.android.settings.gestures.PreventRingingParentPreferenceController"/>

    <!-- Default notification ringtone -->
    <com.android.settings.DefaultRingtonePreference
        android:key="notification_ringtone"
        android:title="@string/notification_ringtone_title"
        android:dialogTitle="@string/notification_ringtone_title"
        android:summary="@string/summary_placeholder"
        android:ringtoneType="notification"
        android:order="-70"/>

    <!-- Default alarm ringtone -->
    <com.android.settings.DefaultRingtonePreference
        android:key="alarm_ringtone"
        android:title="@string/alarm_ringtone_title"
        android:dialogTitle="@string/alarm_ringtone_title"
        android:summary="@string/summary_placeholder"
        android:persistent="false"
        android:ringtoneType="alarm"
        android:order="-60"/>

    <!-- Dial pad tones -->
    <SwitchPreference
        android:key="dial_pad_tones"
        android:title="@string/dial_pad_tones_title"
        android:order="-50"/>

    <!-- Screen locking sounds -->
    <SwitchPreference
        android:key="screen_locking_sounds"
        android:title="@string/screen_locking_sounds_title"
        android:order="-45"/>

    <!-- Charging sounds -->
    <SwitchPreference
        android:key="charging_sounds"
        android:title="@string/charging_sounds_title"
        android:order="-40"/>

    <!-- Docking sounds -->
    <SwitchPreference
        android:key="docking_sounds"
        android:title="@string/docking_sounds_title"
        android:order="-35"/>

    <!-- Touch sounds -->
    <SwitchPreference
        android:key="touch_sounds"
        android:title="@string/touch_sounds_title"
        android:order="-30"/>

    <!-- Show vibrate icon in status bar -->
    <SwitchPreference
        android:key="vibrate_icon"
        android:title="@string/vibrate_icon_title"
        android:order="-27"/>

    <!-- Dock speaker plays -->
    <DropDownPreference
        android:key="dock_audio_media"
        android:title="@string/dock_audio_media_title"
        android:summary="%s"
        android:order="-20"/>

    <!-- Boot sounds -->
    <SwitchPreference
        android:key="boot_sounds"
        android:title="@string/boot_sounds_title"
        android:order="-15"/>

    <!-- Emergency tone -->
    <DropDownPreference
        android:key="emergency_tone"
        android:title="@string/emergency_tone_title"
        android:summary="%s"
        android:order="-10"/>
        <!-- Call connected tones -->

        <SwitchPreference
          android:key="call_connected_tones"
          android:title="@string/call_connected_tones_title" />


    <Preference
        android:key="sound_work_settings"
        android:title="@string/sound_work_settings"
        android:fragment="com.android.settings.notification.SoundWorkSettings"
        android:order="100"
        settings:controller="com.android.settings.notification.WorkSoundsPreferenceController"/>
</PreferenceScreen>

2.2.onAttach

    public void onAttach(Context context) {
        super.onAttach(context);
        ArrayList<VolumeSeekBarPreferenceController> volumeControllers = new ArrayList<>();
        volumeControllers.add(use(AlarmVolumePreferenceController.class));
        volumeControllers.add(use(MediaVolumePreferenceController.class));
        //暂时研究这两个,2选一,正常来讲只会显示其中一个
        volumeControllers.add(use(RingVolumePreferenceController.class));
        volumeControllers.add(use(NotificationVolumePreferenceController.class));
        
        volumeControllers.add(use(CallVolumePreferenceController.class));

        use(HandsFreeProfileOutputPreferenceController.class).setCallback(listPreference ->
                onPreferenceDataChanged(listPreference));
        mHfpOutputControllerKey =
                use(HandsFreeProfileOutputPreferenceController.class).getPreferenceKey();

        for (VolumeSeekBarPreferenceController controller : volumeControllers) {
        //回调见2.5
            controller.setCallback(mVolumeCallback);
            getSettingsLifecycle().addObserver(controller);
        }
    }

2.3.buildPreferenceControllers

    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
            SoundSettings fragment, Lifecycle lifecycle) {
        final List<AbstractPreferenceController> controllers = new ArrayList<>();

        // Volumes are added via xml

        // === Phone & notification ringtone ===
        controllers.add(new PhoneRingtonePreferenceController(context));
        controllers.add(new AlarmRingtonePreferenceController(context));
        controllers.add(new NotificationRingtonePreferenceController(context));

        // === Other Sound Settings ===
        final DialPadTonePreferenceController dialPadTonePreferenceController =
                new DialPadTonePreferenceController(context, fragment, lifecycle);
        final ScreenLockSoundPreferenceController screenLockSoundPreferenceController =
                new ScreenLockSoundPreferenceController(context, fragment, lifecycle);
        final ChargingSoundPreferenceController chargingSoundPreferenceController =
                new ChargingSoundPreferenceController(context, fragment, lifecycle);
        final DockingSoundPreferenceController dockingSoundPreferenceController =
                new DockingSoundPreferenceController(context, fragment, lifecycle);
        final TouchSoundPreferenceController touchSoundPreferenceController =
                new TouchSoundPreferenceController(context, fragment, lifecycle);
        final DockAudioMediaPreferenceController dockAudioMediaPreferenceController =
                new DockAudioMediaPreferenceController(context, fragment, lifecycle);
        final BootSoundPreferenceController bootSoundPreferenceController =
                new BootSoundPreferenceController(context);
        final EmergencyTonePreferenceController emergencyTonePreferenceController =
                new EmergencyTonePreferenceController(context, fragment, lifecycle);
        final VibrateIconPreferenceController vibrateIconPreferenceController =
                new VibrateIconPreferenceController(context, fragment, lifecycle);

        controllers.add(dialPadTonePreferenceController);
        controllers.add(screenLockSoundPreferenceController);
        controllers.add(chargingSoundPreferenceController);
        controllers.add(dockingSoundPreferenceController);
        controllers.add(touchSoundPreferenceController);
        controllers.add(vibrateIconPreferenceController);
        controllers.add(dockAudioMediaPreferenceController);
        controllers.add(bootSoundPreferenceController);
        controllers.add(emergencyTonePreferenceController);
        controllers.add(new PreferenceCategoryController(context,
                "other_sounds_and_vibrations_category").setChildren(
                Arrays.asList(dialPadTonePreferenceController,
                        screenLockSoundPreferenceController,
                        chargingSoundPreferenceController,
                        dockingSoundPreferenceController,
                        touchSoundPreferenceController,
                        vibrateIconPreferenceController,
                        dockAudioMediaPreferenceController,
                        bootSoundPreferenceController,
                        emergencyTonePreferenceController)));

        return controllers;
    }

2.4.onPreferenceTreeClick

RingtonePreference的点击事件单独处理,点击弹框选择铃声

image.png

    public boolean onPreferenceTreeClick(Preference preference) {
        if (preference instanceof RingtonePreference) {
            writePreferenceClickMetric(preference);
            mRequestPreference = (RingtonePreference) preference;
            mRequestPreference.onPrepareRingtonePickerIntent(mRequestPreference.getIntent());
            getActivity().startActivityForResultAsUser(
                    mRequestPreference.getIntent(),
                    REQUEST_CODE,
                    null,
                    UserHandle.of(mRequestPreference.getUserId()));
            return true;
        }
        return super.onPreferenceTreeClick(preference);
    }

2.5.mVolumeCallback

主要作用就是延迟2秒停止播放示例声音

    final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();

3.RingVolumePreferenceController

public class RingVolumePreferenceController extends VolumeSeekBarPreferenceController {

>getAudioStream

    public int getAudioStream() {
        return AudioManager.STREAM_RING; //2
    }

3.1.构造方法

    public RingVolumePreferenceController(Context context, String key) {
        super(context, key);
        mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
        if (mVibrator != null && !mVibrator.hasVibrator()) {
            mVibrator = null;
        }

        mRingAliasNotif = isRingAliasNotification();
        if (mRingAliasNotif) {
            mTitleId = R.string.ring_volume_option_title;

            mNormalIconId = R.drawable.ic_notifications;
            mSilentIconId = R.drawable.ic_notifications_off_24dp;
        } else {
            mTitleId = R.string.separate_ring_volume_option_title;

            mNormalIconId = R.drawable.ic_ring_volume;
            mSilentIconId = R.drawable.ic_ring_volume_off;
        }
        // todo: set a distinct vibrate icon for ring vs notification
        mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;

        updateRingerMode();
    }

>isRingAliasNotification

通知和铃声是否合并统一控制

    boolean isRingAliasNotification() {
        return mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_alias_ring_notif_stream_types);
    }
    <!-- Flag indicating whether notification and ringtone volumes
         are controlled together (aliasing is true) or not. -->
    <bool name="config_alias_ring_notif_stream_types">true</bool>

>updateRingerMode

    void updateRingerMode() {
        final int ringerMode = mHelper.getRingerModeInternal();
        if (mRingerMode == ringerMode) return;
        mRingerMode = ringerMode;
        updatePreferenceIcon();
    }

有3种模式,对应不同的图标

>updatePreferenceIcon

    private void updatePreferenceIcon() {
        if (mPreference != null) {
            if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
                mPreference.showIcon(mNormalIconId);
            } else {
                if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE && mVibrator != null) {
                    mMuteIcon = mVibrateIconId;
                } else {
                    mMuteIcon = mSilentIconId;
                }
                mPreference.showIcon(mMuteIcon);
            }
        }
    }

> RINGER_MODE

    /**
     * Ringer mode that will be silent and will not vibrate. (This overrides the
     * vibrate setting.)
     */
    public static final int RINGER_MODE_SILENT = 0;

    /**
     * Ringer mode that will be silent and will vibrate. (This will cause the
     * phone ringer to always vibrate, but the notification vibrate to only
     * vibrate if set.)
     */
    public static final int RINGER_MODE_VIBRATE = 1;

    /**
     * Ringer mode that may be audible and may vibrate. It will be audible if
     * the volume before changing out of this mode was audible. It will vibrate
     * if the vibrate setting is on.
     */
    public static final int RINGER_MODE_NORMAL = 2;

3.2.getAvailabilityStatus

返回是否可用的状态

    public int getAvailabilityStatus() {
        return Utils.isVoiceCapable(mContext) && !mHelper.isSingleVolume()
                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
    }

>isAvailable

基类BasePreferenceController.java里用上述的状态判断此选项是否可用

    public final boolean isAvailable() {
        if (mIsForWork && mWorkProfileUser == null) {
            return false;
        }

        final int availabilityStatus = getAvailabilityStatus();
        return (availabilityStatus == AVAILABLE
                || availabilityStatus == AVAILABLE_UNSEARCHABLE
                || availabilityStatus == DISABLED_DEPENDENT_SETTING);
    }

>isVoiceCapable

是否有语音功能,或者说是否是手机

    /**
     * Returns whether the device is voice-capable (meaning, it is also a phone).
     */
    public static boolean isVoiceCapable(Context context) {
        final TelephonyManager telephony =
                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
                //读取的是配置
        return telephony != null && telephony.isVoiceCapable();
    }

3.3.RingReceiver

onResume和onPause里注册,取消注册,监听下边2个广播,mode改变更新图标

    private class RingReceiver extends BroadcastReceiver {
        private boolean mRegistered;

        public void register(boolean register) {
            if (mRegistered == register) return;
            if (register) {
                final IntentFilter filter = new IntentFilter();
                filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
                filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
                mContext.registerReceiver(this, filter);
            } else {
                mContext.unregisterReceiver(this);
            }
            mRegistered = register;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED.equals(action)) {
                mHandler.sendEmptyMessage(H.UPDATE_EFFECTS_SUPPRESSOR);
            } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
                mHandler.sendEmptyMessage(H.UPDATE_RINGER_MODE);
            }
        }
    }

>handleMessage

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_EFFECTS_SUPPRESSOR:
                    updateEffectsSuppressor();
                    break;
                case UPDATE_RINGER_MODE:
                    updateRingerMode();
                    break;
            }
        }

4.NotificationVolumePreferenceController

public class NotificationVolumePreferenceController extends VolumeSeekBarPreferenceController {

    private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;

>getAudioStream

    public int getAudioStream() {
        return AudioManager.STREAM_NOTIFICATION; //5
    }

4.1.构造方法

    public NotificationVolumePreferenceController(Context context, String key) {
        super(context, key);
        mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
        if (mVibrator != null && !mVibrator.hasVibrator()) {
            mVibrator = null;
        }
    //是否铃声和通知合并使用
        mRingNotificationAliased = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_alias_ring_notif_stream_types);
        updateRingerMode();
    }

>updateRingerMode

更新铃声模式

    private void updateRingerMode() {
        final int ringerMode = mHelper.getRingerModeInternal();
        if (mRingerMode == ringerMode) return;
        mRingerMode = ringerMode;
        updatePreferenceIconAndSliderState();
    }

4.2.getAvailabilityStatus

可用的条件如下:

  • config_show_notification_volume配置为true并且非单声音控制
  • isVoiceCapable为false或者mRingNotificationAliased为false
    public int getAvailabilityStatus() {

        // Show separate notification slider if ring/notification are not aliased by AudioManager --
        // if they are, notification volume is controlled by RingVolumePreferenceController.
        return mContext.getResources().getBoolean(R.bool.config_show_notification_volume)
                && (!mRingNotificationAliased || !Utils.isVoiceCapable(mContext))
                && !mHelper.isSingleVolume()
                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
    }

5.VolumeSeekBarPreferenceController

这个是小节3和4的父类

public abstract class VolumeSeekBarPreferenceController extends
        AdjustVolumeRestrictedPreferenceController implements LifecycleObserver {

    public VolumeSeekBarPreferenceController(Context context, String key) {
        super(context, key);
        setAudioHelper(new AudioHelper(context));
    }

5.1.displayPreference

    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        if (isAvailable()) {
            mPreference = screen.findPreference(getPreferenceKey());
            //mVolumePreferenceCallback是2.5里设置的
            mPreference.setCallback(mVolumePreferenceCallback);
            //见7.3,
            mPreference.setStream(getAudioStream());
            mPreference.setMuteIcon(getMuteIcon());
        }
    }

6.AudioHelper.java

    public AudioHelper(Context context) {
        mContext = context;
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    }

6.1.isSingleVolume

//是否是单一的音频类型,电视是true,其他的一般都是false

    public boolean isSingleVolume() {
        return AudioSystem.isSingleVolume(mContext);
    }

6.2.get setStreamVolume

获取设置音量,最终实现在小节9里

    public int getStreamVolume(int stream) {
        return mAudioManager.getStreamVolume(stream);//见9.6
    }

    public boolean setStreamVolume(int stream, int volume) {
        mAudioManager.setStreamVolume(stream, volume, 0);//见9.2
        return true;
    }

6.3.getMaxVolume getMinVolume

音量的最大最小值设置,获取

    public int getMaxVolume(int stream) {
        return mAudioManager.getStreamMaxVolume(stream);
    }

    public int getMinVolume(int stream) {
        int minVolume;
        try {
            minVolume = mAudioManager.getStreamMinVolume(stream);
        } catch (IllegalArgumentException e) {
            minVolume = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL);
        }
        return minVolume;
    }

6.4.getRingerModeInternal

    public int getRingerModeInternal() {
        return mAudioManager.getRingerModeInternal();
    }

7.VolumeSeekBarPreference.java

音量调节选项用的都是这个自定义的preference

public class VolumeSeekBarPreference extends SeekBarPreference {

7.1.构造方法

    public VolumeSeekBarPreference(Context context) {
        super(context);
        setLayoutResource(R.layout.preference_volume_slider);
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    }

7.2.onBindViewHolder

绑定的时候会进行初始化操作

    public void onBindViewHolder(PreferenceViewHolder view) {
        super.onBindViewHolder(view);
        mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
        mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
        mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text);
        init();
    }

>1.init

    protected void init() {
        if (mSeekBar == null) return;
        final SeekBarVolumizer.Callback sbvc = new SeekBarVolumizer.Callback() {
            @Override
            public void onSampleStarting(SeekBarVolumizer sbv) {
                if (mCallback != null) {
                    mCallback.onSampleStarting(sbv);
                }
            }
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
                if (mCallback != null) {
                    mCallback.onStreamValueChanged(mStream, progress);
                }
            }
            @Override
            public void onMuted(boolean muted, boolean zenMuted) {
                if (mMuted == muted && mZenMuted == zenMuted) return;
                mMuted = muted;
                mZenMuted = zenMuted;
                updateIconView();
            }
            @Override
            public void onStartTrackingTouch(SeekBarVolumizer sbv) {
                if (mCallback != null) {
                    mCallback.onStartTrackingTouch(sbv);
                }
                mJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
                        .withView(CUJ_SETTINGS_SLIDER, mSeekBar)
                        .setTag(getKey()));
            }
            @Override
            public void onStopTrackingTouch(SeekBarVolumizer sbv) {
                mJankMonitor.end(CUJ_SETTINGS_SLIDER);
            }
        };
        final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null;
        //封装的工具类,监听seekbar的拖动,通过audioManager修改音量,同时调用callback的对应方法
        if (mVolumizer == null) {
            mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc);
        }
        mVolumizer.start();
        mVolumizer.setSeekBar(mSeekBar);
        
        updateIconView();
        updateSuppressionText();
        if (!isEnabled()) {
            mSeekBar.setEnabled(false);
            mVolumizer.stop();
        }
    }

7.3.setStream

根据stream类型设置对应的音量最大最小值以及当前音量

    public void setStream(int stream) {
        mStream = stream;
        setMax(mAudioManager.getStreamMaxVolume(mStream));
        // Use getStreamMinVolumeInt for non-public stream type
        // eg: AudioManager.STREAM_BLUETOOTH_SCO
        setMin(mAudioManager.getStreamMinVolumeInt(mStream));
        setProgress(mAudioManager.getStreamVolume(mStream));
    }

8.SeekBarVolumizer.java

这个就是工具类,根据传入的音频类型,获取默认值,设置给seekbar,并监听seekbar的拖动,修改对应类型的音量大小

8.1.构造方法

    public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) {
        this(context, streamType, defaultUri, callback, true /* playSample */);
    }

    public SeekBarVolumizer(
            Context context,
            int streamType,
            Uri defaultUri,
            Callback callback,
            boolean playSample) {
        mContext = context;
        mAudioManager = context.getSystemService(AudioManager.class);
        mNotificationManager = context.getSystemService(NotificationManager.class);
        mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
        mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
                .PRIORITY_CATEGORY_ALARMS) != 0;
        mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
                .PRIORITY_CATEGORY_MEDIA) != 0;
        mAllowRinger = !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
                mNotificationPolicy);
         //根据stream类型获取对应的各种值
        mStreamType = streamType;
        mAffectedByRingerMode = mAudioManager.isStreamAffectedByRingerMode(mStreamType);
        mNotificationOrRing = isNotificationOrRing(mStreamType);
        if (mNotificationOrRing) {
            mRingerMode = mAudioManager.getRingerModeInternal();
        }
        mNotifAliasRing = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_alias_ring_notif_stream_types);
        mZenMode = mNotificationManager.getZenMode();

        if (hasAudioProductStrategies()) {
            mVolumeGroupId = getVolumeGroupIdForLegacyStreamType(mStreamType);
            mAttributes = getAudioAttributesForLegacyStreamType(
                    mStreamType);
        }

        mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);
        mCallback = callback;
        //初始的音量大小,见9.6
        mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
        mLastAudibleStreamVolume = mAudioManager.getLastAudibleStreamVolume(mStreamType);
        mMuted = mAudioManager.isStreamMute(mStreamType);
        mPlaySample = playSample;
        if (mCallback != null) {
            mCallback.onMuted(mMuted, isZenMuted());
        }
        if (defaultUri == null) {
            if (mStreamType == AudioManager.STREAM_RING) {
                defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
            } else if (mStreamType == AudioManager.STREAM_NOTIFICATION) {
                defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;
            } else {
                defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
            }
        }
        mDefaultUri = defaultUri;
    }

8.2.setSeekBar

    public void setSeekBar(SeekBar seekBar) {
        if (mSeekBar != null) {
            mSeekBar.setOnSeekBarChangeListener(null);
        }
        mSeekBar = seekBar;
        mSeekBar.setOnSeekBarChangeListener(null);
        mSeekBar.setMax(mMaxStreamVolume);
        //更新seekbar的位置
        updateSeekBar();
        mSeekBar.setOnSeekBarChangeListener(this);
    }

8.3.SeekBarChangeListener

    public void onStartTrackingTouch(SeekBar seekBar) {
        if (mCallback != null) {
            mCallback.onStartTrackingTouch(this);
        }
    }

    public void onStopTrackingTouch(SeekBar seekBar) {
        postStartSample();
        if (mCallback != null) {
            mCallback.onStopTrackingTouch(this);
        }
    }

    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
        if (fromTouch) {
            postSetVolume(progress);
        }
        if (mCallback != null) {
            mCallback.onProgressChanged(seekBar, progress, fromTouch);
        }
    }

>1.postSetVolume

修改音量大小,交给handler处理

    private void postSetVolume(int progress) {
        if (mHandler == null) return;
        // Do the volume changing separately to give responsive UI
        mLastProgress = progress;
        mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
        mHandler.removeMessages(MSG_START_SAMPLE);
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME),
                isDelay() ? SET_STREAM_VOLUME_DELAY_MS : 0);
    }

>2.MSG_SET_STREAM_VOLUME

具体逻辑在这里

            case MSG_SET_STREAM_VOLUME:
                if (mMuted && mLastProgress > 0) {
                //之前是静音模式,现在音量大于0
                    mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_UNMUTE, 0);
                } else if (!mMuted && mLastProgress == 0) {
                //之前是非静音模式,现在音量为0
                    mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_MUTE, 0);
                }
                //保存对应类型stream的音量,具体见9.1
                mAudioManager.setStreamVolume(mStreamType, mLastProgress,
                        AudioManager.FLAG_SHOW_UI_WARNINGS);
                break;

9.AudioService.java

mAudioManager里边最终调用的是AudioService的方法

9.1.setStreamVolumeWithAttributionInt

这个里边主要是权限检查,

    /**
     * Internal method for a stream type volume change. Can be used to change the volume on a
     * given device only
     * @param streamType the stream type whose volume is to be changed
     * @param index the volume index
     * @param flags options for volume handling
     * @param device null when controlling volume for the current routing, otherwise the device
     *               for which volume is being changed
     * @param callingPackage client side-provided package name of caller, not to be trusted
     * @param attributionTag client side-provided attribution name, not to be trusted
     */
    protected void setStreamVolumeWithAttributionInt(int streamType, int index, int flags,
            @Nullable AudioDeviceAttributes device,
            String callingPackage, String attributionTag) {
         //无障碍功能类型,没有对应的修改权限
        if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
            Log.w(TAG, "Trying to call setStreamVolume() for a11y without"
                    + " CHANGE_ACCESSIBILITY_VOLUME  callingPackage=" + callingPackage);
            return;
        }
        //语音通话功能,音量为0,没有对应的修改手机状态权限
        if ((streamType == AudioManager.STREAM_VOICE_CALL) && (index == 0)
                && (mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.MODIFY_PHONE_STATE)
                    != PackageManager.PERMISSION_GRANTED)) {
            Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
                    + " MODIFY_PHONE_STATE  callingPackage=" + callingPackage);
            return;
        }
        //语音助手类型,没有对应的权限
        if ((streamType == AudioManager.STREAM_ASSISTANT)
            && (mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.MODIFY_AUDIO_ROUTING)
                    != PackageManager.PERMISSION_GRANTED)) {
            Log.w(TAG, "Trying to call setStreamVolume() for STREAM_ASSISTANT without"
                    + " MODIFY_AUDIO_ROUTING  callingPackage=" + callingPackage);
            return;
        }

    //见9.2
        setStreamVolume(streamType, index, flags, device,
                callingPackage, callingPackage, attributionTag,
                Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
    }

9.2.setStreamVolume

    private void setStreamVolume(int streamType, int index, int flags,
            @Nullable AudioDeviceAttributes ada,
            String callingPackage, String caller, String attributionTag, int uid,
            boolean hasModifyAudioSettings) {
        if (mUseFixedVolume) {//补充1,默认为false
            return;
        }

        ensureValidStreamType(streamType);
        //数组数据见9.3,就是把一种音频类型指向另外一种,大家用一个
        int streamTypeAlias = mStreamVolumeAlias[streamType];
        VolumeStreamState streamState = mStreamStates[streamTypeAlias];

        final int device = (ada == null)
                ? getDeviceForStream(streamType)
                : ada.getInternalType();
        int oldIndex;

        // skip a2dp absolute volume control request when the device
        // is neither an a2dp device nor BLE device
        if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                && !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))
                && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
            return;
        }
        // If we are being called by the system (e.g. hardware keys) check for current user
        // so we handle user restrictions correctly.
        if (uid == android.os.Process.SYSTEM_UID) {
            uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
        }
        if (!checkNoteAppOp(
                STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage, attributionTag)) {
            return;
        }

        if (isAndroidNPlus(callingPackage)
                && wouldToggleZenMode(getNewRingerMode(streamTypeAlias, index, flags))
                && !mNm.isNotificationPolicyAccessGrantedForPackage(callingPackage)) {
            throw new SecurityException("Not allowed to change Do Not Disturb state");
        }

        if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
            return;
        }

        synchronized (mSafeMediaVolumeStateLock) {
            // reset any pending volume command
            mPendingVolumeCommand = null;

            oldIndex = streamState.getIndex(device);
    //就是把原本音频类型的音量大小转化为别名音频对应的音量大小,按照音频范围百分比转换
            index = rescaleIndex(index * 10, streamType, streamTypeAlias);

            if (streamTypeAlias == AudioSystem.STREAM_MUSIC
                    && AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                    && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {

                mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
            } else if (isAbsoluteVolumeDevice(device)
                    && ((flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0)) {
                AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);

                dispatchAbsoluteVolumeChanged(streamType, info, index);
            }

            if (AudioSystem.isLeAudioDeviceType(device)
                    && streamType == getBluetoothContextualVolumeStream()
                    && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
            //蓝牙
                mDeviceBroker.postSetLeAudioVolumeIndex(index,
                    mStreamStates[streamType].getMaxIndex(), streamType);
            }

            if (device == AudioSystem.DEVICE_OUT_HEARING_AID
                    && streamType == getBluetoothContextualVolumeStream()) {
    
                mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
            }

            flags &= ~AudioManager.FLAG_FIXED_VOLUME;
            if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
                flags |= AudioManager.FLAG_FIXED_VOLUME;

                // volume is either 0 or max allowed for fixed volume devices
                if (index != 0) {
                    if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
                            mSafeMediaVolumeDevices.contains(device)) {
                        index = safeMediaVolumeIndex(device);
                    } else {
                        index = streamState.getMaxIndex();
                    }
                }
            }

            if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {
                mVolumeController.postDisplaySafeVolumeWarning(flags);
                mPendingVolumeCommand = new StreamVolumeCommand(
                                                    streamType, index, flags, device);
            } else {
            //这里是修改音量的逻辑,主要就是修改对应的VolumeStreamState对象里的数据
                onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings);
                index = mStreamStates[streamType].getIndex(device);
            }
        }
        synchronized (mHdmiClientLock) {
            if (streamTypeAlias == AudioSystem.STREAM_MUSIC
                    && (oldIndex != index)) {
                maybeSendSystemAudioStatusCommand(false);
            }
        }
        sendVolumeUpdate(streamType, oldIndex, index, flags, device);
    }

>1.mUseFixedVolume

是否禁止用户修改音量,默认是false

    <!-- Flag indicating that the media framework should not allow changes or mute on any
         stream or global volumes. -->
    <bool name="config_useFixedVolume">false</bool>

>2.onSetStreamVolume

    private void onSetStreamVolume(int streamType, int index, int flags, int device,
            String caller, boolean hasModifyAudioSettings) {
        final int stream = mStreamVolumeAlias[streamType];
        //修改对应的音量大小,最终会通过handler操作,存储在settings
        setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);
        // setting volume on ui sounds stream type also controls silent mode
        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                (stream == getUiSoundsStreamType())) {
                //ringMode的获取见9.4
            setRingerMode(getNewRingerMode(stream, index, flags),
                    TAG + ".onSetStreamVolume", false /*external*/);
        }
        // setting non-zero volume for a muted stream unmutes the stream and vice versa,
        // except for BT SCO stream where only explicit mute is allowed to comply to BT requirements
        if (streamType != AudioSystem.STREAM_BLUETOOTH_SCO) {
        //index就是音量大小,为0表示静音
            mStreamStates[stream].mute(index == 0);
        }
    }

>3.setStreamVolumeInt

    private void setStreamVolumeInt(int streamType,
                                    int index,
                                    int device,
                                    boolean force,
                                    String caller, boolean hasModifyAudioSettings) {
        if (isFullVolumeDevice(device)) {
            return;
        }
        VolumeStreamState streamState = mStreamStates[streamType];

        if (streamState.setIndex(index, device, caller, hasModifyAudioSettings) || force) {
            // Post message to set system volume (it in turn will post a message
            // to persist).
            sendMsg(mAudioHandler,
                    MSG_SET_DEVICE_VOLUME,//这个case里又会调用MSG_PERSIST_VOLUME,这里会存储到settings里
                    SENDMSG_QUEUE,
                    device,
                    0,
                    streamState,
                    0);
        }
    }

9.3.updateStreamVolumeAlias

构造方法里以及其他几处调用,更新各种音频流类型音量的别名

    private void updateStreamVolumeAlias(boolean updateVolumes, String caller) {
        int dtmfStreamAlias;
        final int a11yStreamAlias = sIndependentA11yVolume ?
                AudioSystem.STREAM_ACCESSIBILITY : AudioSystem.STREAM_MUSIC;
        final int assistantStreamAlias = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_useAssistantVolume) ?
                AudioSystem.STREAM_ASSISTANT : AudioSystem.STREAM_MUSIC;

        if (mIsSingleVolume) {
            mStreamVolumeAlias = STREAM_VOLUME_ALIAS_TELEVISION;
            dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
        } else if (mUseVolumeGroupAliases) {
            mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NONE;
            dtmfStreamAlias = AudioSystem.STREAM_DTMF;
        } else {
        //正常情况走这里
            switch (mPlatformType) {
                case AudioSystem.PLATFORM_VOICE:
                //带语音功能的走这里
                    mStreamVolumeAlias = STREAM_VOLUME_ALIAS_VOICE;//数据见补充2
                    dtmfStreamAlias = AudioSystem.STREAM_RING;
                    break;
                default://不带语音功能的走这里
                    mStreamVolumeAlias = STREAM_VOLUME_ALIAS_DEFAULT;//数据见补充3
                    dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
            }
            if (!mNotifAliasRing) {
            //默认不走这里,铃声和通知默认是统一处理的,走到这里说明是分开处理的,那把通知的别名改回自己的
                mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] =
                        AudioSystem.STREAM_NOTIFICATION;
            }
        }

        if (mIsSingleVolume) {
            mRingerModeAffectedStreams = 0;
        } else {
            if (isInCommunication()) {
                dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
                mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_DTMF);
            } else {
                mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_DTMF);
            }
        }
        //这里修改下边3种音频类型的别名为dtmfStreamAlias的值
        mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
        mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
        mStreamVolumeAlias[AudioSystem.STREAM_ASSISTANT] = assistantStreamAlias;

        //下边是更新相关流状态的逻辑,暂时不看
        if (updateVolumes && mStreamStates != null) {
            updateDefaultVolumes();

            synchronized (mSettingsLock) {
                synchronized (VolumeStreamState.class) {
                    mStreamStates[AudioSystem.STREAM_DTMF]
                            .setAllIndexes(mStreamStates[dtmfStreamAlias], caller);
                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
                            System.VOLUME_SETTINGS_INT[a11yStreamAlias];
                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
                            mStreamStates[a11yStreamAlias], caller);
                }
            }
            if (sIndependentA11yVolume) {
                // restore the a11y values from the settings
                mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
            }

            // apply stream mute states according to new value of mRingerModeAffectedStreams
            setRingerModeInt(getRingerModeInternal(), false);
            sendMsg(mAudioHandler,
                    MSG_SET_ALL_VOLUMES,
                    SENDMSG_QUEUE,
                    0,
                    0,
                    mStreamStates[AudioSystem.STREAM_DTMF], 0);
            sendMsg(mAudioHandler,
                    MSG_SET_ALL_VOLUMES,
                    SENDMSG_QUEUE,
                    0,
                    0,
                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY], 0);
        }
    }

>1.变量

    // indicates whether the system maps all streams to a single stream.
    private final boolean mIsSingleVolume;//见10.1
    
    private final boolean mUseFixedVolume;
    private final boolean mUseVolumeGroupAliases;

    //默认false
        mUseVolumeGroupAliases = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);

    /**
     * indicates whether STREAM_NOTIFICATION is aliased to STREAM_RING
     *     not final due to test method, see {@link #setNotifAliasRingForTest(boolean)}.
     */
    private boolean mNotifAliasRing;
    //默认是true
        mNotifAliasRing = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_alias_ring_notif_stream_types);

前别贴过,就是铃声和通知是否用一个音量控制

    <!-- Flag indicating whether notification and ringtone volumes
         are controlled together (aliasing is true) or not. -->
    <bool name="config_alias_ring_notif_stream_types">true</bool>

>2.STREAM_VOLUME_ALIAS_VOICE

带语音功能的别名是这个数组,看下注解有解释为啥用别名

    /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
     * of another stream: This avoids multiplying the volume settings for hidden
     * stream types that follow other stream behavior for volume settings
     * NOTE: do not create loops in aliases!
     * Some streams alias to different streams according to device category (phone or tablet) or
     * use case (in call vs off call...). See updateStreamVolumeAlias() for more details.
     *  mStreamVolumeAlias contains STREAM_VOLUME_ALIAS_VOICE aliases for a voice capable device
     *  (phone), STREAM_VOLUME_ALIAS_TELEVISION for a television or set-top box and
     *  STREAM_VOLUME_ALIAS_DEFAULT for other devices (e.g. tablets).*/
     
    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
    };

>3.STREAM_VOLUME_ALIAS_DEFAULT

不带语音功能的别名数组

    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
    };

>4.MIN_STREAM_VOLUME

    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
    };

>5.MAX_STREAM_VOLUME

    protected static int[] MAX_STREAM_VOLUME = new int[] {
        5,  // STREAM_VOICE_CALL
        7,  // STREAM_SYSTEM
        7,  // STREAM_RING            // configured by config_audio_ring_vol_steps
        15, // STREAM_MUSIC
        7,  // STREAM_ALARM
        7,  // STREAM_NOTIFICATION    // configured by config_audio_notif_vol_steps
        15, // STREAM_BLUETOOTH_SCO
        7,  // STREAM_SYSTEM_ENFORCED
        15, // STREAM_DTMF
        15, // STREAM_TTS
        15, // STREAM_ACCESSIBILITY
        15  // STREAM_ASSISTANT
    };

9.4.getNewRingerMode

    private int getNewRingerMode(int stream, int index, int flags) {
        // setRingerMode does nothing if the device is single volume,so the value would be unchanged
        if (mIsSingleVolume) {
            return getRingerModeExternal();
        }

        // setting volume on ui sounds stream type also controls silent mode
        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                (stream == getUiSoundsStreamType())) {
            //铃声模式
            int newRingerMode;
            if (index == 0) {
            //音量是0,先判断是否可以震动,可以的话就是震动模式,不可以继续判断是否
            //音量减少可以进入安静模式,是的话返回silent模式,其他的返回normal模式。
                newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
                        : mVolumePolicy.volumeDownToEnterSilent ? AudioManager.RINGER_MODE_SILENT
                                : AudioManager.RINGER_MODE_NORMAL;
            } else {
            //音量非0,normal模式
                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
            }
            return newRingerMode;
        }
        //不是ring类型的,返回ring的当前类型
        return getRingerModeExternal();
    }

9.5.createStreamStates

    private void createStreamStates() {
        int numStreamTypes = AudioSystem.getNumStreamTypes();
        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];

        for (int i = 0; i < numStreamTypes; i++) {
        //对应的第一个参数名字用的是别名对应的音频名字
            streams[i] =
            //构造方法里会读取对应的音量,见9.7
                    new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
        }

        checkAllFixedVolumeDevices();
        checkAllAliasStreamVolumes();
        checkMuteAffectedStreams();
        updateDefaultVolumes();
    }

>1.checkAllAliasStreamVolumes

    private void checkAllAliasStreamVolumes() {
        synchronized (mSettingsLock) {
            synchronized (VolumeStreamState.class) {
                int numStreamTypes = AudioSystem.getNumStreamTypes();
                for (int streamType = 0; streamType < numStreamTypes; streamType++) {
                //把数据执行别名对应的数据
                    mStreamStates[streamType]
                            .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG);
                    // apply stream volume
                    if (!mStreamStates[streamType].mIsMuted) {
                    //更新state里的数据
                        mStreamStates[streamType].applyAllVolumes();
                    }
                }
            }
        }
    }

9.6.getStreamVolume

    public int getStreamVolume(int streamType) {
        ensureValidStreamType(streamType);
        //获取的也是对应别名的
        int device = getDeviceForStream(streamType);
        synchronized (VolumeStreamState.class) {
            int index = mStreamStates[streamType].getIndex(device);

            // by convention getStreamVolume() returns 0 when a stream is muted.
            if (mStreamStates[streamType].mIsMuted) {
                index = 0;
            }
            if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
                    isFixedVolumeDevice(device)) {
                index = mStreamStates[streamType].getMaxIndex();
            }
            return (index + 5) / 10;
        }
    }

9.7.VolumeStreamState

小节9.5会调用

>1.构造方法

    //数据在这个集合里,存储对应类型的index也就是音量大小值,自定义了put方法,保存数据是native方法
        private final SparseIntArray mIndexMap = new SparseIntArray(8) {
    //..
    
        private VolumeStreamState(String settingName, int streamType) {
            mVolumeIndexSettingName = settingName;

            mStreamType = streamType;
            //数组见9.3.4
            mIndexMin = MIN_STREAM_VOLUME[streamType] * 10;
            mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex()
            //数组见9.3.5
            mIndexMax = MAX_STREAM_VOLUME[streamType] * 10;
            //初始化对应类型的音频,native方法
            final int status = AudioSystem.initStreamVolume(
                    streamType, mIndexMin / 10, mIndexMax / 10);
            if (status != AudioSystem.AUDIO_STATUS_OK) {
            //
            }
            //读取保存的值
            readSettings();
            mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
            mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
            mStreamDevicesChanged = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
            mStreamDevicesChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
        }

>2.readSettings

            synchronized (VolumeStreamState.class) {
            //循环所有的输出装置集合
                for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {

                    // retrieve current volume for device
                    // if no volume stored for current stream and device, use default volume if default
                    // device, continue otherwise
                    //如果是默认的输出装置,那默认值用默认的配置里的,否则默认值是-1
                    int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
                            AudioSystem.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
                    int index;
                    if (!hasValidSettingsName()) {
                        index = defaultIndex;
                    } else {
                    //获取key值,读取settings里存储的值
                        String name = getSettingNameForDevice(device);
                        index = mSettings.getSystemIntForUser(
                                mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
                    }
                    if (index == -1) {
                        continue;
                    }
                    //更新map数据
                    mIndexMap.put(device, getValidIndex(10 * index,
                            true /*hasModifyAudioSettings*/));
                }
            }

>3.getSettingNameForDevice

举例:铃声对应的mVolumeIndexSettingName是volume_ring,后边拼上对应的device name

  • volume_ring_0x40000000 ,默认的输出装置
  • volume_ring_earpiece ,耳机输出装置
        public @Nullable String getSettingNameForDevice(int device) {
            if (!hasValidSettingsName()) {
            //mVolumeIndexSettingName为null,构造方法里传递的,正常不为null
                return null;
            }
            //device转换成对应的string
            final String suffix = AudioSystem.getOutputDeviceName(device);
            if (suffix.isEmpty()) {
                return mVolumeIndexSettingName是
            }
            //拼接在一起
            return mVolumeIndexSettingName + "_" + suffix;
        }

9.8.获取最小最大音量

数据来源见9.7构造方法

    public int getStreamMaxVolume(int streamType) {
        ensureValidStreamType(streamType);
        return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
    }

    public int getStreamMinVolume(int streamType) {
        ensureValidStreamType(streamType);
        final boolean isPrivileged =
                Binder.getCallingUid() == Process.SYSTEM_UID
                 || callingHasAudioSettingsPermission()
                 || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
                        == PackageManager.PERMISSION_GRANTED);
        return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
    }

10.AudioSystem.java

10.1.isSingleVolume

电视是一个音量控制的,或者配置里设置为强制一个音量控制

    public static boolean isSingleVolume(Context context) {
        boolean forceSingleVolume = context.getResources().getBoolean(
                com.android.internal.R.bool.config_single_volume);
        return getPlatformType(context) == PLATFORM_TELEVISION || forceSingleVolume;
    }

10.2.getPlatformType

3种类型

    public static int getPlatformType(Context context) {
        if (((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
                .isVoiceCapable()) {
           //是否有语音功能,也是可配置的
            return PLATFORM_VOICE;
        } else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
            return PLATFORM_TELEVISION;
        } else {
            return PLATFORM_DEFAULT;
        }
    }

10.3.数据

>1.DEFAULT_STREAM_VOLUME

默认输出装置的默认音量值

    public static int[] DEFAULT_STREAM_VOLUME = new int[] {
        4,  // STREAM_VOICE_CALL
        7,  // STREAM_SYSTEM
        5,  // STREAM_RING           // configured in AudioService by config_audio_notif_vol_default
        5, // STREAM_MUSIC
        6,  // STREAM_ALARM
        5,  // STREAM_NOTIFICATION   // configured in AudioService by config_audio_ring_vol_default
        7,  // STREAM_BLUETOOTH_SCO
        7,  // STREAM_SYSTEM_ENFORCED
        5, // STREAM_DTMF
        5, // STREAM_TTS
        5, // STREAM_ACCESSIBILITY
        5, // STREAM_ASSISTANT
    };

>2.STREAM_NAMES

    public static final String[] STREAM_NAMES = new String[] {
        "STREAM_VOICE_CALL",
        "STREAM_SYSTEM",
        "STREAM_RING",
        "STREAM_MUSIC",
        "STREAM_ALARM",
        "STREAM_NOTIFICATION",
        "STREAM_BLUETOOTH_SCO",
        "STREAM_SYSTEM_ENFORCED",
        "STREAM_DTMF",
        "STREAM_TTS",
        "STREAM_ACCESSIBILITY",
        "STREAM_ASSISTANT"
    };

>3.常量

对应补充2里的索引

    public static final int STREAM_DEFAULT = -1;
    /** @hide Used to identify the volume of audio streams for phone calls */
    public static final int STREAM_VOICE_CALL = 0;
    /** @hide Used to identify the volume of audio streams for system sounds */
    public static final int STREAM_SYSTEM = 1;
    /** @hide Used to identify the volume of audio streams for the phone ring and message alerts */
    public static final int STREAM_RING = 2;
    /** @hide Used to identify the volume of audio streams for music playback */
    public static final int STREAM_MUSIC = 3;
    /** @hide Used to identify the volume of audio streams for alarms */
    public static final int STREAM_ALARM = 4;
    /** @hide Used to identify the volume of audio streams for notifications */
    public static final int STREAM_NOTIFICATION = 5;
    /** @hide
     *  Used to identify the volume of audio streams for phone calls when connected on bluetooth */
    public static final int STREAM_BLUETOOTH_SCO = 6;
    /** @hide Used to identify the volume of audio streams for enforced system sounds in certain
     * countries (e.g camera in Japan) */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public static final int STREAM_SYSTEM_ENFORCED = 7;
    /** @hide Used to identify the volume of audio streams for DTMF tones */
    public static final int STREAM_DTMF = 8;
    /** @hide Used to identify the volume of audio streams exclusively transmitted through the
     *  speaker (TTS) of the device */
    public static final int STREAM_TTS = 9;
    /** @hide Used to identify the volume of audio streams for accessibility prompts */
    public static final int STREAM_ACCESSIBILITY = 10;
    /** @hide Used to identify the volume of audio streams for virtual assistant */
    public static final int STREAM_ASSISTANT = 11;

10.4.getNumStreamTypes()

数据参考10.3.2

    // Expose only the getter method publicly so we can change it in the future
    private static final int NUM_STREAM_TYPES = 12;

    public static final int getNumStreamTypes() { return NUM_STREAM_TYPES; }

11.Settings.java

11.1.数据

>1.VOLUME_SETTINGS_INT

数据个数和10.3.2是一样的,不过有几个是空字符串(他们的音量设置不是永久的)

        /**
         * The mapping of stream type (integer) to its setting.
         * Unlike the VOLUME_SETTINGS array, this one contains as many entries as
         * AudioSystem.NUM_STREAM_TYPES, and has empty strings for stream types whose volumes
         * are never persisted.
         */
        public static final String[] VOLUME_SETTINGS_INT = {
                VOLUME_VOICE, VOLUME_SYSTEM, VOLUME_RING, VOLUME_MUSIC,
                VOLUME_ALARM, VOLUME_NOTIFICATION, VOLUME_BLUETOOTH_SCO,
                "" /*STREAM_SYSTEM_ENFORCED, no setting for this stream*/,
                "" /*STREAM_DTMF, no setting for this stream*/,
                "" /*STREAM_TTS, no setting for this stream*/,
                VOLUME_ACCESSIBILITY, VOLUME_ASSISTANT
            };

12.总结

  • 支持语音通话的显示的是ring&notification volume ,不支持的显示的是notifaction volume
  • ring和notification的streamType一个是2一个是5,不过最终用的都是2,具体见9.3的别名数组
  • 音量大小的读取是存储在settings里的,至于key值见9.7里key值的组合