美文网首页Android平台Audio系统
Android平台音量调节(一)音量键调节音量

Android平台音量调节(一)音量键调节音量

作者: 夕月风 | 来源:发表于2018-04-24 16:29 被阅读0次

    Android平台音量调节

    本文基于Android 8.0讲述Android平台原生音量控制功能。

    流的定义

    Android中,音量都是分开控制,各种流定义各种流的音量。在Android8.0中,定义了11种流类型。对每种流类型都定义了最大音量(MAX_STREAM_VOLUME),默认音量(DEFAULT_STREAM_VOLUME)和最小音量(MIN_STREAM_VOLUME)。一个流也可以用另外一个流的音量设置,所以需要一个别名映射。

    • 最大音量
    * frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    
        private 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
        };
    
    • 最小音量
    * frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    
        private static int[] MIN_STREAM_VOLUME = new int[] {
            1,  // STREAM_VOICE_CALL
            0,  // STREAM_SYSTEM
            0,  // STREAM_RING
            0,  // STREAM_MUSIC
            0,  // STREAM_ALARM
            0,  // STREAM_NOTIFICATION
            0,  // STREAM_BLUETOOTH_SCO
            0,  // STREAM_SYSTEM_ENFORCED
            0,  // STREAM_DTMF
            0,  // STREAM_TTS
            0   // STREAM_ACCESSIBILITY
        };
    
    • 默认音量
    * frameworks/base/media/java/android/media/AudioSystem.java
    
        public static int[] DEFAULT_STREAM_VOLUME = new int[] {
            4,  // STREAM_VOICE_CALL
            7,  // STREAM_SYSTEM
            5,  // STREAM_RING
            11, // STREAM_MUSIC
            6,  // STREAM_ALARM
            5,  // STREAM_NOTIFICATION
            7,  // STREAM_BLUETOOTH_SCO
            7,  // STREAM_SYSTEM_ENFORCED
            11, // STREAM_DTMF
            11, // STREAM_TTS
            11, // STREAM_ACCESSIBILITY
        };
    
    • 别名映射
      Android中各种设备的映射不尽相同,Android中定义了3中设备DEFAULT,VOICE,TELEVISION对应的别名映射。(STREAM_VOLUME_ALIAS_VOICE)。

    STREAM_VOLUME_ALIAS_VOICE对应能处理能处理Voice的设备,比如电话,请映射如下:

    * frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    
        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
        };
    

    STREAM_VOLUME_ALIAS_TELEVISION对应电视,机顶盒等。

    * frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    
        private final int[] STREAM_VOLUME_ALIAS_TELEVISION = new int[] {
            AudioSystem.STREAM_MUSIC,       // STREAM_VOICE_CALL
            AudioSystem.STREAM_MUSIC,       // STREAM_SYSTEM
            AudioSystem.STREAM_MUSIC,       // STREAM_RING
            AudioSystem.STREAM_MUSIC,       // STREAM_MUSIC
            AudioSystem.STREAM_MUSIC,       // STREAM_ALARM
            AudioSystem.STREAM_MUSIC,       // STREAM_NOTIFICATION
            AudioSystem.STREAM_MUSIC,       // STREAM_BLUETOOTH_SCO
            AudioSystem.STREAM_MUSIC,       // STREAM_SYSTEM_ENFORCED
            AudioSystem.STREAM_MUSIC,       // STREAM_DTMF
            AudioSystem.STREAM_MUSIC,       // STREAM_TTS
            AudioSystem.STREAM_MUSIC        // STREAM_ACCESSIBILITY
        };
    

    STREAM_VOLUME_ALIAS_DEFAULT其实和Voice映射是一样的,对应其他的设备,比如平板等。

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

    映射关系如下:

    流代号 流类型 默认音量 最大音量 最小音量 DEFAULT/Voice映射 TELEVISION映射
    0 STREAM_VOICE_CALL 4 5 1 STREAM_VOICE_CALL STREAM_MUSIC
    1 STREAM_SYSTEM 5 7 0 STREAM_RING STREAM_MUSIC
    2 STREAM_RING 5 7 0 STREAM_RING STREAM_MUSIC
    5 STREAM_NOTIFICATION 5 7 0 STREAM_RING STREAM_MUSIC
    7 STREAM_SYSTEM_ENFORCED 7 7 0 STREAM_RING STREAM_MUSIC
    8 STREAM_DTMF 11 15 0 STREAM_RING STREAM_MUSIC
    3 STREAM_MUSIC 11 15 0 STREAM_MUSIC STREAM_MUSIC
    9 STREAM_TTS 11 15 0 STREAM_MUSIC STREAM_MUSIC
    10 STREAM_ACCESSIBILITY 11 15 0 STREAM_MUSIC STREAM_MUSIC
    4 STREAM_ALARM 6 7 0 STREAM_ALARM STREAM_MUSIC
    6 STREAM_BLUETOOTH_SCO 7 15 0 STREAM_BLUETOOTH_SCO STREAM_MUSIC

    所以在手机设备中,从上表来看,我们能调节的其实就5个音量。当你想调节STREAM_SYSTEM,STREAM_NOTIFICATION等流类型的音量时,实际上是调节了STREAM_RING的音量。当前可控的流类型可以通过下表更直观地显示:

    流类型 默认音量 最大音量 意义
    STREAM_VOICE_CALL 4 5 通话音量
    STREAM_RING 5 7 响铃,通知,系统默认音等
    STREAM_MUSIC 11 15 多媒体音量
    STREAM_ALARM 6 7 闹钟音量
    STREAM_BLUETOOTH_SCO 7 15 蓝牙音量

    音量键的处理流程

    音量键是常用的调节音量的方式,调节音量时,是调节音量节,比如媒体音STREAM_MUSIC的最大音量为15,其实是15节,你也可以理解为16节,因为还有0.

    音量键的处理

    Android设置了3个音量处理键,音量加,音量减和静音。3个音量键通过PhoneWindowManager进行处理。

    * frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
    
        private void dispatchDirectAudioEvent(KeyEvent event) {
            if (event.getAction() != KeyEvent.ACTION_DOWN) {
                return;
            }
            int keyCode = event.getKeyCode();
            int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
                    | AudioManager.FLAG_FROM_KEY;
            String pkgName = mContext.getOpPackageName();
            switch (keyCode) {
                case KeyEvent.KEYCODE_VOLUME_UP:
                    try {
                        getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
                                AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
                    } catch (Exception e) {
                        Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e);
                    }
                    break;
                case KeyEvent.KEYCODE_VOLUME_DOWN:
                    try {
                        getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
                                AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
                    } catch (Exception e) {
                        Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e);
                    }
                    break;
                case KeyEvent.KEYCODE_VOLUME_MUTE:
                    try {
                        if (event.getRepeatCount() == 0) {
                            getAudioService().adjustSuggestedStreamVolume(
                                    AudioManager.ADJUST_TOGGLE_MUTE,
                                    AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
                        }
                    } catch (Exception e) {
                        Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e);
                    }
                    break;
            }
        }
    

    音量的加减都是一节一节的调节的,比如最大音量为 7,表示有7节,加上0,就是8节。

    3音量按键对应如下:

    按键 AudioService 对应的操作 实际意义
    KEYCODE_VOLUME_UP ADJUST_RAISE 音量加
    KEYCODE_VOLUME_DOWN ADJUST_LOWER 音量减
    KEYCODE_VOLUME_MUTE ADJUST_TOGGLE_MUTE 静音按钮

    ADJUST_TOGGLE_MUTE是静音按钮,如果现在是静音的,那么将切为非静音;如果是非静音的,那么将设置为静音。相关的还有ADJUST_MUTE和ADJUST_UNMUTE。

    此外,AudioService相关的还有ADJUST_SAME,这个只是弹出调节音量的框,不去修改音量。

    adjustSuggestedStreamVolume通过Binder调到AudioService中。

    * frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    
        public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
                String callingPackage, String caller) {
            adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
                    caller, Binder.getCallingUid());
        }
    

    adjustSuggestedStreamVolume调同名函数adjustSuggestedStreamVolume:

        private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
                String callingPackage, String caller, int uid) {
            final int streamType;
            if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
                streamType = mVolumeControlStream;
            } else {
                final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
                final boolean activeForReal;
                ... ...
            }
    
            final boolean isMute = isMuteAdjust(direction);
    
            ensureValidStreamType(streamType);
            final int resolvedStream = mStreamVolumeAlias[streamType];
    
            // Play sounds on STREAM_RING only.
            if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
                    resolvedStream != AudioSystem.STREAM_RING) {
                flags &= ~AudioManager.FLAG_PLAY_SOUND;
            }
    
            if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) {
                direction = 0;
                flags &= ~AudioManager.FLAG_PLAY_SOUND;
                flags &= ~AudioManager.FLAG_VIBRATE;
                if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
            }
    
            adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid);
        }
    

    这里主要做了以下几件事:

    • mUserSelectedVolumeControlStream 表示强制控制某一种流类型,这里应该很少走
    • getActiveStreamType获取要控制的流类型,从Binder那边传过来的 值为USE_DEFAULT_STREAM_TYPE,getActiveStreamType大概流程如下:
    获取Stream的类型
    • 获取映射别名 resolvedStream
    • 对于通知音或铃声STREAM_RING,先显示调节音量的对话框,这个在AudioService的内部类VolumeController中处理。
    • 最后,通过adjustStreamVolume函数设置音量

    adjustStreamVolume函数

    adjustStreamVolume函数比较长,我们分段来看

    * frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    
        private void adjustStreamVolume(int streamType, int direction, int flags,
                String callingPackage, String caller, int uid) {
            ... ...
    
            ensureValidDirection(direction);
            ensureValidStreamType(streamType);
    
            boolean isMuteAdjust = isMuteAdjust(direction);
    
            if (isMuteAdjust && !isStreamAffectedByMute(streamType)) {
                return;
            }
    
            int streamTypeAlias = mStreamVolumeAlias[streamType];
    
            VolumeStreamState streamState = mStreamStates[streamTypeAlias];
    
    • 获取需要设置音量类型的映射别名streamTypeAlias
    • 获取音量类型对应的音量信息状态 streamState,VolumeStreamState 音量控制的核心,映射后的每种音量都有各自的VolumeStreamState,它保存了一个流类型所有的音量信息。
    * frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    
        private void adjustStreamVolume(int streamType, int direction, int flags,
                String callingPackage, String caller, int uid) {
            ... ...
            final int device = getDeviceForStream(streamTypeAlias);
    
            int aliasIndex = streamState.getIndex(device);
            boolean adjustVolume = true;
            int step;
    
    • 获取流类型对应的设备
      流各自的VolumeStreamState通过observeDevicesForStream_syncVSS去获取对应的device,observeDevicesForStream_syncVSS的获取到的是一个device列表,最终是通过AudioSystem去native获取的。

    再从获取的device列表中进行二次选择device:

        private int getDeviceForStream(int stream) {
            int device = getDevicesForStream(stream);
            if ((device & (device - 1)) != 0) {
                if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
                    device = AudioSystem.DEVICE_OUT_SPEAKER;
                } else if ((device & AudioSystem.DEVICE_OUT_HDMI_ARC) != 0) {
                    device = AudioSystem.DEVICE_OUT_HDMI_ARC;
                } else if ((device & AudioSystem.DEVICE_OUT_SPDIF) != 0) {
                    device = AudioSystem.DEVICE_OUT_SPDIF;
                } else if ((device & AudioSystem.DEVICE_OUT_AUX_LINE) != 0) {
                    device = AudioSystem.DEVICE_OUT_AUX_LINE;
                } else {
                    device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
                }
            }
            return device;
        }
    

    继续看adjustStreamVolume,这部分我们以注释的形式加在代码中

    * frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    
        private void adjustStreamVolume(int streamType, int direction, int flags,
                String callingPackage, String caller, int uid) {
            ... ...
            // 绝对音量控制
            if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) == 0 &&
                (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
                return;
            }
    
            // user处理
            if (uid == android.os.Process.SYSTEM_UID) {
                uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
            }
            // 权限处理
            if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
                    != AppOpsManager.MODE_ALLOWED) {
                return;
            }
    
            // 清掉待处理的音量处理命令
            synchronized (mSafeMediaVolumeState) {
                mPendingVolumeCommand = null;
            }
    
            // 处理是否混音,获取step
            flags &= ~AudioManager.FLAG_FIXED_VOLUME;
            if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
                   ((device & mFixedVolumeDevices) != 0)) {
                flags |= AudioManager.FLAG_FIXED_VOLUME;
    
                // Always toggle between max safe volume and 0 for fixed volume devices where safe
                // volume is enforced, and max and 0 for the others.
                // This is simulated by stepping by the full allowed volume range
                if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
                        (device & mSafeMediaVolumeDevices) != 0) {
                    step = mSafeMediaVolumeIndex;
                } else {
                    step = streamState.getMaxIndex();
                }
                if (aliasIndex != 0) {
                    aliasIndex = step;
                }
            } else {
                // convert one UI step (+/-1) into a number of internal units on the stream alias
                step = rescaleIndex(10, streamType, streamTypeAlias);
            }
    
            // 响铃模式处理
            if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                    (streamTypeAlias == getUiSoundsStreamType())) {
                int ringerMode = getRingerModeInternal();
                // do not vibrate if already in vibrate mode
                if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
                    flags &= ~AudioManager.FLAG_VIBRATE;
                }
                // Check if the ringer mode handles this adjustment. If it does we don't
                // need to adjust the volume further.
                final int result = checkForRingerModeChange(aliasIndex, direction, step,
                        streamState.mIsMuted, callingPackage, flags);
                adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
                // If suppressing a volume adjustment in silent mode, display the UI hint
                if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
                    flags |= AudioManager.FLAG_SHOW_SILENT_HINT;
                }
                // If suppressing a volume down adjustment in vibrate mode, display the UI hint
                if ((result & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
                    flags |= AudioManager.FLAG_SHOW_VIBRATE_HINT;
                }
            }
            // 如果不设置音量adjustVolume
            if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
                adjustVolume = false;
            }
            int oldIndex = mStreamStates[streamType].getIndex(device);
    
            if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
                mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);
    
                // Check if volume update should be send to AVRCP
                if (streamTypeAlias == AudioSystem.STREAM_MUSIC &&
                    (device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
                    (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
                    synchronized (mA2dpAvrcpLock) {
                        if (mA2dp != null && mAvrcpAbsVolSupported) {
                            mA2dp.adjustAvrcpAbsoluteVolume(direction);
                        }
                    }
                }
                // 静音设置
                if (isMuteAdjust) {
                    boolean state;
                    if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
                        state = !streamState.mIsMuted;
                    } else {
                        state = direction == AudioManager.ADJUST_MUTE;
                    }
                    if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
                        setSystemAudioMute(state);
                    }
                    for (int stream = 0; stream < mStreamStates.length; stream++) {
                        if (streamTypeAlias == mStreamVolumeAlias[stream]) {
                            if (!(readCameraSoundForced()
                                        && (mStreamStates[stream].getStreamType()
                                            == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
                                mStreamStates[stream].mute(state);
                            }
                        }
                    }
                } else if ((direction == AudioManager.ADJUST_RAISE) &&
                        !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
                    //安全模式,提示音量过高
                    mVolumeController.postDisplaySafeVolumeWarning(flags);
                } else if (streamState.adjustIndex(direction * step, device, caller)
                        || streamState.mIsMuted) {
                    if (streamState.mIsMuted) {
                        // Unmute the stream if it was previously muted
                        if (direction == AudioManager.ADJUST_RAISE) {
                            // unmute immediately for volume up
                            streamState.mute(false);
                        } else if (direction == AudioManager.ADJUST_LOWER) {
                            if (mIsSingleVolume) {
                                sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,
                                        streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);
                            }
                        }
                    } // 设置到底层
                    sendMsg(mAudioHandler,
                            MSG_SET_DEVICE_VOLUME,
                            SENDMSG_QUEUE,
                            device,
                            0,
                            streamState,
                            0);
                }
    
                // 是否需要设置HDMI的音量
                int newIndex = mStreamStates[streamType].getIndex(device);
                if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
                    setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
                }
                if (mHdmiManager != null) {
                    synchronized (mHdmiManager) {
                        // mHdmiCecSink true => mHdmiPlaybackClient != null
                        if (mHdmiCecSink &&
                                streamTypeAlias == AudioSystem.STREAM_MUSIC &&
                                oldIndex != newIndex) {
                            synchronized (mHdmiPlaybackClient) {
                                int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
                                        KeyEvent.KEYCODE_VOLUME_UP;
                                final long ident = Binder.clearCallingIdentity();
                                try {
                                    mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
                                    mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
                                } finally {
                                    Binder.restoreCallingIdentity(ident);
                                }
                            }
                        }
                    }
                }
            }
            // 更新音量
            int index = mStreamStates[streamType].getIndex(device);
            sendVolumeUpdate(streamType, oldIndex, index, flags);
        }
    

    在上面这段代码中,需要注意下面几个关键的流程:

    • 计算step
      如果不是音乐类型,且固定音量,那么音量调节将转换,转换的算法为:
        private int rescaleIndex(int index, int srcStream, int dstStream) {
            return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
        }
    

    index为10,所以STREAM_NOTIFICATION的step为10,STREAM_DTMF的step为(10x15 + 15/2) / 15 = 5

    • 处理RingMode
      Android定义了如下的模式:
        public static final int RINGER_MODE_SILENT = 0;
        public static final int RINGER_MODE_VIBRATE = 1;
        public static final int RINGER_MODE_NORMAL = 2;
        public static final int RINGER_MODE_MAX = RINGER_MODE_NORMAL;
    
    • 设置音量
      首先处理静音,处理安全音量,最后再处理音量。

    音量首先保存在每个流类型的VolumeStreamState中:

            public boolean adjustIndex(int deltaIndex, int device, String caller) {
                return setIndex(getIndex(device) + deltaIndex, device, caller);
            }
    

    在通过 MSG_SET_DEVICE_VOLUME 消息设置到底层。AudioHandler通过setDeviceVolume处理MSG_SET_DEVICE_VOLUME消息。最后是通过AudioSystem的native接口设置到native。

    • 通知音量更改
      最后,通过sendVolumeUpdate广播整个系统,通知整个系统音量修改。

    看完代码,我们来看看Java层的时序吧~


    AudioService设置按键音量的过程

    相关文章

      网友评论

        本文标题:Android平台音量调节(一)音量键调节音量

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