Android CarAudioFocus详解(二)

前面文章分析了申请调用的流程,同时也有一篇执行流程的文字描述,但是总觉得不够清晰,让我们来用图示和代码结合说下这部分:

AudioRequester:AudioFocusInfo为传入的参数,里面包含了USAGE类型,GAIN类型,ClientId。其中permanent表示是否永久获取焦点,allowDucking表示是否接受duck。

mFocusHolders:焦点持有者HashMap。里面保存了当前持有焦点的FocusEntry。

mFocusLosers:也是HashMap,里面保存了被抢占焦点,有可能恢复持有的FocusEntry。

losers:AudioRequester和mFocusHolders通过evaluateRequest函数冲突后生成的中间产物。

blocks:AudioRequester和mFocusLosers通过evaluateRequest函数冲突后生成的中间产物。

mDelayerRequest:保存未申请成功,但是接受延迟获得焦点的FocusEntry。

介绍完成员开始介绍申请焦点的流程:

流程一:mFocusHolders和和AudioRequester的冲突:

        Log.i(TAG, "Scanning focus holders...");
        final ArrayList<FocusEntry> losers = new ArrayList<FocusEntry>();
        for (FocusEntry entry : mFocusHolders.values()) {
            Log.d(TAG, "Evaluating focus holder: " + entry.getClientId());

            // 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 == CarAudioContext.NOTIFICATION)
                    && (entry.getAudioFocusInfo().getGainRequest()
                    == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {
                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
            }

            // 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.getAudioFocusInfo().getClientId())) {
                if (entry.getAudioContext() == 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.
                    Log.i(TAG, "Replacing accepted request from same client");
                    replacedCurrentEntry = entry;
                    continue;
                } else {
                    // Trivially reject a request for a different USAGE
                    Log.e(TAG, String.format(
                            "Client %s has already requested focus for %s - cannot request focus "
                                    + "for %s on same listener.",
                            entry.getClientId(),
                            entry.getAudioFocusInfo().getAttributes().usageToString(),
                            afi.getAttributes().usageToString()));
                    return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
                }
            }

            @AudioManager.FocusRequestResult int interactionResult = mFocusInteraction
                    .evaluateRequest(requestedContext, entry, losers, allowDucking,
                            allowDelayedFocus);
            if (interactionResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
                return interactionResult;
            }
            if (interactionResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
                delayFocusForCurrentRequest = true;
            }
        }

流程二:mFocusLosers和和AudioRequester的冲突:

Log.i(TAG, "Scanning those who've already lost focus...");
        final ArrayList<FocusEntry> blocked = new ArrayList<FocusEntry>();
        for (FocusEntry entry : mFocusLosers.values()) {
            Log.i(TAG, entry.getAudioFocusInfo().getClientId());

            // If this request is for Notifications and a pending focus holder has specified
            // AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE, then reject the request
            if ((requestedContext == CarAudioContext.NOTIFICATION)
                    && (entry.getAudioFocusInfo().getGainRequest()
                    == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {
                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
            }

            // 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.getAudioFocusInfo().getClientId())) {
                if (entry.getAudioContext() == requestedContext) {
                    // This is a repeat of a request that is currently blocked.
                    // Evaluate it as if it were a new request, but note that we should remove
                    // the old pending request, and move it.
                    // We do not want to evaluate the new request against itself.
                    Log.i(TAG, "Replacing pending request from same client");
                    replacedBlockedEntry = entry;
                    continue;
                } else {
                    // Trivially reject a request for a different USAGE
                    Log.e(TAG, String.format(
                            "Client %s has already requested focus for %s - cannot request focus "
                                    + "for %s on same listener.",
                            entry.getClientId(),
                            entry.getAudioFocusInfo().getAttributes().usageToString(),
                            afi.getAttributes().usageToString()));
                    return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
                }
            }

            @AudioManager.FocusRequestResult int interactionResult = mFocusInteraction
                    .evaluateRequest(requestedContext, entry, blocked, allowDucking,
                            allowDelayedFocus);
            if (interactionResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
                return interactionResult;
            }
            if (interactionResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
                delayFocusForCurrentRequest = true;
            }
        }

流程三:mFocusInteraction.evaluateRequest执行冲突:

public @FocusRequestResult int evaluateRequest(@AudioContext int requestedContext,
            FocusEntry focusHolder, List<FocusEntry> focusLosers, boolean allowDucking,
            boolean allowsDelayedFocus) {
        @AudioContext int holderContext = focusHolder.getAudioContext();
        Preconditions.checkArgumentInRange(holderContext, 0, mInteractionMatrix.length - 1,
                "holderContext");
        synchronized (mLock) {
            int[] holderRow = mInteractionMatrix[holderContext];
            Preconditions.checkArgumentInRange(requestedContext, 0, holderRow.length - 1,
                    "requestedContext");

            switch (holderRow[requestedContext]) {
                case INTERACTION_REJECT:
                    if (allowsDelayedFocus) {
                        return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
                    }
                    return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
                case INTERACTION_EXCLUSIVE:
                    focusLosers.add(focusHolder);
                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
                case INTERACTION_CONCURRENT:
                    // If ducking isn't allowed by the focus requester, then everybody else
                    // must get a LOSS.
                    // If a focus holder has set the AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS flag,
                    // they must get a LOSS message even if ducking would otherwise be allowed.
                    // If a focus holder holds the RECEIVE_CAR_AUDIO_DUCKING_EVENTS permission,
                    // they must receive all audio focus losses.
                    if (!allowDucking
                            || focusHolder.wantsPauseInsteadOfDucking()
                            || focusHolder.receivesDuckEvents()) {
                        focusLosers.add(focusHolder);
                    }
                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
                default:
                    Log.e(TAG, String.format("Unsupported CarAudioContext %d - rejecting request",
                            holderRow[requestedContext]));
                    return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
            }
        }
    }

流程四:blocked处理

for (FocusEntry entry : blocked) {
            // If we're out of focus it must be because somebody is blocking us
            assert !entry.isUnblocked();

            if (permanent) {
                // This entry has now lost focus forever
                sendFocusLossLocked(entry.getAudioFocusInfo(), AudioManager.AUDIOFOCUS_LOSS);
                entry.setDucked(false);
                final FocusEntry deadEntry = mFocusLosers.remove(
                        entry.getAudioFocusInfo().getClientId());
                assert deadEntry != null;
                permanentlyLost.add(entry);
            } else {
                if (!allowDucking && entry.isDucked()) {
                    // This entry was previously allowed to duck, but can no longer do so.
                    Log.i(TAG, "Converting duckable loss to non-duckable for "
                            + entry.getClientId());
                    sendFocusLossLocked(entry.getAudioFocusInfo(),
                            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
                    entry.setDucked(false);
                }
                // Note that this new request is yet one more reason we can't (yet) have focus
                entry.addBlocker(newEntry);
            }
        }

流程五:losers的处理:

for (FocusEntry entry : losers) {
            // If we have focus (but are about to loose it), nobody should be blocking us yet
            assert entry.isUnblocked();

            int lossType;
            if (permanent) {
                lossType = AudioManager.AUDIOFOCUS_LOSS;
            } else if (allowDucking && entry.receivesDuckEvents()) {
                lossType = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
                entry.setDucked(true);
            } else {
                lossType = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
            }
            sendFocusLossLocked(entry.getAudioFocusInfo(), lossType);

            // The entry no longer holds focus, so take it out of the holders list
            mFocusHolders.remove(entry.getAudioFocusInfo().getClientId());

            if (permanent) {
                permanentlyLost.add(entry);
            } else {
                // Add ourselves to the list of requests waiting to get focus back and
                // note why we lost focus so we can tell when it's time to get it back
                mFocusLosers.put(entry.getAudioFocusInfo().getClientId(), entry);
                entry.addBlocker(newEntry);
            }
        }

流程六:对于永久失去焦点的permanentlyLost这个HashMap进行处理:

        for (FocusEntry entry : permanentlyLost) {
            Log.d(TAG, "Cleaning up entry " + entry.getClientId());
            removeBlockerAndRestoreUnblockedWaitersLocked(entry);
        }

流程七:对于AudioRequester接受延迟获得焦点的处理:

        if (delayFocusForCurrentRequest) {
            Log.e(TAG,"delayFocusForCurrentRequest");
            swapDelayedAudioFocusRequestLocked(afi);
            return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
        }
    private void swapDelayedAudioFocusRequestLocked(AudioFocusInfo afi) {
        // If we are swapping to a different client then send the focus loss signal
        if (mDelayedRequest != null
                && !afi.getClientId().equals(mDelayedRequest.getClientId())) {
            sendFocusLossLocked(mDelayedRequest, AudioManager.AUDIOFOCUS_LOSS);
        }
        mDelayedRequest = afi;
    }

总图如下:

接下来是释放焦点的流程:

流程一:从mFocusHloders和mFocusLoser中分别尝试删除AudioReleaser

private FocusEntry removeFocusEntryLocked(AudioFocusInfo afi) {
        Log.i(TAG, "removeFocusEntry " + afi.getClientId());

        // Remove this entry from our active or pending list
        FocusEntry deadEntry = mFocusHolders.remove(afi.getClientId());
        if (deadEntry == null) {
            deadEntry = mFocusLosers.remove(afi.getClientId());
            if (deadEntry == null) {
                // Caller is providing an unrecognzied clientId!?
                Log.w(TAG, "Audio focus abandoned by unrecognized client id: " + afi.getClientId());
                // This probably means an app double released focused for some reason.  One
                // harmless possibility is a race between an app being told it lost focus and the
                // app voluntarily abandoning focus.  More likely the app is just sloppy.  :)
                // The more nefarious possibility is that the clientId is actually corrupted
                // somehow, in which case we might have a real focus entry that we're going to fail
                // to remove. If that were to happen, I'd expect either the app to swallow it
                // silently, or else take unexpected action (eg: resume playing spontaneously), or
                // else to see "Failure to signal ..." gain/loss error messages in the log from
                // this module when a focus change tries to take action on a truly zombie entry.
            }
        }
        return deadEntry;
    }

流程二:如果从mFocusHolders或mFocusLosers中找到了要删除的FocusEntry,就让mDelayedRequest走一遍evaluateFocusRequestLocked冲突函数,如果冲突结果是AUDIOFOCUS_REQUEST_GRANTED,那mDelayedRequest就成了焦点持有者:

代码一:
    private void removeBlockerAndRestoreUnblockedWaitersLocked(FocusEntry deadEntry) {
        attemptToGainFocusForDelayedAudioFocusRequest();
        removeBlockerAndRestoreUnblockedFocusLosersLocked(deadEntry);
    }
代码二:
    private void attemptToGainFocusForDelayedAudioFocusRequest() {
        if (!mEnabledDelayedFocusRequest || mDelayedRequest == null) {
            return;
        }
        int delayedFocusRequestResults = evaluateFocusRequestLocked(mDelayedRequest);
        if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            FocusEntry focusEntry = mFocusHolders.get(mDelayedRequest.getClientId());
            mDelayedRequest = null;
            if (dispatchFocusGainedLocked(focusEntry.getAudioFocusInfo())
                    == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
                Log.e(TAG,
                        "Failure to signal gain of audio focus gain for "
                                + "delayed focus clientId " + focusEntry.getClientId());
                mFocusHolders.remove(focusEntry.getClientId());
                removeBlockerFromBlockedFocusLosersLocked(focusEntry);
                sendFocusLossLocked(focusEntry.getAudioFocusInfo(),
                        AudioManager.AUDIOFOCUS_LOSS);
                logFocusEvent("Did not gained delayed audio focus for "
                        + focusEntry.getClientId());
            }
        }
    }

流程三:如果mDelayRequest为null的话,就要从mFocusLoser中找下一个焦点持有者了

代码三:
    private void removeBlockerAndRestoreUnblockedFocusLosersLocked(FocusEntry deadEntry) {
        // Remove this entry from the blocking list of any pending requests
        Iterator<FocusEntry> it = mFocusLosers.values().iterator();
        while (it.hasNext()) {
            FocusEntry entry = it.next();

            // Remove the retiring entry from all blocker lists
            entry.removeBlocker(deadEntry);

            // Any entry whose blocking list becomes empty should regain focus
            if (entry.isUnblocked()) {
                Log.i(TAG, "Restoring unblocked entry " + entry.getClientId());
                // Pull this entry out of the focus losers list
                it.remove();

                // Add it back into the focus holders list
                mFocusHolders.put(entry.getClientId(), entry);

                dispatchFocusGainedLocked(entry.getAudioFocusInfo());
            }
        }
    }

流程四:如果从mFocusHolders和mFocusLosers中没有找到要释放的这个FocusEntry,并且这个要释放的FocusEntry和mDelayedRequest有着相同的clientId,就把mDelayerRequest赋值为null

    private void removeDelayedAudioFocusRequestLocked(AudioFocusInfo afi) {
        if (mDelayedRequest != null && afi.getClientId().equals(mDelayedRequest.getClientId())) {
            mDelayedRequest = null;
        }
    }

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值