CarAudioFocus中的evaluateFocusRequestLocked函数分析:
private int evaluateFocusRequestLocked(AudioFocusInfo afi) {
// 判断请求是否是永久性的,即是否是AUDIOFOCUS_GAIN类型
final boolean permanent =
(afi.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN);
// 判断请求是否允许ducking,即是否是AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK类型
final boolean allowDucking =
(afi.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
// 定义一个布尔值,表示是否需要为当前请求延迟焦点
boolean delayFocusForCurrentRequest = false;
// 获取请求的音频上下文,即请求的音频用途,例如音乐、导航、语音命令等
final int requestedContext = CarAudioContext.getContextForUsage(
afi.getAttributes().getUsage());
// 定义两个FocusEntry对象,表示需要被当前请求替换的之前的请求
// 这种情况发生在一个客户端对同一个监听器发出了第二个焦点请求时
// 在授予当前请求焦点后,我们会放弃这些之前的请求
FocusEntry replacedCurrentEntry = null;
FocusEntry replacedBlockedEntry = null;
// 判断当前请求是否可以接受延迟获取焦点,即在没有可用焦点时等待其他应用释放焦点后再获取
boolean allowDelayedFocus = mEnabledDelayedFocusRequest && canReceiveDelayedFocus(afi);
// 我们不允许在两个并发的请求之间共享监听器(客户端ID)
// 因为应用无法知道后续的事件应该应用于哪个请求
// 如果已经有一个延迟请求存在,并且与当前请求使用了相同的监听器(客户端ID),就检查两个请求是否有相同的音频上下文
// 如果没有,就拒绝当前请求,因为同一个监听器不能同时处理两个不同用途的请求
if (mDelayedRequest != null && afi.getClientId().equals(mDelayedRequest.getClientId())) {
// int delayedRequestedContext = CarAudioContext.getContextForUsage(
// mDelayedRequest.getAttributes().getSystemUsage());
int delayedRequestedContext = CarAudioContext.getContextForUsage(
mDelayedRequest.getAttributes().getUsage());
// 如果两个请求的音频上下文不同,就拒绝当前请求,并打印错误日志
if (delayedRequestedContext != requestedContext) {
// Trivially reject a request for a different USAGE
Log.e(TAG, String.format(
"Client %s has already delayed requested focus for %s "
+ "- cannot request focus for %s on same listener.",
mDelayedRequest.getClientId(),
mDelayedRequest.getAttributes().usageToString(),
afi.getAttributes().usageToString()));
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
}
// 遍历所有当前活跃或等待的音频焦点请求
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());
// 如果新的请求是通知类型,且当前的焦点持有者指定了AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE类型,就拒绝新的请求
// 这与默认音频策略引擎中的硬编码行为相匹配,应用可能会期望(交互矩阵没有处理这种覆盖标志的方法)
if ((requestedContext == CarAudioContext.NOTIFICATION)
&& (entry.getAudioFocusInfo().getGainRequest()
== AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
// 我们不允许在两个并发的请求之间共享监听器(客户端ID)
// 因为应用无法知道后续的事件应该通知到哪个请求
if (afi.getClientId().equals(entry.getAudioFocusInfo().getClientId())) {
if (entry.getAudioContext() == requestedContext) {
// 这是一个来自当前焦点持有者的请求
// 放弃之前的请求(不向它发送LOSS通知),
// 并且不检查它的交互矩阵
Log.i(TAG, "Replacing accepted request from same client");
replacedCurrentEntry = entry;
continue;
} else {
// 拒绝一个不同用途的请求,并打印错误日志
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
}
// 遍历所有当前已经失去音频焦点的请求
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());
// 如果新的请求是通知类型,且已经失去焦点的请求指定了AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE类型,就拒绝新的请求
if ((requestedContext == CarAudioContext.NOTIFICATION)
&& (entry.getAudioFocusInfo().getGainRequest()
== AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
// 我们不允许在两个并发的请求之间共享监听器(客户端ID)
// 因为应用无法知道后续的事件应该应用于哪个请求
if (afi.getClientId().equals(entry.getAudioFocusInfo().getClientId())) {
if (entry.getAudioContext() == requestedContext) {
// 这是一个重复的请求,之前已经被阻塞
// 将它视为一个新的请求进行评估,但注意我们应该移除
// 旧的等待请求,并移动它
// 我们不想将新的请求与自己进行比较
Log.i(TAG, "Replacing pending request from same client");
replacedBlockedEntry = entry;
continue;
} else {
// 拒绝一个不同用途的请求,并打印错误日志
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;
}
}
// 在确定授予新的音频焦点请求后,构造一个新的FocusEntry对象
// FocusEntry对象包含了请求的信息、音频上下文和包管理器等属性
FocusEntry newEntry = new FocusEntry(afi, requestedContext, mPackageManager);
// 这些请求因为新的请求而永久失去焦点,所以应该从所有阻塞列表中移除
ArrayList<FocusEntry> permanentlyLost = new ArrayList<>();
// 如果有被当前请求替换的活跃或等待的请求,就从相应的列表中移除,并添加到永久失去焦点的列表中
if (replacedCurrentEntry != null) {
mFocusHolders.remove(replacedCurrentEntry.getClientId());
permanentlyLost.add(replacedCurrentEntry);
}
if (replacedBlockedEntry != null) {
mFocusLosers.remove(replacedBlockedEntry.getClientId());
permanentlyLost.add(replacedBlockedEntry);
}
// 在确定接受当前请求后,更新那些被当前请求阻塞但已经失去焦点并等待恢复的请求
for (FocusEntry entry : blocked) {
// 如果我们失去了焦点,必然是因为有人阻塞了我们
assert !entry.isUnblocked();
// 如果当前请求是永久性的,就向这些请求发送永久失去焦点的通知,并从等待列表中移除,并添加到永久失去焦点的列表中
if (permanent) {
// 这个请求现在永远失去了焦点
sendFocusLossLocked(entry.getAudioFocusInfo(), AudioManager.AUDIOFOCUS_LOSS);
entry.setDucked(false);
final FocusEntry deadEntry = mFocusLosers.remove(
entry.getAudioFocusInfo().getClientId());
assert deadEntry != null;
permanentlyLost.add(entry);
} else {
// 如果当前请求不允许ducking,且这些请求之前被允许ducking,就向这些请求发送暂时失去焦点的通知,并取消ducking状态
if (!allowDucking && entry.isDucked()) {
// 这个请求之前被允许ducking,但现在不行了
Log.i(TAG, "Converting duckable loss to non-duckable for "
+ entry.getClientId());
sendFocusLossLocked(entry.getAudioFocusInfo(),
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
entry.setDucked(false);
}
// 记录当前请求是另一个原因导致我们不能(暂时)获取焦点
entry.addBlocker(newEntry);
}
}
// 通知并更新那些因为新的请求而失去焦点的请求
for (FocusEntry entry : losers) {
// 如果我们有焦点(但即将失去),那么还没有人应该阻塞我们
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);
// 这些请求不再持有焦点,所以从持有者列表中移除
mFocusHolders.remove(entry.getAudioFocusInfo().getClientId());
// 如果当前请求是永久性的,就添加到永久失去焦点的列表中
if (permanent) {
permanentlyLost.add(entry);
} else {
// 将这些请求添加到等待恢复焦点的列表中,并记录导致我们失去焦点的原因,以便在合适的时候恢复焦点
mFocusLosers.put(entry.getAudioFocusInfo().getClientId(), entry);
entry.addBlocker(newEntry);
}
}
// 在所有新的阻塞者被添加后,清理那些因为新的请求而永久失去焦点的请求
// 将它们视为被放弃的 - 如果它们在任何阻塞列表中,就移除它们
// 如果有任何请求因此而解除阻塞,就重新授予它们
// (当一个 GAIN_TRANSIENT_MAY_DUCK 请求替换了来自同一个监听器的 GAIN_TRANSIENT 请求时,就会发生这种情况)
for (FocusEntry entry : permanentlyLost) {
Log.d(TAG, "Cleaning up entry " + entry.getClientId());
removeBlockerAndRestoreUnblockedWaitersLocked(entry);
}
// 最后,将我们授予的请求添加到焦点持有者列表中
if (delayFocusForCurrentRequest) {
swapDelayedAudioFocusRequestLocked(afi);
return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
}
mFocusHolders.put(afi.getClientId(), newEntry);
Log.i(TAG, "AUDIOFOCUS_REQUEST_GRANTED");
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
FocusInteraction中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:
// 如果请求是并发的,就根据持有者和请求者的属性来判断是否需要添加到失去焦点的列表中,并返回授予结果
// 如果请求者不允许ducking,或者持有者要求暂停而不是ducking,或者持有者接收duck事件,就将持有者添加到失去焦点的列表中
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;
}
}
}