客户端通过AudioManager
的调用requestAudioFocus
请求焦点的过程中,其他媒体客户端会因媒体焦点丢失进行媒体压低或暂停,下面从framework层简单分析其过程。
首先调用AudioManager
的requestAudioFocus
方法:
//AudioManager.java
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {
...
registerAudioFocusRequest(afr);
final IAudioService service = getService();
final int status;
...
synchronized (mFocusRequestsLock) {
try {
// TODO status contains result and generation counter for ext policy
status = service.requestAudioFocus(afr.getAudioAttributes(),
afr.getFocusGain(), mICallBack,
mAudioFocusDispatcher,
clientId,
getContext().getOpPackageName() /* package name */, afr.getFlags(),
ap != null ? ap.cb() : null,
sdk);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
...
}
return focusReceiver.requestResult();
}
AudioManager
与系统服务AudioService
通信,调用AudioService
的requestAudioFocus
:
//AudioService.java
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
IAudioPolicyCallback pcb, int sdk) {
...
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
clientId, callingPackageName, flags, sdk,
forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid()));
}
接着调用MediaFocusControl
的方法:
//MediaFocusControl.java
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
int sdk, boolean forceDuck) {
...
// propagate the focus change through the stack
if (!mFocusStack.empty()) {
propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck);
}
...
}
MediaFocusControl
通过propagateFocusLossFromGain_syncAf
方法同步处理焦点丢失:
@GuardedBy("mAudioFocusLock")
private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr,
boolean forceDuck) {
final List<String> clientsToRemove = new LinkedList<String>();
// going through the audio focus stack to signal new focus, traversing order doesn't
// matter as all entries respond to the same external focus gain
for (FocusRequester focusLoser : mFocusStack) {
final boolean isDefinitiveLoss =
focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
if (isDefinitiveLoss) {
clientsToRemove.add(focusLoser.getClientId());
}
}
for (String clientToRemove : clientsToRemove) {
removeFocusStackEntry(clientToRemove, false /*signal*/,
true /*notifyFocusFollowers*/);
}
}
接着调用FocusRequester
的handleFocusLossFromGain
方法再到handleFocusLoss
方法
//FocusRequester.java
@GuardedBy("MediaFocusControl.mAudioFocusLock")
void handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck)
{
try {
if (focusLoss != mFocusLossReceived) {
...
handled = mFocusController.duckPlayers(frWinner, this, forceDuck);
...
}
其中mFocusController
即MediaFocusControl
MediaFocusControl
的requestAudioFocus
方法中创建FocusRequester
传入
final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);
其中this
即MediaFocusControl
对象。
回到MediaFocusControl
的duckPlayers
方法:
//MediaFocusControl.java
@Override
public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {
return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);
}
其中mFocusEnforcer
即PlaybackActivityMonitor
,在创建MediaFocusControl
对象时传入。
private final PlaybackActivityMonitor mPlaybackMonitor;
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
来到PlaybackActivityMonitor
执行duckPlayers
方法,通过apcsToDuck.add(apc)
添加到压低媒体apc(AudioPlaybackConfiguration)列表。
//PlaybackActivityMonitor.java
@Override
public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {
...
apcsToDuck.add(apc);
...
mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck);
}
return true;
}
其中mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck)
为内部类DuckingManager
根据mDuckers
获取内部类DuckedApp
,遍历apcsToDuck
去addDuck
音量压低逻辑。
//PlaybackActivityMonitor.java
private static final class DuckingManager {
private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>();
synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) {
if (DEBUG) { Log.v(TAG, "DuckingManager: duckUid() uid:"+ uid); }
if (!mDuckers.containsKey(uid)) {
mDuckers.put(uid, new DuckedApp(uid));
}
final DuckedApp da = mDuckers.get(uid);
for (AudioPlaybackConfiguration apc : apcsToDuck) {
da.addDuck(apc, false /*skipRamp*/);
}
}
内部类DuckedApp
,遍历apcsToDuck
去addDuck
音量压低逻辑:
//PlaybackActivityMonitor.java
private static final class DuckedApp {
private final int mUid;
private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
DuckedApp(int uid) {
mUid = uid;
}
...
void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
...
try {
apc.getPlayerProxy().applyVolumeShaper(
FLY_DUCK_VSHAPE,
VolumeShaper.Operation.PLAY);
mDuckedPlayers.add(piid);
} catch (Exception e) {
Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e);
}
}
DuckedApp
中执行apc.getPlayerProxy().applyVolumeShaper
进行音量压低,apc即AudioPlaybackConfiguration
。
存在AudioPlaybackConfiguration
的Map,在trackPlayer
时添加:
//PlaybackActivityMonitor.java
private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers =
new HashMap<Integer, AudioPlaybackConfiguration>();
public int trackPlayer(PlayerBase.PlayerIdCard pic) {
final AudioPlaybackConfiguration apc =
new AudioPlaybackConfiguration(pic, newPiid,
Binder.getCallingUid(), Binder.getCallingPid());
apc.init();
synchronized(mPlayerLock) {
mPlayers.put(newPiid, apc);
}
return newPiid;
}
trackPlayer
方法在PlayerBase
的baseRegisterPlayer
方法中调用。
//PlayerBase.java
/**
* Call from derived class when instantiation / initialization is successful
*/
protected void baseRegisterPlayer() {
...
try {
newPiid = getService().trackPlayer(
new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
} catch (RemoteException e) {
Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
}
mPlayerIId = newPiid;
}
AudioPlaybackConfiguration
中getPlayerProxy
获取IPlayerShell
的getIPlayer()
得到的是IPlayer实例,内部类IPlayerShell
获取的IPlayer
即为PlayerBase.PlayerIdCard pic
的参数mIPlayer
。
回到AudioService
中通过PlayerBase
的方法baseRegisterPlayer
调用getService().trackPlayer( new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)))
IPlayerWrapper
即为IPlayer
的实现类执行applyVolumeShaper
调用PlayerBase
实现类的playerApplyVolumeShaper
进行压低
MediaPlayer
、SoundPool
、AudioTrack
实现了PlayerBase
, 构造函数中调用baseRegisterPlayer
进行媒体注册,方法playerApplyVolumeShaper
调用native_applyVolumeShaper
进行压低。
VolumeShaper
实现音频音量控制,调用mediaPlayer
的playerApplyVolumeShaper
方法。
以上为媒体焦点音量压低的简单逻辑分析。