日志打印MediaSessionService相关信息:
10-07 19:11:28.967004 1578 3705 D MediaSessionService: dispatchVolumeKeyEvent, pkg=com.ss.android.ugc.aweme, opPkg=com.ss.android.ugc.aweme, pid=32083, uid=10219, asSystem=true, event=KeyEvent { action=ACTION_UP, keyCode=KEYCODE_VOLUME_UP, scanCode=115, metaState=0, flags=0x8, repeatCount=0, eventTime=369450945499000, downTime=369450256419000, deviceId=3, source=0x101, displayId=-1 }, stream=-2147483648, musicOnly=false
10-07 19:11:28.967339 1578 3705 D MediaSessionService: Adjusting suggestedStream=-2147483648 by 0. flags=4116, preferSuggestedStream=false, session=null
流程走到了 MediaSessionService.dispatchAdjustVolumeLocked() 方法内:
// system/frameworks/base/services/core/java/com/android/server/media/MediaSessionService.java
private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
int uid, boolean asSystemService, int suggestedStream, int direction, int flags,
boolean musicOnly) {
MediaSessionRecordImpl session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
: mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
boolean preferSuggestedStream = false;
if (isValidLocalStreamType(suggestedStream)
&& AudioSystem.isStreamActive(suggestedStream, 0)) {
preferSuggestedStream = true;
}
if (session == null || preferSuggestedStream) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Adjusting suggestedStream=" + suggestedStream + " by " + direction
+ ". flags=" + flags + ", preferSuggestedStream="
+ preferSuggestedStream + ", session=" + session);
}
if (musicOnly && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Nothing is playing on the music stream. Skipping volume event,"
+ " flags=" + flags);
}
return;
}
// 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() {
final String callingOpPackageName;
final int callingUid;
final int callingPid;
if (asSystemService) {
callingOpPackageName = mContext.getOpPackageName();
callingUid = Process.myUid();
callingPid = Process.myPid();
} else {
callingOpPackageName = opPackageName;
callingUid = uid;
callingPid = pid;
}
try {
mAudioManager.adjustSuggestedStreamVolumeForUid(suggestedStream,
direction, flags, callingOpPackageName, callingUid, callingPid,
getContext().getApplicationInfo().targetSdkVersion);
} catch (SecurityException | IllegalArgumentException e) {
Log.e(TAG, "Cannot adjust volume: direction=" + direction
+ ", suggestedStream=" + suggestedStream + ", flags=" + flags
+ ", packageName=" + packageName + ", uid=" + uid
+ ", asSystemService=" + asSystemService, e);
}
}
});
} else {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
+ flags + ", suggestedStream=" + suggestedStream
+ ", preferSuggestedStream=" + preferSuggestedStream);
}
session.adjustVolume(packageName, opPackageName, pid, uid, asSystemService,
direction, flags, true);
}
}
通过日志结合代码分析,调用 mAudioManager.adjustSuggestedStreamVolumeForUid 方法:
// system/frameworks/base/media/java/android/media/AudioManager.java
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction,
@SystemVolumeFlags int flags,
@NonNull String packageName, int uid, int pid, int targetSdkVersion) {
try {
getService().adjustSuggestedStreamVolumeForUid(suggestedStreamType, direction, flags,
packageName, uid, pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
通过 AIDL 调用 AudioService.adjustSuggestedStreamVolumeForUid
// system/frameworks/base/services/core/java/com/android/server/audio/AudioService.java
/** @see AudioManager#adjustSuggestedStreamVolumeForUid(int, int, int, String, int, int, int) */
@Override
public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
@NonNull String packageName, int uid, int pid, UserHandle userHandle,
int targetSdkVersion) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Should only be called from system process");
}
// direction and stream type swap here because the public
// adjustSuggested has a different order than the other methods.
adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName,
uid, pid, hasAudioSettingsPermission(uid, pid),
AudioDeviceVolumeManager.ADJUST_MODE_NORMAL);
}
最后调用到 AudioService.adjustSuggestedStreamVolume 方法,即从输入子系统进入到音频子系统。
由于该日志属于 CFC 版本,缺少AudioService日志,导致无法分析出问题发生时是否走到 adjustStreamVolume() 方法内部,该方法真正实现了系统对音量键事件触发的音量调整。
从代码上来看,必然会调用 adjustSuggestedStreamVolume() -> adjustStreamVolume()。
/** All callers come from platform apps/system server, so no attribution tag is needed */
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller, int uid, int pid, boolean hasModifyAudioSettings,
int keyEventMode) {
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType
+ ", flags=" + flags + ", caller=" + caller
+ ", volControlStream=" + mVolumeControlStream
+ ", userSelect=" + mUserSelectedVolumeControlStream);
if (direction != AudioManager.ADJUST_SAME) {
sVolumeLogger.enqueue(
new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType,
direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
.append("/").append(caller).append(" uid:").append(uid).toString()));
}
boolean hasExternalVolumeController = notifyExternalVolumeController(direction);
new MediaMetrics.Item(mMetricsId + "adjustSuggestedStreamVolume")
.setUid(Binder.getCallingUid())
.set(MediaMetrics.Property.CALLING_PACKAGE, callingPackage)
.set(MediaMetrics.Property.CLIENT_NAME, caller)
.set(MediaMetrics.Property.DIRECTION, direction > 0
? MediaMetrics.Value.UP : MediaMetrics.Value.DOWN)
.set(MediaMetrics.Property.EXTERNAL, hasExternalVolumeController
? MediaMetrics.Value.YES : MediaMetrics.Value.NO)
.set(MediaMetrics.Property.FLAGS, flags)
.record();
if (hasExternalVolumeController) {
return;
}
final int streamType;
synchronized (mForceControlStreamLock) {
// Request lock in case mVolumeControlStream is changed by other thread.
if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
streamType = mVolumeControlStream;
} else {
// TODO discard activity on a muted stream?
final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
final boolean activeForReal;
if (maybeActiveStreamType == AudioSystem.STREAM_RING
|| maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
} else {
activeForReal = mAudioSystem.isStreamActive(maybeActiveStreamType, 0);
}
if (activeForReal || mVolumeControlStream == -1) {
streamType = maybeActiveStreamType;
} else {
streamType = mVolumeControlStream;
}
}
}
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;
}
// For notifications/ring, show the ui before making any adjustments
// Don't suppress mute/unmute requests
// Don't suppress adjustments for single volume device
if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)
&& !mIsSingleVolume) {
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, pid,
null, hasModifyAudioSettings, keyEventMode);
}
调用 adjustStreamVolume() 方法调整音量大小
protected void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid, int pid, String attributionTag,
boolean hasModifyAudioSettings, int keyEventMode) {
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;
}
// If adjust is mute and the stream is STREAM_VOICE_CALL or STREAM_BLUETOOTH_SCO, make sure
// that the calling app have the MODIFY_PHONE_STATE permission.
if (isMuteAdjust &&
(streamType == AudioSystem.STREAM_VOICE_CALL ||
streamType == AudioSystem.STREAM_BLUETOOTH_SCO) &&
mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
}
// If the stream is STREAM_ASSISTANT,
// make sure that the calling app have the MODIFY_AUDIO_ROUTING permission.
if (streamType == AudioSystem.STREAM_ASSISTANT &&
mContext.checkPermission(
android.Manifest.permission.MODIFY_AUDIO_ROUTING, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
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)
int streamTypeAlias = mStreamVolumeAlias[streamType];
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 neither an a2dp device nor BLE device
if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
&& !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))
&& (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.
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
}
// validate calling package and app op
if (!checkNoteAppOp(
STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage, attributionTag)) {
return;
}
mSoundDoseHelper.invalidatPendingVolumeCommand();
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
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
step = mSoundDoseHelper.getSafeMediaVolumeIndex(device);
if (step < 0) {
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 = rescaleStep(10, streamType, streamTypeAlias);
}
// If either the client forces allowing ringer modes for this adjustment,
// or stream is used for UI sonification
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
(isUiSoundsStreamType(streamTypeAlias))) {
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;
}
} else if (isStreamMutedByRingerOrZenMode(streamTypeAlias) && streamState.mIsMuted) {
// if the stream is currently muted streams by ringer/zen mode
// then it cannot be unmuted (without FLAG_ALLOW_RINGER_MODES) with an unmute or raise
if (direction == AudioManager.ADJUST_TOGGLE_MUTE
|| direction == AudioManager.ADJUST_UNMUTE
|| direction == AudioManager.ADJUST_RAISE) {
adjustVolume = false;
}
}
// If the ringer mode or zen is muting the stream, do not change stream unless
// it'll cause us to exit dnd
if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
adjustVolume = false;
}
int oldIndex = mStreamStates[streamType].getIndex(device);
// Check if the volume adjustment should be handled by an absolute volume controller instead
if (isAbsoluteVolumeDevice(device)
&& (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
if (info.mHandlesVolumeAdjustment) {
dispatchAbsoluteVolumeAdjusted(streamType, info, oldIndex, direction,
keyEventMode);
return;
}
}
if (adjustVolume && (direction != AudioManager.ADJUST_SAME)
&& (keyEventMode != AudioDeviceVolumeManager.ADJUST_MODE_END)) {
mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);
if (isMuteAdjust && !mFullVolumeDevices.contains(device)) {
boolean state;
if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
state = !streamState.mIsMuted;
} else {
state = direction == AudioManager.ADJUST_MUTE;
}
muteAliasStreams(streamTypeAlias, state);
} else if ((direction == AudioManager.ADJUST_RAISE)
&& mSoundDoseHelper.raiseVolumeDisplaySafeMediaVolume(streamTypeAlias,
aliasIndex + step, device, flags)) {
Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
} else if (!isFullVolumeDevice(device)
&& (streamState.adjustIndex(direction * step, device, caller,
hasModifyAudioSettings)
|| 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
muteAliasStreams(streamTypeAlias, 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);
}
int newIndex = mStreamStates[streamType].getIndex(device);
// Check if volume update should be send to AVRCP
if (streamTypeAlias == AudioSystem.STREAM_MUSIC
&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
Log.d(TAG, "adjustSreamVolume: postSetAvrcpAbsoluteVolumeIndex index="
+ newIndex + "stream=" + streamType);
}
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);
} else if (isAbsoluteVolumeDevice(device)
&& (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
dispatchAbsoluteVolumeChanged(streamType, info, newIndex);
}
if (AudioSystem.isLeAudioDeviceType(device)
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
int nowStreamType = getBluetoothContextualVolumeStream();
if (streamType == nowStreamType ||
(streamType == AudioSystem.STREAM_MUSIC
&& nowStreamType == AudioSystem.STREAM_VOICE_CALL
&& direction == AudioManager.ADJUST_UNMUTE)) {
streamType = nowStreamType;
newIndex = mStreamStates[streamType].getIndex(device);
if (DEBUG_VOL) {
Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+ newIndex + " stream=" + streamType);
}
mDeviceBroker.postSetLeAudioVolumeIndex(newIndex,
mStreamStates[streamType].getMaxIndex(), streamType);
}
}
// Check if volume update should be send to Hearing Aid
if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
// only modify the hearing aid attenuation when the stream to modify matches
// the one expected by the hearing aid
if (streamType == getBluetoothContextualVolumeStream()) {
if (DEBUG_VOL) {
Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="
+ newIndex + " stream=" + streamType);
}
mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
}
}
}
final int newIndex = mStreamStates[streamType].getIndex(device);
if (adjustVolume) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
// At most one of mHdmiPlaybackClient and mHdmiTvClient should be non-null
HdmiClient fullVolumeHdmiClient = mHdmiPlaybackClient;
if (mHdmiTvClient != null) {
fullVolumeHdmiClient = mHdmiTvClient;
}
if (fullVolumeHdmiClient != null
&& mHdmiCecVolumeControlEnabled
&& streamTypeAlias == AudioSystem.STREAM_MUSIC
// vol change on a full volume device
&& isFullVolumeDevice(device)) {
int keyCode = KeyEvent.KEYCODE_UNKNOWN;
switch (direction) {
case AudioManager.ADJUST_RAISE:
keyCode = KeyEvent.KEYCODE_VOLUME_UP;
break;
case AudioManager.ADJUST_LOWER:
keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;
break;
case AudioManager.ADJUST_TOGGLE_MUTE:
case AudioManager.ADJUST_MUTE:
case AudioManager.ADJUST_UNMUTE:
// Many CEC devices only support toggle mute. Therefore, we send the
// same keycode for all three mute options.
keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;
break;
default:
break;
}
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
final long ident = Binder.clearCallingIdentity();
try {
switch (keyEventMode) {
case AudioDeviceVolumeManager.ADJUST_MODE_NORMAL:
fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);
fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);
break;
case AudioDeviceVolumeManager.ADJUST_MODE_START:
fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);
break;
case AudioDeviceVolumeManager.ADJUST_MODE_END:
fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);
break;
default:
Log.e(TAG, "Invalid keyEventMode " + keyEventMode);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
if (streamTypeAlias == AudioSystem.STREAM_MUSIC
&& (oldIndex != newIndex || isMuteAdjust)) {
maybeSendSystemAudioStatusCommand(isMuteAdjust);
}
}
}
}
sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);
if (oldIndex != newIndex) {
setRescaleStreamVolume(streamType, device, newIndex);
}
}
=============== 待更新 ===============