当前位置:   article > 正文

Android 10 音频焦点仲裁策略分析_android audio 仲裁

android audio 仲裁

摘要:Android 9 的音频焦点仲裁策略基本上可以用一句话来概括:后来居上,电话最大。这种策略显然是不能满足音频焦点仲裁的复杂需求的,所以Google在Android 10 中做了大幅度的改进,其中最主要的就是引入了音频焦点判断矩阵,通过矩阵来仲裁后来者是否可以抢占当前焦点。

由于Android9的音频焦点策略基本不能满足项目需求,所以一般会引入外部焦点仲裁策略,不知道如何引入的可以参考这篇文章:自定义音频焦点策略的实现。既然要引入,何不引入Android 10 的音频焦点策略呢?

音频焦点仲裁策略分析

1.关键类及变量列表

变量 类型 说明
AudioFocusInfo 描述焦点申请者属性
FocusEntry 内部类 对AudioFocusInfo和Context的封装
sInteractionMatrix 二维数组 仲裁焦点申请结果
mFocusHolders 全局变量,HashMap 保存当前焦点持有者
mFocusLosers 全局变量,HashMap 保存暂时失去焦点并等待重新获得焦点的申请
losers 局部变量,ArrayList 保存失去焦点但失去焦点类型尚未确定的申请
blocked 局部变量,ArrayList 保存mFocusLosers中可以被当前申请者抢占的申请
permanentlyLost 局部变量,ArrayList 保存永久失去焦点的申请

2.申请焦点及仲裁过程详解

申请焦点的入口函数是:evaluateFocusRequest,接受一个AudioFocusInfo类型参数

新建两个boolean类型参数用来描述焦点申请者属性:
permanent:描述申请者是否申请永久获取焦点(即申请类型是否为AUDIOFOCUS_GAIN)
allowDucking:描述申请者是否申请暂时获取焦点且允许混音(即申请类型是否为AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)

        // Is this a request for premanant focus?
        // AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE -- Means Notifications should be denied
        // AUDIOFOCUS_GAIN_TRANSIENT -- Means current focus holders should get transient loss
        // AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK -- Means other can duck (no loss message from us)
        // NOTE:  We expect that in practice it will be permanent for all media requests and
        //        transient for everything else, but that isn't currently an enforced requirement.
        final boolean permanent =
                (afi.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN);
        //final boolean allowDucking =
        //        (afi.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
        // only interaction matrix can decide if ducking allowed
        final boolean allowDucking = 
        		(afi.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

usage转化为Context:(什么是usage和Context?参考此处

        // Convert from audio attributes "usage" to HAL level "context"
        final int requestedContext = mCarAudioService.getContextForUsage(
                afi.getAttributes().getUsage());
  • 1
  • 2
  • 3

防止已经拥有或者暂时失去焦点的引用再次申请焦点,新建两个FocusEntry
replacedCurrentEntry:当已经拥有焦点的应用再次申请焦点时,对这个变量赋值,如果申请成功则删除replacedCurrentEntry,用新的请申请代替
replacedBlockedEntry:当暂时失去焦点的应用再次申请焦点时,对这个变量赋值,如果申请成功则删除replacedBlockedEntry,用新的请申请代替

        // If we happen to find entries that this new request should replace, we'll store them here.
        // This happens when a client makes a second AF request on the same listener.
        // After we've granted audio focus to our current request, we'll abandon these requests.
        FocusEntry replacedCurrentEntry = null;
        FocusEntry replacedBlockedEntry = null;
  • 1
  • 2
  • 3
  • 4
  • 5

接下来会遍历mFocusHolders

首先判断如果当前申请者的requestedContextNOTIFICATION并且mFocusHolders已经存在一个暂时独占焦点的申请者(AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE),则直接返回申请失败

            // If this request is for Notifications and a current focus holder has specified
            // AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE, then reject the request.
            // This matches the hardwired behavior in the default audio policy engine which apps
            // might expect (The interaction matrix doesn't have any provision for dealing with
            // override flags like this).
            if ((requestedContext == ContextNumber.NOTIFICATION) &&
                    (entry.mAfi.getGainRequest() ==
                            AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {
   
                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

接着判断是否是同一个应用申请的焦点,如果是同一个应用且申请的是同一个Context类型的焦点,则将目前mFocusHolders中的此应用的entry赋值给replacedCurrentEntry,之后如果焦点申请成功则会将这个entry删除并将新的加入,如果申请的是不同的Context类型,则直接返回申请失败

            // We don't allow sharing listeners (client IDs) between two concurrent requests
            // (because the app would have no way to know to which request a later event applied)
            if (afi.getClientId().equals(entry.mAfi.getClientId())) {
   
                if (entry.mAudioContext == requestedContext) {
   
                    // This is a request from a current focus holder.
                    // Abandon the previous request (without sending a LOSS notification to it),
                    // and don't check the interaction matrix for it.
                    Slog.i(TAG, "Replacing accepted request from same client");
                    replacedCurrentEntry = entry;
                    continue;
                } else {
   
                    // Trivially reject a request for a different USAGE
                    Slog.e(TAG, "Client " + entry.getClientId() + " has already requested focus "
                            + "for " + entry.mAfi.getAttributes().usageToString() + " - cannot "
                            + "request focus for " + afi.<
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号