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

摘要: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);

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

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

防止已经拥有或者暂时失去焦点的引用再次申请焦点,新建两个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;

接下来会遍历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;
            }

接着判断是否是同一个应用申请的焦点,如果是同一个应用且申请的是同一个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
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Verilog仲裁器是一种用于控制多个用户对共享资源进行访问的电路。它根据一定的规则和算法,决定哪个用户有权使用资源,并在不同的情况下进行相应的处理。在Verilog中,仲裁器的设计和实现可以有多种方式。根据引用所提到的公平轮询仲裁器的Verilog RTL代码,可以实现一个公平轮询的仲裁器。在公平轮询方案中,所有用户优先级相等,每个用户依次获得授权。仲裁器按序检查每个用户的请求信号是否有效,如果一个用户的请求无效,则按序查看下一个用户。仲裁器会记住上一次被授权的用户,在该用户的操作完成后,会按序轮询其他用户是否有请求。一旦某个用户获得了授权,它可以长时间使用总线或占用资源,直到当前数据包传输结束或一个访问过程结束后,仲裁器才会授权其他用户进行操作。这样的仲裁器适用于基于数据包的协议,如以太网交换或PCI交换机。除了公平轮询方案,还可以使用其他的仲裁算法和策略来实现Verilog仲裁器,例如优先级仲裁、旋转仲裁、定时仲裁等,具体的选择取决于系统的需求和设计要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [verilog实例-仲裁(Arbiter)](https://blog.csdn.net/qq_70829439/article/details/127611837)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值