使用案例:
mAudioManager.adjustVolume(ADJUST_RAISE, 0);
mAudioManager.adjustSuggestedStreamVolume( ADJUST_LOWER, USE_DEFAULT_STREAM_TYPE, 0);
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
}
direction:ADJUST_LOWER,ADJUST_RAISE,ADJUST_SAME,ADJUST_MUTE,ADJUST_UNMUTE,ADJUST_TOGGLE_MUTE,flags:0或者FLAG_ACTIVE_MEDIA_ONLY
frameworks/base/media/java/android/media/session/MediaSessionLegacyHelper.java
public void sendAdjustVolumeBy(int suggestedStream, int delta, int flags) {
mSessionManager.dispatchAdjustVolume(suggestedStream, delta, flags);
if (DEBUG) {
Log.d(TAG, "dispatched volume adjustment");
}
}
mSessionManager 是在构造函数中获取的系统服务manager,通过binder 机制调用到MediaSessionService的成员函数
frameworks/base/media/java/android/media/session/MediaSessionManager.java
public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) {
try {
mService.dispatchAdjustVolume(mContext.getPackageName(), mContext.getOpPackageName(),
suggestedStream, direction, flags);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send adjust volume.", e);
}
}
frameworks/base/services/core/java/com/android/server/media/MediaSessionService.java
public void dispatchAdjustVolume(String packageName, String opPackageName,
int suggestedStream, int delta, int flags) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false,
suggestedStream, delta, flags);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
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 ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
&& !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 {
mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
direction, flags, callingOpPackageName, callingUid, callingPid);
} 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);
}
}
mAudioManagerInternal就是在AudioService中 LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
String callingPackage, int uid, int pid) {
final boolean hasModifyAudioSettings =
mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
== PackageManager.PERMISSION_GRANTED;
// direction and stream type swap here because the public
// adjustSuggested has a different order than the other methods.
adjustSuggestedStreamVolume(direction, streamType, flags, callingPackage,
callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL);
}
/** @see AudioManager#adjustVolume(int, int) */
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
boolean hasModifyAudioSettings =
mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
== PackageManager.PERMISSION_GRANTED;
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
}
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller, int uid, 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.log(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) {
///如果应用注册了audiopolicy.setAudioPolicyVolumeCallback,就会走这里
return;
}
final int streamType;
synchronized (mForceControlStreamLock) {
// Request lock in case mVolumeControlStream is changed by other thread.
if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
streamType = mVolumeControlStream;
} else {
final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
final boolean activeForReal;
if (maybeActiveStreamType == AudioSystem.STREAM_RING
|| maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
} else {
activeForReal = AudioSystem.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,
hasModifyAudioSettings, keyEventMode);
}
如上代码:boolean hasExternalVolumeController = notifyExternalVolumeController(direction);当应用自己注册了audiopolicycolumecontroler,这这里就会返回true
private void setExtVolumeController(IAudioPolicyCallback apc) {
if (!mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeKeysInWindowManager)) {
Log.e(TAG, "Cannot set external volume controller: device not set for volume keys" +
" handled in PhoneWindowManager");
return;
}
synchronized (mExtVolumeControllerLock) {
if (mExtVolumeController != null && !mExtVolumeController.asBinder().pingBinder()) {
Log.e(TAG, "Cannot set external volume controller: existing controller");
}
mExtVolumeController = apc;
}
}
private boolean notifyExternalVolumeController(int direction) {
final IAudioPolicyCallback externalVolumeController;
synchronized (mExtVolumeControllerLock) {
externalVolumeController = mExtVolumeController;
}
if (externalVolumeController == null) {
return false;
}
sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE,
direction, 0 /*ignored*/,
externalVolumeController, 0 /*delay*/);
return true;
}
通过binder机制在audioservice进程中发送MSG_NOTIFY_VOL_EVENT
case MSG_NOTIFY_VOL_EVENT:
onNotifyVolumeEvent((IAudioPolicyCallback) msg.obj, msg.arg1);
break;
private void onNotifyVolumeEvent(@NonNull IAudioPolicyCallback apc,
@AudioManager.VolumeAdjustment int direction) {
try {
apc.notifyVolumeAdjust(direction);
} catch(Exception e) {
// nothing we can do about this. Do not log error, too much potential for spam
}
}
frameworks/base/media/java/android/media/audiopolicy/AudioPolicy.java
public void notifyVolumeAdjust(int adjustment) {
sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment);
if (DEBUG) {
Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
}
}
通过binder机制在audiopolicy进程中发送MSG_VOL_ADJUST
case MSG_VOL_ADJUST:
if (mVolCb != null) {
mVolCb.onVolumeAdjustment(msg.arg1);
} else { // should never be null, but don't crash
Log.e(TAG, "Invalid null volume event");
}
break;
mVolCb就是在应用中需要注册自己单独的audiopolicy ,如车机就需要单独注册自己的,如下就是案例
Android 定义了一些用于控制音量的键码,包括 KEYCODE_VOLUME_UP、KEYCODE_VOLUME_DOWN 和 KEYCODE_VOLUME_MUTE。默认情况下,Android 会将音量键事件路由(audiopolicy)到应用。Automotive 实现应强制将这些键事件路由到 CarAudioService,以便该服务可以根据情况适当地调用 setGroupVolume 或 setMasterMute。
如要强制实现此行为,请将config.xml中的 config_handleVolumeKeysInWindowManager 标志设为 true:
<resources>
<bool name="config_handleVolumeKeysInWindowManager">true</bool>
</resources>
packages/services/Car/service/src/com/android/car/audio/XXX.java
private final AudioPolicy.AudioPolicyVolumeCallback mAudioPolicyVolumeCallback =
new AudioPolicy.AudioPolicyVolumeCallback() {
@Override
public void onVolumeAdjustment(int adjustment) {
}
}
private void XXX() {
final AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);
builder.setLooper(Looper.getMainLooper());
builder.setAudioPolicyVolumeCallback(mAudioPolicyVolumeCallback);
if (sUseCarAudioFocus) {
// Configure our AudioPolicy to handle focus events.
// This gives us the ability to decide which audio focus requests to accept and bypasses
// the framework ducking logic.
mFocusHandler = new CarZonesAudioFocus(mAudioManager,
mContext.getPackageManager(),
mCarAudioZones,
mCarAudioSettings, ENABLE_DELAYED_AUDIO_FOCUS);
builder.setAudioPolicyFocusListener(mFocusHandler);
builder.setIsAudioFocusPolicy(true);
}
mAudioPolicy = builder.build();
if (sUseCarAudioFocus) {
// Connect the AudioPolicy and the focus listener
mFocusHandler.setOwningPolicy(this, mAudioPolicy);
}
int r = mAudioManager.registerAudioPolicy(mAudioPolicy);
if (r != AudioManager.SUCCESS) {
throw new RuntimeException("registerAudioPolicy failed " + r);
}
}
同时使用固定音量车机实现应使用硬件放大器(而非软件混音器)来控制音量。为避免产生副作用,请将 config_useFixedVolume 标志设为 true,如上代码:
boolean果应用没有注册了audiopolicy.setAudioPolicyVolumeCallback,而config_useFixedVolume又为true,就会走到1
<resources>
<!-- Car uses hardware amplifier for volume. -->
<bool name="config_useFixedVolume">true</bool>
</resources>
protected void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
int keyEventMode) {
if (mUseFixedVolume) {
/1:走到这里
return;
}
if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream=" + streamType + ", dir=" + direction
+ ", flags=" + flags + ", caller=" + caller);
ensureValidDirection(direction);
ensureValidStreamType(streamType);
boolean isMuteAdjust = isMuteAdjust(direction);
//
}