Android10-Audio-音频焦点申请-调用流程

Android 10 音频焦点申请框架层调用流程详解

// 说明:

Android 10及以上的版本才有packages/services/Car/service/src/com/android/car/audio/CarAudioFocus.java

一、整体调用流程图

应用调用 requestAudioFocus
AudioManager.requestAudioFocus
AudioManager.requestAudioFocus 重载
IAudioService.requestAudioFocus
Binder IPC 到 AudioService
AudioService.requestAudioFocus
MediaFocusControl.requestAudioFocus

二、详细代码调用流程

2.1 应用层入口

java 复制代码
// 应用代码
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
AudioAttributes attributes = new AudioAttributes.Builder()
    .setUsage(AudioAttributes.USAGE_MEDIA)
    .build();
AudioFocusRequest request = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
    .setAudioAttributes(attributes)
    .setOnAudioFocusChangeListener(listener)
    .build();

// 关键调用入口
int result = audioManager.requestAudioFocus(request);

2.2 AudioManager.java

java 复制代码
// 路径: frameworks/base/media/java/android/media/AudioManager.java
    public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {
        if (afr == null) {
            throw new NullPointerException("Illegal null AudioFocusRequest");
        }
        // this can only be checked now, not during the creation of the AudioFocusRequest instance
        if (afr.locksFocus() && ap == null) {
            throw new IllegalArgumentException(
                    "Illegal null audio policy when locking audio focus");
        }
        registerAudioFocusRequest(afr);
        final IAudioService service = getService();
        final int status;
        int sdk;
        try {
            sdk = getContext().getApplicationInfo().targetSdkVersion;
        } catch (NullPointerException e) {
            // some tests don't have a Context
            sdk = Build.VERSION.SDK_INT;
        }

        final String clientId = getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener());
        final BlockingFocusResultReceiver focusReceiver;
        synchronized (mFocusRequestsLock) {
            try {
                // TODO status contains result and generation counter for ext policy
                status = service.requestAudioFocus(afr.getAudioAttributes(),
                        afr.getFocusGain(), mICallBack,
                        mAudioFocusDispatcher,
                        clientId,
                        getContext().getOpPackageName() /* package name */, afr.getFlags(),
                        ap != null ? ap.cb() : null,
                        sdk);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
            if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
                // default path with no external focus policy
                return status;
            }
            if (mFocusRequestsAwaitingResult == null) {
                mFocusRequestsAwaitingResult =
                        new HashMap<String, BlockingFocusResultReceiver>(1);
            }
            focusReceiver = new BlockingFocusResultReceiver(clientId);
            mFocusRequestsAwaitingResult.put(clientId, focusReceiver);
        }
        focusReceiver.waitForResult(EXT_FOCUS_POLICY_TIMEOUT_MS);
        if (DEBUG && !focusReceiver.receivedResult()) {
            Log.e(TAG, "requestAudio response from ext policy timed out, denying request");
        }
        synchronized (mFocusRequestsLock) {
            mFocusRequestsAwaitingResult.remove(clientId);
        }
        return focusReceiver.requestResult();
    }

2.3 IAudioService.aidl 接口定义

java 复制代码
// 路径: frameworks/base/media/java/android/media/IAudioService.aidl
interface IAudioService {
    int requestAudioFocus(in AudioAttributes aa, int durationHint,
                         IAudioFocusDispatcher fd, String clientId,
                         IBinder cb, String callingPackageName,
                         int flags, IAudioPolicyCallback pcb);
}

2.4 AudioService.java

java 复制代码
// 路径: frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
            IAudioPolicyCallback pcb, int sdk) {
        // permission checks
        if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {
            if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
                if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
                            android.Manifest.permission.MODIFY_PHONE_STATE)) {
                    Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception());
                    return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
                }
            } else {
                // only a registered audio policy can be used to lock focus
                synchronized (mAudioPolicies) {
                    if (!mAudioPolicies.containsKey(pcb.asBinder())) {
                        Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus");
                        return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
                    }
                }
            }
        }

        if (callingPackageName == null || clientId == null || aa == null) {
            Log.e(TAG, "Invalid null parameter to request audio focus");
            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
        }

        return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
                clientId, callingPackageName, flags, sdk,
                forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid()));
    }

2.5 MediaFocusControl.java 焦点控制核心

java 复制代码
// 路径: frameworks/base/services/core/java/com/android/server/audio/MediaFocusControl.java
protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
            IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
            String attributionTag, int flags, int sdk, boolean forceDuck, int testUid) {
    
            // external focus policy?
            if (mFocusPolicy != null) {
                if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) {
                    // stop handling focus request here as it is handled by external audio
                    // focus policy (return code will be handled in AudioManager)
                    return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY;
                } else {
                    // an error occured, client already dead, bail early
                    return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
                }
            }

}

    boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi,
            IAudioFocusDispatcher fd, @NonNull IBinder cb) {
        if (DEBUG) {
            Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId()
            + " dispatcher=" + fd);
        }
      
        try {
            //oneway
            mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
            return true;
        } catch (RemoteException e) {
            Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
                    + mFocusPolicy.asBinder(), e);
        }
        return false;
    }

2.6 AudioPolicy.java

java 复制代码
// 路径: frameworks/base/media/java/android/media/audiopolicy/AudioPolicy.java
    private final IAudioPolicyCallback mPolicyCb = new IAudioPolicyCallback.Stub() {
        public void notifyAudioFocusRequest(AudioFocusInfo afi, int requestResult) {
            sendMsg(MSG_FOCUS_REQUEST, afi, requestResult);
            if (DEBUG) {
                Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client="
                        + afi.getClientId() + " gen=" + afi.getGen());
            }
        }
    };
 private class EventHandler extends Handler {
        public EventHandler(AudioPolicy ap, Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case MSG_FOCUS_REQUEST:
                    if (mFocusListener != null) {
                        mFocusListener.onAudioFocusRequest((AudioFocusInfo) msg.obj, msg.arg1);
                    } else { // should never be null, but don't crash
                        Log.e(TAG, "Invalid null focus listener for focus request event");
                    }
                    break;
                default:
                    Log.e(TAG, "Unknown event " + msg.what);
            }
        }
    }

2.7 CarZonesAudioFocus.java

java 复制代码
// 路径: packages/services/Car/service/src/com/android/car/audio/CarZonesAudioFocus.java
        public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {
        CarAudioFocus focus = getFocusForAudioFocusInfo(afi);
        focus.onAudioFocusRequest(afi, requestResult);
    }

2.8 CarAudioFocus.java

java 复制代码
// 路径: packages/services/Car/service/src/com/android/car/audio/CarAudioFocus.java
     public synchronized void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {
        Log.i(TAG, "onAudioFocusRequest " + afi.getClientId());

        int response = evaluateFocusRequest(afi);

        // Post our reply for delivery to the original focus requester
        mAudioManager.setFocusRequestResult(afi, response, mAudioPolicy);
    }

2.9 AudioManager.java

java 复制代码
// 路径: frameworks/base/media/java/android/media/AudioManager.java
    public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
            @FocusRequestResult int requestResult, @NonNull AudioPolicy ap) {
        if (afi == null) {
            throw new IllegalArgumentException("Illegal null AudioFocusInfo");
        }
        if (ap == null) {
            throw new IllegalArgumentException("Illegal null AudioPolicy");
        }
        final IAudioService service = getService();
        try {
            service.setFocusRequestResultFromExtPolicy(afi, requestResult, ap.cb());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

3.1 IAudioService.aidl

java 复制代码
 // 路径: frameworks/base/media/java/android/media/IAudioService.aidl
        oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
            in IAudioPolicyCallback pcb);

3.2 AudioService.java

java 复制代码
 // 路径: frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    public void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult,
            IAudioPolicyCallback pcb) {
        if (afi == null) {
            throw new IllegalArgumentException("Illegal null AudioFocusInfo");
        }
        if (pcb == null) {
            throw new IllegalArgumentException("Illegal null AudioPolicy callback");
        }
        synchronized (mAudioPolicies) {
            if (!mAudioPolicies.containsKey(pcb.asBinder())) {
                throw new IllegalStateException("Unregistered AudioPolicy for external focus");
            }
            mMediaFocusControl.setFocusRequestResultFromExtPolicy(afi, requestResult);
        }
    }   

3.3 MediaFocusControl.java

java 复制代码
 // 路径: frameworks/base/services/core/java/com/android/server/audio/MediaFocusControl.java
     void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult) {
        synchronized (mExtFocusChangeLock) {
            if (afi.getGen() > mExtFocusChangeCounter) {
                return;
            }
        }
        final FocusRequester fr;
        if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
            fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
        } else {
            fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
        }
        if (fr != null) {
            fr.dispatchFocusResultFromExtPolicy(requestResult);
        }
    }

3.4 FocusRequester.java

java 复制代码
 // 路径: frameworks/base/services/core/java/com/android/server/audio/FocusRequester.java

    void dispatchFocusResultFromExtPolicy(int requestResult) {
        final IAudioFocusDispatcher fd = mFocusDispatcher;
        if (fd == null) {
            if (MediaFocusControl.DEBUG) {
                Log.e(TAG, "dispatchFocusResultFromExtPolicy: no focus dispatcher");
            }
            return;
        }
        if (DEBUG) {
            Log.v(TAG, "dispatching result" + requestResult + " to " + mClientId);
        }
        try {
            fd.dispatchFocusResultFromExtPolicy(requestResult, mClientId);
        } catch (android.os.RemoteException e) {
            Log.e(TAG, "dispatchFocusResultFromExtPolicy: error talking to focus listener"
                    + mClientId, e);
        }
    }

3.5 AudioManager.java

java 复制代码
 // 路径: frameworks/base/media/java/android/media/AudioManager.java
 private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
        @Override
        public void dispatchFocusResultFromExtPolicy(int requestResult, String clientId) {
            synchronized (mFocusRequestsLock) {
                // TODO use generation counter as the key instead
                final BlockingFocusResultReceiver focusReceiver =
                        mFocusRequestsAwaitingResult.remove(clientId);
                if (focusReceiver != null) {
                    focusReceiver.notifyResult(requestResult);
                } else {
                    Log.e(TAG, "dispatchFocusResultFromExtPolicy found no result receiver");
                }
            }
        }
    };    

三、调试和日志

3.1 调试方法

bash 复制代码
# 查看音频焦点状态
adb shell dumpsys audio

# 查看焦点栈
adb shell dumpsys media.audio_policy

# 启用详细日志
adb shell setprop log.tag.AudioFocus V
相关推荐
胡伯来了2 小时前
17 Transformers - 音频领域的任务类
音视频·transformer·transformers·大数据模型
TEL189246224772 小时前
IT6636:3输入1输出HDMI 2.1V重定时开关,内置MCU
音视频·实时音视频·视频编解码
简鹿视频3 小时前
怎么把mkv视频格式转换为asf视频格式
ffmpeg·音视频·实时音视频·视频编解码·格式工厂
八月的雨季 最後的冰吻3 小时前
FFmepg-- 37-ffplay源码- 播放器中音频输出模块
ffmpeg·音视频
EasyCVR3 小时前
编辑器分发RTSP地址接入到视频汇聚平台EasyCVR离线原因排查
编辑器·音视频
kkk_皮蛋3 小时前
WebRTC 视频编码基础 (VP8/VP9/H.264/AV1)
音视频·webrtc·vp8
科技小E3 小时前
EasyGBS助力平安乡村搭建无线视频联网监控系统
音视频
程序猿小郑3 小时前
Quill 编辑器自定义视频模块:将 iframe 替换为 video 标签
编辑器·音视频
线束线缆组件品替网4 小时前
TE Linx RF 物联网射频模块的 RF 线缆连接设计思路
数码相机·物联网·测试工具·电脑·音视频·pcb工艺