Android音量相关知识总结(三)调节音量流程梳理
一、在响铃过程中,按音量键或者电源键停止铃声
二、调节音量流程梳理
三、相关文章推荐
一、在响铃过程中,按音量键或者电源键停止铃声
这里的逻辑一般都没有修改的,这里我们只做了解。首先会被PhoneWindowManager.java方法接受到。
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { //判断是否按下按键 final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;.... //音量上下键和静音键 case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_MUTE:{.... if (down) { sendSystemKeyToStatusBarAsync(event.getKeyCode()); TelecomManager telecomManager = getTelecommService(); if (telecomManager != null) { if (telecomManager.isRinging()) { // If an incoming call is ringing, either VOLUME key means // "silence ringer". We handle these keys here, rather than // in the InCallScreen, to make sure we'll respond to them // even if the InCallScreen hasn't come to the foreground yet. // Look for the DOWN event here, to agree with the "fallback" // behavior in the InCallScreen. Log.i(TAG, "interceptKeyBeforeQueueing:" + " VOLUME key-down while ringing: Silence ringer!"); // Silence the ringer. (It's safe to call this // even if the ringer has already been silenced.) //静音 telecomManager.silenceRinger(); // And *don't* pass this key thru to the current activity // (which is probably the InCallScreen.) result &= ~ACTION_PASS_TO_USER; break; } }}
按下电源键的接收方法
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) { // Stop ringing or end call if configured to do so when power is pressed. TelecomManager telecomManager = getTelecommService(); boolean hungUp = false; if (telecomManager != null) { if (telecomManager.isRinging()) { // Pressing Power while there's a ringing incoming // call should silence the ringer. telecomManager.silenceRinger(); } else if ((mIncallPowerBehavior & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 && telecomManager.isInCall() && interactive) { // Otherwise, if "Power button ends call" is enabled, // the Power button will hang up any current active call. hungUp = telecomManager.endCall(); } }}
二、调节音量流程梳理
这把我们可能比较熟悉了,在按下音量键的时候会出发下面的方法
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; } }
我们可以看见通过getAudioService().adjustSuggestedStreamVolume 去调用AudioService里面的adjustSuggestedStreamVolume方法。
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()); }
调用同名方法
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, String callingPackage, String caller, int uid) { if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType + ", flags=" + flags + ", caller=" + caller + ", volControlStream=" + mVolumeControlStream + ", userSelect=" + mUserSelectedVolumeControlStream); mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType, direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage) .append("/").append(caller).append(" uid:").append(uid).toString())); final int streamType; if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1 streamType = mVolumeControlStream; } else {//获取要控制的流类型 final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType); final boolean activeForReal; if (maybeActiveStreamType == AudioSystem.STREAM_MUSIC) { activeForReal = isAfMusicActiveRecently(0); } else { activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0); } if (activeForReal || mVolumeControlStream == -1) { streamType = maybeActiveStreamType; } else { streamType = mVolumeControlStream; } } final boolean isMute = isMuteAdjust(direction); ensureValidStreamType(streamType); //先获取到该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; } // For notifications/ring, show the ui before making any adjustments //对于铃声或者通知,先显示调节音量对话框,在处理音量调节 // Don't suppress mute/unmute requests 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); }
知识点来了
1.mUserSelectedVolumeControlStream:这个属性表示,用户是否已通过单击音量进度条选择音量流来更改由音量键控制的音量,如果mVolumeControlStream为-1,那么mUserSelectedVolumeControlStream 为false。其实就是当用户点击了某个音量条,这时再去按下音量加减,这个时候调节的是你点击的那个流类型。
2.getActiveStreamType:获取我们要控制的流类型。
3.mStreamVolumeAlias[streamType]:进行流映射,获取最终需要调整的流类型
4.
1422 int streamTypeAlias = mStreamVolumeAlias[streamType];
1423
1424 VolumeStreamState streamState = mStreamStates[streamTypeAlias];
1425
1426 final int device = getDeviceForStream(streamTypeAlias);
根据streamtype 获取到的device,会不同。 getDeviceForStream()的结果总结如下
话题收回来我们接着探究,上面最后会调用改变音量的方法adjustStreamVolume()。
private void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage, String caller, int uid) { if (mUseFixedVolume) { return; } if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream=" + streamType + ", dir=" + direction + ", flags=" + flags + ", caller=" + caller); ensureValidDirection(direction); ensureValidStreamType(streamType); boolean isMuteAdjust = isMuteAdjust(direction); if (isMuteAdjust && !isStreamAffectedByMute(streamType)) { return; } // use stream type alias here so that streams with same alias have the same behavior, // including with regard to silent mode control (e.g the use of STREAM_RING below and in // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION) //获取需要设置的音量类型的streamTypeAlias xiangzaixiansheng int streamTypeAlias = mStreamVolumeAlias[streamType]; //获取音量类型对应的音量信息状态streamState,映射后的美中音量都有自己的VolumeStreamState,它保存了每种音量类型的所有信息 VolumeStreamState streamState = mStreamStates[streamTypeAlias]; //获取流对应的设备信息 final int device = getDeviceForStream(streamTypeAlias); int aliasIndex = streamState.getIndex(device); boolean adjustVolume = true; int step; // skip a2dp absolute volume control request when the device // is not an a2dp device //绝对音量控制 if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) == 0 && (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. //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; } // reset any pending volume command //清理掉待处理的音量处理指令 synchronized (mSafeMediaVolumeState) { mPendingVolumeCommand = null; } //处理混合音 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 = safeMediaVolumeIndex(device); } 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 either the client forces allowing ringer modes for this adjustment, // or the stream type is one that is affected by ringer modes //响铃模式处理 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || (streamTypeAlias == getUiSoundsStreamType())) { int ringerMode = getRingerModeInternal(); // 如果已经是震动模式,就不要在震动了 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; } } // If the ringermode is suppressing media, prevent changes 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)) { 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); } } } //设置到底层 sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE, device, 0, streamState, 0); } // Check if volume update should be sent to Hdmi system audio. //检查是否需要设置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); }
mIndexMap实际是个数组,不含映射的,记录了device对应的index值.
后面的流程:
后面发送msg:MSG_SET_DEVICE_VOLUME -> handler 接收MSG_SET_DEVICE_VOLUME->setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1) -> mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice); ->AudioSystem.setStreamVolumeIndex(mStreamType, index, device)。
按照上面的流程,我们看一下applyDeviceVolume_syncVSS(streamDevice)这个方法。
后面会调用到Audiosystem.cpp里面的native方法中
后续有时间在探究哈。
相关文章推荐:
Android音频相关(一)插入耳机后调节音量(安全音量)
https://blog.csdn.net/xiangzaixiansheng/article/details/88620146
更多相关文章
- HasMap 与 SparseArray
- mac 系统中Android(安卓)studio的一些快捷键
- AsyncTask、View.post(Runnable)、ViewTreeObserver三种方式总结
- android 文字尺寸(宽高)的测量
- Android(安卓)Drawable Resource学习(二)、BitmapDrawable和Bitmap
- 64位win7下Android(安卓)SDK Manager闪退的解决方法
- Android(安卓)Toast无延时方法
- Android程序有很多Activity,在B中如何关闭整个Android应用程序
- Android下利用zbar类库实现扫一扫