android媒体焦点音量压低/暂停逻辑源码简析

客户端通过AudioManager的调用requestAudioFocus请求焦点的过程中,其他媒体客户端会因媒体焦点丢失进行媒体压低或暂停,下面从framework层简单分析其过程。
首先调用AudioManagerrequestAudioFocus方法:

    //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通信,调用AudioServicerequestAudioFocus

    //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*/);
        }
    }

接着调用FocusRequesterhandleFocusLossFromGain方法再到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);
            ...
    }

其中mFocusControllerMediaFocusControl
MediaFocusControlrequestAudioFocus方法中创建FocusRequester传入

            final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
                    clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);

其中thisMediaFocusControl对象。

回到MediaFocusControlduckPlayers方法:

    //MediaFocusControl.java
    @Override
    public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {
        return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);
    }

其中mFocusEnforcerPlaybackActivityMonitor,在创建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,遍历apcsToDuckaddDuck音量压低逻辑。

    //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,遍历apcsToDuckaddDuck音量压低逻辑:

    //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方法在PlayerBasebaseRegisterPlayer方法中调用。

   //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;
    }

AudioPlaybackConfigurationgetPlayerProxy获取IPlayerShellgetIPlayer()得到的是IPlayer实例,内部类IPlayerShell获取的IPlayer即为PlayerBase.PlayerIdCard pic的参数mIPlayer

回到AudioService中通过PlayerBase的方法baseRegisterPlayer调用getService().trackPlayer( new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)))
IPlayerWrapper即为IPlayer的实现类执行applyVolumeShaper调用PlayerBase实现类的playerApplyVolumeShaper进行压低
MediaPlayerSoundPoolAudioTrack实现了PlayerBase, 构造函数中调用baseRegisterPlayer进行媒体注册,方法playerApplyVolumeShaper调用native_applyVolumeShaper进行压低。

VolumeShaper实现音频音量控制,调用mediaPlayerplayerApplyVolumeShaper方法。

以上为媒体焦点音量压低的简单逻辑分析。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

言并肃

感谢大哥支持!您的鼓励是我动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值