Android 9 按音量键音量调节流程 您所在的位置:网站首页 音量键控制通话音量 Android 9 按音量键音量调节流程

Android 9 按音量键音量调节流程

2023-12-16 02:59| 来源: 网络整理| 查看: 265

参考:https://blog.csdn.net/kehyuanyu/article/details/49153223 https://blog.csdn.net/qq_34787560/article/details/97374073?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2aggregatepagefirst_rank_v2~rank_aggregation-7-97374073.pc_agg_rank_aggregation&utm_term=android+%E9%9F%B3%E9%87%8F%E6%8C%89%E9%94%AE%E6%B5%81%E7%A8%8B&spm=1000.2123.3001.4430

基于android 9平台分析。 在Android系统中,默认的设备(phone等)音量都是分开控制的,这些包括媒体、铃声、闹铃、蓝牙、通话通过音频流来区别不同的音量类型。每种流类型都定义最大音量、最小音量及默认音量,Android 9定了了11中音频流类型:

流类型 //frameworks/base/media/java/android/media/AudioSystem.java 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" }; 最大音量(音量等级) //frameworks/base/services/core/java/com/android/server/audio/AudioService.java /** Maximum volume index values for audio streams */ protected static int[] MAX_STREAM_VOLUME = new int[] { 5, // STREAM_VOICE_CALL 7, // STREAM_SYSTEM 7, // STREAM_RING 15, // STREAM_MUSIC 7, // STREAM_ALARM 7, // STREAM_NOTIFICATION 15, // STREAM_BLUETOOTH_SCO 7, // STREAM_SYSTEM_ENFORCED 15, // STREAM_DTMF 15, // STREAM_TTS 15 // STREAM_ACCESSIBILITY };

根据音量曲线表,一般情况音量等级最大可以设置为100。但是,有些音频音量调节并不经过音箱曲线表,而是直接调用HAL层的set_volume,而HAL层对音量又做了类似“音量曲线”的转换。所有修改音量级别,可能会有以下问题: 1、调至15时音量已经最大,15级以上的音量等级无效。 比如amlogic T972将MAX_STREAM_VOLUME 调整为30等级,HAL层audio_hw.c对音量调节:out_set_volume()–>volume2Ms12DBGain()–>AmplToDb(),因15等级时DB值已经“够大”,再往上调音量变化不明显,修改如下: 在这里插入图片描述 2、调节音量时音量过大导致输出波形失真。 因喇叭性能或功放电路的原因(最好改电路,否则产品声音小),CPU音量输出增益不能太大,否则引起波形失真。比如Mst358在HAL层audio_hw.c对音量调节: 在这里插入图片描述

最小音量 //frameworks/base/services/core/java/com/android/server/audio/AudioService.java /** Minimum volume index values for audio streams */ protected static int[] MIN_STREAM_VOLUME = new int[] { 1, // STREAM_VOICE_CALL 0, // STREAM_SYSTEM 0, // STREAM_RING 0, // STREAM_MUSIC 1, // STREAM_ALARM 0, // STREAM_NOTIFICATION 0, // STREAM_BLUETOOTH_SCO 0, // STREAM_SYSTEM_ENFORCED 0, // STREAM_DTMF 0, // STREAM_TTS 1 // STREAM_ACCESSIBILITY };

设置最小音量的目的是有些音频不能单独设置为静音。

默认音量 //frameworks/base/media/java/android/media/AudioSystem.java public static int[] DEFAULT_STREAM_VOLUME = new int[] { 4, // STREAM_VOICE_CALL 7, // STREAM_SYSTEM 0, // STREAM_RING 5, // STREAM_MUSIC 0, // STREAM_ALARM 5, // STREAM_NOTIFICATION 7, // STREAM_BLUETOOTH_SCO 7, // STREAM_SYSTEM_ENFORCED 5, // STREAM_DTMF 5, // STREAM_TTS 5, // STREAM_ACCESSIBILITY }; 音频流映射StreamAlias

不同设备的映射定义不同,系统中一共定义三种设备的音频流的映射,分别是STREAM_VOLUME_ALIAS_VOICE,STREAM_VOLUME_ALIAS_TELEVISION,STREAM_VOLUME_ALIAS_DEFAULT

StreamAlias存在的意义:流类型别名,某音频流的行为等同于另外一种音频流,可以将它映射为另一种音频流,比如AudioSystem.STREAM_RING用作AudioSystem.STREAM_MUSIC来处理。

StreamAlias实际使用的意义是,Android为了兼容各种设备,定义了尽可能多的音频流。但是,在有些简单的设备中,可能仅有一个喇叭,所以对音频操作没有必要区分音频流。所以通过StreamAlias,在手机和平板上实际上能调节的就是五种音量,在TV和就机顶盒之类设备能调节的仅仅一种音量即music,故如有需求需要统一音量的可以将当前的音频流改为TV类型。 调用AudioSsytem::getPlatformType()可知道系统是手机、平板或TV类型。

//frameworks/base/services/core/java/com/android/server/audio/AudioService.java 1.voice--具有语音功能的设备,电话等 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 }; 2. television--电视机顶盒或投影设备 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 }; 3. 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 }; 音量按键处理流程

在这里插入图片描述 在Android平台上,音量键,主页键(home),都是全局按键,但是主页键是个例外不能被应用所捕获。下面分析一下音量按键的流程,主要从framework层处理开始,至于EventHub 从驱动的/dev/input/event0获取按键信息到上抛属于Android input 系统方面的流程。

系统层接收音量按键

ViewRootImpl.processKeyEvent 处理Activity 上面收到的按键

private int processKeyEvent(QueuedInputEvent q) { final KeyEvent event = (KeyEvent)q.mEvent; if (mUnhandledKeyManager.preViewDispatch(event)) { return FINISH_HANDLED; } // Deliver the key to the view hierarchy. if (mView.dispatchKeyEvent(event)) { return FINISH_HANDLED; } if (shouldDropInputEvent(q)) { return FINISH_NOT_HANDLED; } 。。。。。 }

从中可以看到mView.dispatchKeyEvent(event),完成将按键发送给Activity处理,由于每个Activity都是view的子类,所有这些按键将dispatchKeyEvent传递给onKeyDown:

/** * Called to process key events. You can override this to intercept all * key events before they are dispatched to the window. Be sure to call * this implementation for key events that should be handled normally. * * @param event The key event. * * @return boolean Return true if this event was consumed. */ public boolean dispatchKeyEvent(KeyEvent event) { onUserInteraction(); // Let action bars open menus in response to the menu key prioritized over // the window handling it final int keyCode = event.getKeyCode(); if (keyCode == KeyEvent.KEYCODE_MENU && mActionBar != null && mActionBar.onMenuKeyEvent(event)) { return true; } Window win = getWindow(); if (win.superDispatchKeyEvent(event)) { return true; } View decor = mDecor; if (decor == null) decor = win.getDecorView(); return event.dispatch(this, decor != null ? decor.getKeyDispatcherState() : null, this); }

注意这个方法可以被子类覆盖。 win.superDispatchKeyEvent()不处理音量键,调用根View的dispatchKeyEvent,进而调用ViewGroup的dispatchKeyEvent,如果都没处理,则调用View的dispatchKeyEvent:

public boolean dispatchKeyEvent(KeyEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onKeyEvent(event, 0); } // Give any attached key listener a first crack at the event. //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) { return true; } if (event.dispatch(this, mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null, this)) { return true; } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; }

通过event.dispatch进一步分发

//framework/base/core/java/android/view/KeyEvent.java public final boolean dispatch(Callback receiver, DispatcherState state, Object target) { switch (mAction) { case ACTION_DOWN: { mFlags &= ~FLAG_START_TRACKING; if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state + ": " + this); boolean res = receiver.onKeyDown(mKeyCode, this); if (state != null) { if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) { if (DEBUG) Log.v(TAG, " Start tracking!"); state.startTracking(this, target); } else if (isLongPress() && state.isTracking(this)) { try { if (receiver.onKeyLongPress(mKeyCode, this)) { if (DEBUG) Log.v(TAG, " Clear from long press!"); state.performedLongPress(this); res = true; } } catch (AbstractMethodError e) { } } } return res; } ...... }

上面只关注了ACTION_DOWN的处理。KeyEvent.dispatch通过receiver.onKeyDown将最终的按键消息发送给当前的Activity,而receiver即为KeyEvent.Callback的实现类(View的子类等等),至此如果上面上传应用处理完了就会返回,如果没有处理就会流向mFallbackEventHandler.dispatchKeyEvent(event),其实mFallbackEventHandler就是PhoneFallbackEventHandler,接着看 PhoneFallbackEventHandler.dispatchKeyEvent的处理流程

//framework/base/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java public boolean dispatchKeyEvent(KeyEvent event) { final int action = event.getAction(); final int keyCode = event.getKeyCode(); if (action == KeyEvent.ACTION_DOWN) { return onKeyDown(keyCode, event); } else { return onKeyUp(keyCode, event); } }

进入onKeyDown

boolean onKeyDown(int keyCode, KeyEvent event) { /* **************************************************************************** * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES. * See the comment in PhoneWindow.onKeyDown * ****************************************************************************/ final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_MUTE: { handleVolumeKeyEvent(event); return true; } 。。。。。 } private void handleVolumeKeyEvent(KeyEvent keyEvent) { getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(keyEvent, AudioManager.USE_DEFAULT_STREAM_TYPE); }

调用MeidaSessionManager::dispatchVolumeKeyEventAsSystemService()–>MediaSessionService::dispatchVolumeKeyEvent()

/*将音量按钮事件分派给其中一个已注册的侦听器。 如果有一个音量键长按侦听器,并且没有活动的全局优先级会话,长按将被发送到长按侦听器,而不是调整音量。 如果没有注册长按监听器、没有活动的全局优先级会话,则进行音量调节*/ @Override public void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) { ...... try { synchronized (mLock) { //如果没有注册长按监听器,则调用dispatchVolumeKeyEventLocked进行音量调节。 if (isGlobalPriorityActiveLocked() || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, stream, musicOnly); } else { // TODO: Consider the case when both volume up and down keys are pressed // at the same time. if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { if (keyEvent.getRepeatCount() == 0) { // Keeps the copy of the KeyEvent because it can be reused. mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = KeyEvent.obtain(keyEvent); mCurrentFullUserRecord.mInitialDownVolumeStream = stream; mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly; mHandler.sendMessageDelayed( mHandler.obtainMessage( MessageHandler.MSG_VOLUME_INITIAL_DOWN, mCurrentFullUserRecord.mFullUserId, 0), mLongPressTimeout); } if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) { mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) { dispatchVolumeKeyLongPressLocked( mCurrentFullUserRecord.mInitialDownVolumeKeyEvent); // Mark that the key is already handled. mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null; } dispatchVolumeKeyLongPressLocked(keyEvent); } } else { // if up ...... }

继续dispatchVolumeKeyEventLocked()–>dispatchAdjustVolumeLocked()

private void dispatchAdjustVolumeLocked(String packageName, int pid, int uid, boolean asSystemService, int suggestedStream, int direction, int flags) { MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); boolean preferSuggestedStream = false; if (isValidLocalStreamType(suggestedStream) && AudioSystem.isStreamActive(suggestedStream, 0)) { preferSuggestedStream = true; } if (session == null || preferSuggestedStream) { // Execute mAudioService.adjustSuggestedStreamVolume() on // handler thread of MediaSessionService. // This will release the MediaSessionService.mLock sooner and avoid // a potential deadlock between MediaSessionService.mLock and // ActivityManagerService lock. mHandler.post(new Runnable() { @Override public void run() { try { String packageName = getContext().getOpPackageName(); mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags, packageName, TAG); } } }); } else { session.adjustVolume(packageName, pid, uid, null, asSystemService, direction, flags, true); } }

两种情况,一种调用mAudioService.adjustSuggestedStreamVolume(),一种调用session.adjustVolume()。这里以adjustSuggestedStreamVolume()为例。

AudioService音量控制流程

adjustSuggestedStreamVolume 过渡到adjustStreamVolume,进入音量设置的主要流程,主要对流类型,设备,声音设备状态,步进大小进行判断处理,另外蓝牙设备音量和主设备音量进行了控制,最后通过mVolumePanel刷新界面音量显示,并且广播通过上层应用。

protected void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage, String caller, int uid) { 。。。。。 if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) { mAudioHandler.removeMessages(MSG_UNMUTE_STREAM); 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 if (!(readCameraSoundForced() && (mStreamStates[stream].getStreamType() == AudioSystem.STREAM_SYSTEM_ENFORCED))) { mStreamStates[stream].mute(state); } } } } else if ((direction == AudioManager.ADJUST_RAISE) && !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex); mVolumeController.postDisplaySafeVolumeWarning(flags); } else if (streamState.adjustIndex(direction * step, device, caller) || streamState.mIsMuted) { // Post message to set system volume (it in turn will post a // message to persist). 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); } } } //发送MSG_SET_DEVICE_VOLUME消息去设置系统音量,在handleMessage()被处理 sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE, device, 0, streamState, 0); } int newIndex = mStreamStates[streamType].getIndex(device); // 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.setAvrcpAbsoluteVolume(newIndex / 10); } } } // Check if volume update should be send to Hearing Aid if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) { setHearingAidVolume(newIndex, streamType); } // Check if volume update should be sent to Hdmi system audio. //与HDMI输出相关 if (streamTypeAlias == AudioSystem.STREAM_MUSIC) { setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags); } if (mHdmiManager != null) { ...... } } int index = mStreamStates[streamType].getIndex(device); //UI更新系统音量 sendVolumeUpdate(streamType, oldIndex, index, flags); } 蓝牙音量的控制

由上可知,如果当前连接了蓝牙也将对音量进行控制,mA2dp.adjustAvrcpAbsoluteVolume,暂不分析。

音频处理设置

音频处理由AudioHandler来进行,adjustStreamVolume做完相关处理后,通过sendMsg发送音量变化消息MSG_SET_DEVICE_VOLUME进入AudioHandler.handleMessage调用AudioHandler.setDeviceVolume

private void setDeviceVolume(VolumeStreamState streamState, int device) { synchronized (VolumeStreamState.class) { // Apply volume streamState.applyDeviceVolume_syncVSS(device); // Apply change to all streams using this one as alias int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { if (streamType != streamState.mStreamType && mStreamVolumeAlias[streamType] == streamState.mStreamType) { // Make sure volume is also maxed out on A2DP device for aliased stream // that may have a different device selected int streamDevice = getDeviceForStream(streamType); if ((device != streamDevice) && mAvrcpAbsVolSupported && ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) { mStreamStates[streamType].applyDeviceVolume_syncVSS(device); } mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice); } } } // Post a persist volume msg sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, SENDMSG_QUEUE, device, 0, streamState, PERSIST_DELAY); }

VolumeStreamState.applyDeviceVolume_syncVSS设置设备音量

// must be called while synchronized VolumeStreamState.class public void applyDeviceVolume_syncVSS(int device) { int index; if (mIsMuted) { index = 0; } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported) { index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10); } else if ((device & mFullVolumeDevices) != 0) { index = (mIndexMax + 5)/10; } else if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) { index = (mIndexMax + 5)/10; } else { index = (getIndex(device) + 5)/10; } AudioSystem.setStreamVolumeIndex(mStreamType, index, device); }

接着发送MSG_PERSIST_VOLUME消息通过handleMessage进入persistVolume,最终调用System.putIntForUser将用户设置的内容设置到Settings.system中。

AudioSystem调节音量

applyDeviceVolume处理完,AudioSystem就开始接着往下设置setStreamVolumeIndex,通过JNI调用到AudioSystem.cpp中setStreamVolumeIndex()。

status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream, int index, audio_devices_t device) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; return aps->setStreamVolumeIndex(stream, index, device); }

获取去音频策略服务(AudioPolicyService.cpp),进行设置 AudioPolicyService::setStreamVolumeIndex()–>AudioPolicyManager::setStreamVolumeIndex()



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有