现象
app在前台可以申请音频焦点,但是在后台申请音频焦点失败。
原因
HardeningEnforcer.blockFocusMethod方法拒绝了后台app的申请,这个方法的第一个if会判断app是否符合拿到音频焦点的条件。第二if判断如果targetSdk是否小于35,如果小于35也不会拒绝。
java
//frameworks/base/services/core/java/com/android/server/audio/HardeningEnforcer.java
/**
* Checks whether the call in the current thread should be allowed or blocked
* @return false if the method call is allowed, true if it should be a no-op
*/
@SuppressWarnings("AndroidFrameworkCompatChange")
protected boolean blockFocusMethod(int callingUid, int focusMethod, @NonNull String clientId,
int focusReqType, @NonNull String packageName, String attributionTag, int targetSdk) {
if (noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName, attributionTag)) {
blocked = false;
} else if (targetSdk < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
// Build.VERSION_CODES.VANILLA_ICE_CREAM=35
// 如果targetSdk<35,允许申请音频焦点
blocked = false;
unblockedBySdk = true;
}
metricsLogFocusReq(blocked, focusReqType, callingUid, unblockedBySdk);
if (!blocked) {
// 不允许申请音频焦点
return false;
}
// 允许申请音频焦点
return true;
}
解决方法
方法一
直接降级targetSdk,改到35以下
plain
android {
defaultConfig {
targetSdk = 34
}
}
方法二
创建前台Service来申请音频焦点,https://developer.android.com/develop/background-work/services/fgs?hl=zh-cn
方法三
添加MODIFY_AUDIO_ROUTING或MODIFY_AUDIO_SETTINGS_PRIVILEGED权限。必须要有系统签名的应用才能获取这个权限。
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING"/>
<!-- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED"/> -->
</manifest>
AudioService在申请音频焦点时,会先判断是否有这个权限。如果MODIFY_AUDIO_ROUTING或MODIFY_AUDIO_SETTINGS_PRIVILEGED,就不会调用HardeningEnforcer.blockFocusMethod,自然也不会拒绝音频焦点了。
java
//frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public int requestAudioFocus(AudioAttributes aa, int focusReqType, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName,
String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk) {
// 检查申请音频焦点的app是否有权限绕过HardeningEnforcer
// does caller have system privileges to bypass HardeningEnforcer
boolean permissionOverridesCheck = false;
if ((mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
== PackageManager.PERMISSION_GRANTED)
|| (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
== PackageManager.PERMISSION_GRANTED)) {
permissionOverridesCheck = true;
} else if (uid < UserHandle.AID_APP_START) {
permissionOverridesCheck = true;
}
try {
if (permissionOverridesCheck) {
mHardeningEnforcer.metricsLogFocusReq(/*blocked*/ false, focusReqType, uid,
/*unblockedBySdk*/ false);
}
if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid,
HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS,
clientId, focusReqType, callingPackageName, attributionTag, sdk)) {
// 没有MODIFY_AUDIO_ROUTING权限,并且blockFocusMethod返回false,
// 才会申请焦点失败
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
} finally {
Binder.restoreCallingIdentity(token);
}
}