Android 音频源码分析——音频设备切换(插入耳机)

Posted VNanyesheshou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 音频源码分析——音频设备切换(插入耳机)相关的知识,希望对你有一定的参考价值。

源码分析基于android9.0

通常带线耳机分类

  • 模拟耳机
    平时常用的3.5mm或6.3mm接口耳机,接收模拟信号(音频数据需要先处理,转码成pcm格式)
    android中模拟耳机由WiredAccessoryManager获取上报的事件,调用AudioService.setWiredDeviceConnectionState传递信息给Audioservice,更新设备信息。
  • 数字耳机
    例如USB Type-c耳机,接收数字信号(音频数据不需要解码成pcm,由耳机进行转换)
    Android中由UsbAlsaManager负责usb音频设备连接断开事件,调用AudioService.setWiredDeviceConnectionState传递信息给AudioService。

使用扬声器播放音乐,此时插入耳机,则音乐切换从耳机播放出。这里以插入数字耳机为例分析下音频设备切换流程。

一、Framework层

当设备插入usb 耳机,UsbHostManager会接收到usb设备事件调用usbDeviceAdded函数,调到UsbAlsaManager.usbDeviceAdded函数。

UsbAlsaManager.java

void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice,
        UsbDescriptorParser parser) 
    //...
    if (hasInput || hasOutput) 
        
        UsbAlsaDevice alsaDevice =
                    new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/,
                                      deviceAddress, hasOutput, hasInput,
                                      isInputHeadset, isOutputHeadset);
            if (alsaDevice != null) 
                alsaDevice.setDeviceNameAndDescription(
                          cardRec.getCardName(), cardRec.getCardDescription());
                mAlsaDevices.add(0, alsaDevice);
                selectAlsaDevice(alsaDevice);
            
    


private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) 
    //.......
    mSelectedDevice = alsaDevice;
    alsaDevice.start();

  • 创建UsbAlsaDevice,并设置信息;
  • 设置为mSelectedDevice;
  • 调用UsbAlsaDevice start,更新设备连接状态;

接着看UsbAlsaDevice start

public synchronized void start() 
    mSelected = true;
    mInputState = 0;
    mOutputState = 0;
    startJackDetect();
    updateWiredDeviceConnectionState(true);


public synchronized void updateWiredDeviceConnectionState(boolean enable) 
    //.......
     if (mHasOutput) 
         mAudioService.setWiredDeviceConnectionState(device, outputState,
                                                                alsaCardDeviceString,
                                                                mDeviceName, TAG);
     
    
    if (mHasInput) 
        mAudioService.setWiredDeviceConnectionState(
                            device, inputState, alsaCardDeviceString,
                            mDeviceName, TAG);
    

调用AudioService,设置设备连接状态。

AudioService

接下来分析AudioService中流程

public void setWiredDeviceConnectionState(int type, int state, String address, String name,
        String caller) 
    synchronized (mConnectedDevices) 
        if (DEBUG_DEVICES) 
            Slog.i(TAG, "setWiredDeviceConnectionState(" + state + " nm: " + name + " addr:"
                    + address + ")");
        
        int delay = checkSendBecomingNoisyIntent(type, state, AudioSystem.DEVICE_NONE);
        queueMsgUnderWakeLock(mAudioHandler,
                MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
                0 /* arg1 unused */,
                0 /* arg2 unused */,
                new WiredDeviceConnectionState(type, state, address, name, caller),
                delay);
    

发送Message消息,由AudioHandler处理

public void handleMessage(Message msg) 
	switch (msg.what) 
        case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
               WiredDeviceConnectionState connectState =
                (WiredDeviceConnectionState)msg.obj;
             mDeviceLogger.log(new WiredDevConnectEvent(connectState));
             onSetWiredDeviceConnectionState(connectState.mType, connectState.mState,
                                             connectState.mAddress, connectState.mName, connectState.mCaller);
             mAudioEventWakeLock.release();
            
            break;
    

接着看onSetWiredDeviceConnectionState函数

private void onSetWiredDeviceConnectionState(int device, int state, String address,
        String deviceName, String caller) 
    synchronized (mConnectedDevices) 
        if ((state == 0) && ((device & DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG) != 0)) 
            setBluetoothA2dpOnInt(true, "onSetWiredDeviceConnectionState state 0");
        

        if (!handleDeviceConnection(state == 1, device, address, deviceName)) 
            // change of connection state failed, bailout
            return;
        
        //..... 
    
   
    sendDeviceConnectionIntent(device, state, address, deviceName);
    updateAudioRoutes(device, state);

  • 如果是设备拔出,则设置A2dp;
  • handleDeviceConnection 处理设备连接;
  • 发送广播、更新audio通路;

接着分析handleDeviceConnection

private boolean handleDeviceConnection(boolean connect, int device, String address,
        String deviceName) 
     synchronized (mConnectedDevices) 
            String deviceKey = makeDeviceListKey(device, address);
            DeviceListSpec deviceSpec = mConnectedDevices.get(deviceKey);
            boolean isConnected = deviceSpec != null;
            
            if (connect && !isConnected) 
                //连接 处理
                final int res = AudioSystem.setDeviceConnectionState(device,
                        AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName);
                if (res != AudioSystem.AUDIO_STATUS_OK) 
                    return false;
                
                mConnectedDevices.put(deviceKey, new DeviceListSpec(device, deviceName, address));
                sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
                        device, 0, null, 0);
                return true;
             else if (!connect && isConnected) 
                //断开连接 处理
                AudioSystem.setDeviceConnectionState(device,
                        AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName);
                // always remove even if disconnection failed
                mConnectedDevices.remove(deviceKey);
                return true;
            
        
        return false;

调用AudioSystem.setDeviceConnectionState处理状态,连接或者断开连接都调用此流程,只是参数不同,连接对应DEVICE_STATE_AVAILABLE。

接着看AudioSystem

AudioSystem

AudioSystem.java

public static native int setDeviceConnectionState(int device, int state,
                                                  String device_address, String device_name);

setDeviceConnectionState为native方法,直接看jni对应实现

android_media_AudioSystem.cpp

static jint
android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name)

    const char *c_address = env->GetStringUTFChars(device_address, NULL);
    const char *c_name = env->GetStringUTFChars(device_name, NULL);
    int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast <audio_devices_t>(device),
                                          static_cast <audio_policy_dev_state_t>(state),
                                          c_address, c_name));
    env->ReleaseStringUTFChars(device_address, c_address);
    env->ReleaseStringUTFChars(device_name, c_name);
    return (jint) status;

这里继续向下追查

AudioSystem.cpp

status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
                                               audio_policy_dev_state_t state,
                                               const char *device_address,
                                               const char *device_name)

    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    const char *address = "";
    const char *name = "";

    if (aps == 0) return PERMISSION_DENIED;

    if (device_address != NULL) 
        address = device_address;
    
    if (device_name != NULL) 
        name = device_name;
    
    return aps->setDeviceConnectionState(device, state, address, name);

  • 获取AudioPolicyService;
  • 快进程调用AudioPolicyService setDeviceConnectionState;

二、audioserver

status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
                                                  audio_policy_dev_state_t state,
                                                  const char *device_address,
                                                  const char *device_name)

    if (mAudioPolicyManager == NULL) 
        return NO_INIT;
    
    if (!settingsAllowed()) 
        return PERMISSION_DENIED;
    
    if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE &&
            state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) 
        return BAD_VALUE;
    

    ALOGV("setDeviceConnectionState()");
    Mutex::Autolock _l(mLock);
    AutoCallerClear acc;
    return mAudioPolicyManager->setDeviceConnectionState(device, state,
                                                         device_address, device_name);

  • 检查mAudioPolicyManager、权限、state;
  • 调用AudioPolicyManager对应setDeviceConnectionState;

AudioPolicyManager

setDeviceConnectionState调用setDeviceConnectionStateInt。

status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
                                                      audio_policy_dev_state_t state,
                                                      const char *device_address,
                                                      const char *device_name)

    status_t status = setDeviceConnectionStateInt(device, state, device_address, device_name);
    nextAudioPortGeneration();
    return status;


status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
                                                         audio_policy_dev_state_t state,
                                                         const char *device_address,
                                                         const char *device_name)

        // connect/disconnect only 1 device at a time
    if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE;

    sp<DeviceDescriptor> devDesc =
            mHwModules.getDeviceDescriptor(device, device_address, device_name);

    // 处理输出设备
    if (audio_is_output_device(device)) 
        SortedVector <audio_io_handle_t> outputs;

        ssize_t index = mAvailableOutputDevices.indexOf(devDesc);

        // 在通过checkOutputsForDevice()打开或关闭任何输出之前,请保存打开的输出描述符的副本。 checkOutputForAllStrategies()将需要它
        mPreviousOutputs = mOutputs;
        switch (state)
        
        // 处理输出设备连接
        case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: 
            if (index >= 0) 
                return INVALID_OPERATION;
            

            // register new device as available
            index = mAvailableOutputDevices.add(devDesc);
            if (index >= 0) 
                sp<HwModule> module = mHwModules.getModuleForDevice(device);
                if (module == 0) 
                    mAvailableOutputDevices.remove(devDesc);
                    return INVALID_OPERATION;
                
                mAvailableOutputDevices[index]->attach(module);
             else 
                return NO_MEMORY;
            

            // Before checking outputs, broadcast connect event to allow HAL to retrieve dynamic
            // parameters on newly connected devices (instead of opening the outputs...)
            broadcastDeviceConnectionState(device, state, devDesc->mAddress);

            if (checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress) != NO_ERROR) 
                mAvailableOutputDevices.remove(devDesc);
                broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
                                               devDesc->mAddress);
                return INVALID_OPERATION;
            
            // Propagate device availability to Engine
            mEngine->setDeviceConnectionState(devDesc, state);
             break;
        // 处理输出设备断开连接
        case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: 
            //....
             break;
        default:
            return BAD_VALUE;
        

        // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP
        // output is suspended before any tracks are moved to it
        checkA2dpSuspend();
        checkOutputForAllStrategies();
        // outputs must be closed after checkOutputForAllStrategies() is executed
        if (!outputs.isEmpty()) 
            for (audio_io_handle_t output : outputs) 
                sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(output);
                // close unused outputs after device disconnection or direct outputs that have been
                // opened by checkOutputsForDevice() to query dynamic parameters
                if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
                        (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
                         (desc->mDirectOpenCount == 0))) 
                    closeOutput(output);
                
            
            // check again after closing A2DP output to reset mA2dpSuspended if needed
            checkA2dpSuspend();
        

        updateDevicesAndOutputs();
        if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) 
            audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
            updateCallRouting(newDevice);
        
        for (size_t i = 0; i < mOutputs.size(); i++) 
            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
            if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) 
                audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/);
                // do not force device change on duplicated output because if device is 0, it will
                // also force a device 0 for the two outputs it is duplicated to which may override
                // a valid device selection on those outputs.
                bool force = !desc->isDuplicated()
                        && (!device_distinguishes_on_address(device)
                                // always force when disconnecting (a non-duplicated device)
                                || (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
                setOutputDevice(desc, newDevice, force, 0);
            
        

        if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) 
            cleanUpForDevice(devDesc);
        

        mpClientInterface->onAudioPortListUpdate();
        return NO_ERROR;
      // end if is output device

分析setDeviceConnectionStateInt

  • 注册新设备到mAvailableOutputDevices,并绑定其HWModules;
  • broadcastDeviceConnectionState 在检查outputs前,广播连接事件以允许hal在新连接的设备上检索动态参数(而不是打开输出);
  • checkOutputsForDevice 检查设备支持profile,并打开output,更新output参数;
  • checkA2dpSuspend 检查a2dp;
  • checkOutputForAllStrategies检查所有音频 strategy,并将不一致outputs,对应track置为无效;
  • closeOutput 设备断开连接或checkOutputsForDeivce中打开的direct outputs,则进行关闭。
  • updateDevicesAndOutputs 更新devices 和outputs;
  • setOutputDevice 设置输出设备;

1 checkOutputsForDevice

检查device的outputs。

status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor>& devDesc,
                                                   audio_policy_dev_state_t state,
                                                   SortedVector<audio_io_handle_t>& outputs,
                                                   const String8& address)

    //.........
    if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) 
        // first list already open outputs that can be routed to this device
        for (size_t i = 0; i < mOutputs.size(); i++) 
            desc = mOutputs.valueAt(i);
            if (!desc->isDuplicated() && (desc->supportedDevices() & device)) 
                if (!device_distinguishes_on_address(device)) 
                    outputs.add(mOutputs.keyAt(i));
                 else 
                    findIoHandlesByAddress(desc, device, address, outputs);
                
            Android 音频源码分析——音频设备切换(插入耳机)

Android 音频源码分析——音频设备切换(插入耳机)

Android 音频源码分析——AudioTrack设备选择

Android 音频源码分析——AudioTrack设备选择

Android 音频源码分析——音量调节流程

Android 音频源码分析——音量调节流程