Android平臺(tái)音量調(diào)節(jié)(一)音量鍵調(diào)節(jié)音量

Android平臺(tái)音量調(diào)節(jié)

本文基于Android 8.0講述Android平臺(tái)原生音量控制功能。

流的定義

Android中绽左,音量都是分開控制甲雅,各種流定義各種流的音量。在Android8.0中萎馅,定義了11種流類型双戳。對(duì)每種流類型都定義了最大音量(MAX_STREAM_VOLUME),默認(rèn)音量(DEFAULT_STREAM_VOLUME)和最小音量(MIN_STREAM_VOLUME)糜芳。一個(gè)流也可以用另外一個(gè)流的音量設(shè)置飒货,所以需要一個(gè)別名映射。

  • 最大音量
* 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
    };
  • 默認(rèn)音量
* 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中各種設(shè)備的映射不盡相同峭竣,Android中定義了3中設(shè)備DEFAULT塘辅,VOICE,TELEVISION對(duì)應(yīng)的別名映射皆撩。(STREAM_VOLUME_ALIAS_VOICE)扣墩。

STREAM_VOLUME_ALIAS_VOICE對(duì)應(yīng)能處理能處理Voice的設(shè)備,比如電話扛吞,請(qǐng)映射如下:

* 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對(duì)應(yīng)電視呻惕,機(jī)頂盒等。

* 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其實(shí)和Voice映射是一樣的滥比,對(duì)應(yīng)其他的設(shè)備亚脆,比如平板等。

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

映射關(guān)系如下:

流代號(hào) 流類型 默認(rèn)音量 最大音量 最小音量 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

所以在手機(jī)設(shè)備中盲泛,從上表來(lái)看,我們能調(diào)節(jié)的其實(shí)就5個(gè)音量。當(dāng)你想調(diào)節(jié)STREAM_SYSTEM漾根,STREAM_NOTIFICATION等流類型的音量時(shí)址遇,實(shí)際上是調(diào)節(jié)了STREAM_RING的音量。當(dāng)前可控的流類型可以通過下表更直觀地顯示:

流類型 默認(rèn)音量 最大音量 意義
STREAM_VOICE_CALL 4 5 通話音量
STREAM_RING 5 7 響鈴,通知,系統(tǒng)默認(rèn)音等
STREAM_MUSIC 11 15 多媒體音量
STREAM_ALARM 6 7 鬧鐘音量
STREAM_BLUETOOTH_SCO 7 15 藍(lán)牙音量

音量鍵的處理流程

音量鍵是常用的調(diào)節(jié)音量的方式,調(diào)節(jié)音量時(shí)棚亩,是調(diào)節(jié)音量節(jié),比如媒體音STREAM_MUSIC的最大音量為15虏杰,其實(shí)是15節(jié)讥蟆,你也可以理解為16節(jié),因?yàn)檫€有0.

音量鍵的處理

Android設(shè)置了3個(gè)音量處理鍵纺阔,音量加瘸彤,音量減和靜音。3個(gè)音量鍵通過PhoneWindowManager進(jìn)行處理笛钝。

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

音量的加減都是一節(jié)一節(jié)的調(diào)節(jié)的质况,比如最大音量為 7,表示有7節(jié)玻靡,加上0结榄,就是8節(jié)。

3音量按鍵對(duì)應(yīng)如下:

按鍵 AudioService 對(duì)應(yīng)的操作 實(shí)際意義
KEYCODE_VOLUME_UP ADJUST_RAISE 音量加
KEYCODE_VOLUME_DOWN ADJUST_LOWER 音量減
KEYCODE_VOLUME_MUTE ADJUST_TOGGLE_MUTE 靜音按鈕

ADJUST_TOGGLE_MUTE是靜音按鈕囤捻,如果現(xiàn)在是靜音的臼朗,那么將切為非靜音;如果是非靜音的蝎土,那么將設(shè)置為靜音视哑。相關(guān)的還有ADJUST_MUTE和ADJUST_UNMUTE。

此外誊涯,AudioService相關(guān)的還有ADJUST_SAME挡毅,這個(gè)只是彈出調(diào)節(jié)音量的框,不去修改音量暴构。

adjustSuggestedStreamVolume通過Binder調(diào)到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調(diào)同名函數(shù)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 表示強(qiáng)制控制某一種流類型,這里應(yīng)該很少走
  • getActiveStreamType獲取要控制的流類型丹壕,從Binder那邊傳過來(lái)的 值為USE_DEFAULT_STREAM_TYPE庆械,getActiveStreamType大概流程如下:
獲取Stream的類型
  • 獲取映射別名 resolvedStream
  • 對(duì)于通知音或鈴聲STREAM_RING薇溃,先顯示調(diào)節(jié)音量的對(duì)話框菌赖,這個(gè)在AudioService的內(nèi)部類VolumeController中處理。
  • 最后沐序,通過adjustStreamVolume函數(shù)設(shè)置音量

adjustStreamVolume函數(shù)

adjustStreamVolume函數(shù)比較長(zhǎng)琉用,我們分段來(lái)看

* 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];
  • 獲取需要設(shè)置音量類型的映射別名streamTypeAlias
  • 獲取音量類型對(duì)應(yīng)的音量信息狀態(tài) streamState堕绩,VolumeStreamState 音量控制的核心,映射后的每種音量都有各自的VolumeStreamState邑时,它保存了一個(gè)流類型所有的音量信息奴紧。
* 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;
  • 獲取流類型對(duì)應(yīng)的設(shè)備
    流各自的VolumeStreamState通過observeDevicesForStream_syncVSS去獲取對(duì)應(yīng)的device,observeDevicesForStream_syncVSS的獲取到的是一個(gè)device列表晶丘,最終是通過AudioSystem去native獲取的黍氮。

再?gòu)墨@取的device列表中進(jìn)行二次選擇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;
    }

繼續(xù)看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) {
        ... ...
        // 絕對(duì)音量控制
        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));
        }
        // 權(quán)限處理
        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;
            }
        }
        // 如果不設(shè)置音量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);
                    }
                }
            }
            // 靜音設(shè)置
            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);
                        }
                    }
                } // 設(shè)置到底層
                sendMsg(mAudioHandler,
                        MSG_SET_DEVICE_VOLUME,
                        SENDMSG_QUEUE,
                        device,
                        0,
                        streamState,
                        0);
            }

            // 是否需要設(shè)置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);
    }

在上面這段代碼中,需要注意下面幾個(gè)關(guān)鍵的流程:

  • 計(jì)算step
    如果不是音樂類型滚秩,且固定音量专执,那么音量調(diào)節(jié)將轉(zhuǎn)換,轉(zhuǎn)換的算法為:
    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;
  • 設(shè)置音量
    首先處理靜音,處理安全音量桐腌,最后再處理音量拄显。

音量首先保存在每個(gè)流類型的VolumeStreamState中:

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

在通過 MSG_SET_DEVICE_VOLUME 消息設(shè)置到底層。AudioHandler通過setDeviceVolume處理MSG_SET_DEVICE_VOLUME消息哩掺。最后是通過AudioSystem的native接口設(shè)置到native凿叠。

  • 通知音量更改
    最后,通過sendVolumeUpdate廣播整個(gè)系統(tǒng)嚼吞,通知整個(gè)系統(tǒng)音量修改盒件。

看完代碼,我們來(lái)看看Java層的時(shí)序吧~


AudioService設(shè)置按鍵音量的過程
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末舱禽,一起剝皮案震驚了整個(gè)濱河市炒刁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌誊稚,老刑警劉巖翔始,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異里伯,居然都是意外死亡城瞎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門疾瓮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)脖镀,“玉大人,你說我怎么就攤上這事狼电⊙鸦遥” “怎么了弦蹂?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)强窖。 經(jīng)常有香客問我凸椿,道長(zhǎng),這世上最難降的妖魔是什么翅溺? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任脑漫,我火速辦了婚禮,結(jié)果婚禮上咙崎,老公的妹妹穿的比我還像新娘窿撬。我一直安慰自己,他們只是感情好叙凡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布劈伴。 她就那樣靜靜地躺著,像睡著了一般握爷。 火紅的嫁衣襯著肌膚如雪跛璧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天新啼,我揣著相機(jī)與錄音追城,去河邊找鬼。 笑死燥撞,一個(gè)胖子當(dāng)著我的面吹牛座柱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播物舒,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼色洞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了冠胯?” 一聲冷哼從身側(cè)響起火诸,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎荠察,沒想到半個(gè)月后置蜀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悉盆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年盯荤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焕盟。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秋秤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情航缀,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布堰怨,位于F島的核電站芥玉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏备图。R本人自食惡果不足惜灿巧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望揽涮。 院中可真熱鬧抠藕,春花似錦、人聲如沸盾似。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至村刨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嵌牺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工逆粹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人僻弹。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像奢方,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蟋字,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容