• 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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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()));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    接着调用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);
                    }
                ...
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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*/);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    接着调用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);
                ...
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

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

    其中thisMediaFocusControl对象。

    回到MediaFocusControlduckPlayers方法:

        //MediaFocusControl.java
        @Override
        public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {
            return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中mFocusEnforcerPlaybackActivityMonitor,在创建MediaFocusControl对象时传入。

        private final PlaybackActivityMonitor mPlaybackMonitor;
    
           mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
    
    • 1
    • 2
    • 3

    来到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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其中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*/);
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    内部类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);
                    }
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    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;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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方法。

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

  • 相关阅读:
    C++ - 使用标准库实现事件和委托,信号和槽机制
    【php快速入门】学习笔记
    .NET抽象类 Abstract 虚方法 Virtural 接口 interface
    C#单例模式懒汉式与饿汉式
    Nginx安全加固
    Day03—Python的条件判断(if、elif和else语句),代码缩进规则
    DNS如何在Windows NIC配置多个DNS服务器时完成DNS解析查询
    2、Windows下安装
    O-羟丙基壳聚糖/聚乙二醇水凝胶/N-乙烯基吡咯烷酮(NVP)接枝壳聚糖(CHI)水凝胶的制备
    zabbix监控
  • 原文地址:https://blog.csdn.net/CJohn1994/article/details/126046277