手机 网站内 搜索,一个优秀的个人网站,张斌网站建设,理聪网营销型网站建设基于android 9平台分析。 在Android系统中#xff0c;默认的设备(phone等)音量都是分开控制的#xff0c;这些包括媒体、铃声、闹铃、蓝牙、通话通过音频流来区别不同的音量类型。每种流类型都定义最大音量、最小音量及默认音量#xff0c;Android 9定了了11中音频流类型默认的设备(phone等)音量都是分开控制的这些包括媒体、铃声、闹铃、蓝牙、通话通过音频流来区别不同的音量类型。每种流类型都定义最大音量、最小音量及默认音量Android 9定了了11中音频流类型
流类型
//frameworks/base/media/java/android/media/AudioSystem.javapublic 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_CALL7, // STREAM_SYSTEM7, // STREAM_RING15, // STREAM_MUSIC7, // STREAM_ALARM7, // STREAM_NOTIFICATION15, // STREAM_BLUETOOTH_SCO7, // STREAM_SYSTEM_ENFORCED15, // STREAM_DTMF15, // STREAM_TTS15 // 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_CALL0, // STREAM_SYSTEM0, // STREAM_RING0, // STREAM_MUSIC1, // STREAM_ALARM0, // STREAM_NOTIFICATION0, // STREAM_BLUETOOTH_SCO0, // STREAM_SYSTEM_ENFORCED0, // STREAM_DTMF0, // STREAM_TTS1 // STREAM_ACCESSIBILITY};设置最小音量的目的是有些音频不能单独设置为静音。
默认音量
//frameworks/base/media/java/android/media/AudioSystem.javapublic static int[] DEFAULT_STREAM_VOLUME new int[] {4, // STREAM_VOICE_CALL7, // STREAM_SYSTEM0, // STREAM_RING5, // STREAM_MUSIC0, // STREAM_ALARM5, // STREAM_NOTIFICATION7, // STREAM_BLUETOOTH_SCO7, // STREAM_SYSTEM_ENFORCED5, // STREAM_DTMF5, // STREAM_TTS5, // STREAM_ACCESSIBILITY};音频流映射StreamAlias
不同设备的映射定义不同系统中一共定义三种设备的音频流的映射分别是STREAM_VOLUME_ALIAS_VOICESTREAM_VOLUME_ALIAS_TELEVISIONSTREAM_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_CALLAudioSystem.STREAM_RING, // STREAM_SYSTEMAudioSystem.STREAM_RING, // STREAM_RINGAudioSystem.STREAM_MUSIC, // STREAM_MUSICAudioSystem.STREAM_ALARM, // STREAM_ALARMAudioSystem.STREAM_RING, // STREAM_NOTIFICATIONAudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCOAudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCEDAudioSystem.STREAM_RING, // STREAM_DTMFAudioSystem.STREAM_MUSIC // STREAM_TTS
};
2. television--电视机顶盒或投影设备
private final int[] STREAM_VOLUME_ALIAS_TELEVISION new int[] {AudioSystem.STREAM_MUSIC, // STREAM_VOICE_CALLAudioSystem.STREAM_MUSIC, // STREAM_SYSTEMAudioSystem.STREAM_MUSIC, // STREAM_RINGAudioSystem.STREAM_MUSIC, // STREAM_MUSICAudioSystem.STREAM_MUSIC, // STREAM_ALARMAudioSystem.STREAM_MUSIC, // STREAM_NOTIFICATIONAudioSystem.STREAM_MUSIC, // STREAM_BLUETOOTH_SCOAudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCEDAudioSystem.STREAM_MUSIC, // STREAM_DTMFAudioSystem.STREAM_MUSIC // STREAM_TTS
};
3. default--平板之类的设备
private final int[] STREAM_VOLUME_ALIAS_DEFAULT new int[] {AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALLAudioSystem.STREAM_RING, // STREAM_SYSTEMAudioSystem.STREAM_RING, // STREAM_RINGAudioSystem.STREAM_MUSIC, // STREAM_MUSICAudioSystem.STREAM_ALARM, // STREAM_ALARMAudioSystem.STREAM_RING, // STREAM_NOTIFICATIONAudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCOAudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCEDAudioSystem.STREAM_RING, // STREAM_DTMFAudioSystem.STREAM_MUSIC // STREAM_TTS
};
音量按键处理流程 在Android平台上音量键主页键(home),都是全局按键但是主页键是个例外不能被应用所捕获。
下面分析一下音量按键的流程主要从framework层处理开始至于EventHub 从驱动的/dev/input/event0获取按键信息到上抛属于Android input 系统方面的流程。
framework层接收音量按键
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 itfinal 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 SimplifiableIfStatementListenerInfo 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.javapublic 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 (mFlagsFLAG_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.javapublic 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() /*将音量按钮事件分派给其中一个已注册的侦听器。如果有一个音量键长按侦听器并且没有活动的全局优先级会话长按将被发送到长按侦听器而不是调整音量。如果没有注册长按监听器、没有活动的全局优先级会话则进行音量调节*/Overridepublic 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() {Overridepublic 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 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)) {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 mutedif (direction AudioManager.ADJUST_RAISE) {// unmute immediately for volume upstreamState.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 Aidif ((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 volumestreamState.applyDeviceVolume_syncVSS(device);// Apply change to all streams using this one as aliasint 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 selectedint 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 msgsendMsg(mAudioHandler,MSG_PERSIST_VOLUME,SENDMSG_QUEUE,device,0,streamState,PERSIST_DELAY);}
VolumeStreamState.applyDeviceVolume_syncVSS设置设备音量
// must be called while synchronized VolumeStreamState.classpublic 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 spIAudioPolicyService aps AudioSystem::get_audio_policy_service();if (aps 0) return PERMISSION_DENIED;return aps-setStreamVolumeIndex(stream, index, device);
}
获取去音频策略服务(AudioPolicyService.cpp)进行设置 AudioPolicyService::setStreamVolumeIndex()–AudioPolicyManager::setStreamVolumeIndex()。