Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9976
  • 博文数量: 4
  • 博客积分: 11
  • 博客等级: 民兵
  • 技术积分: 30
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-21 00:57
文章分类

全部博文(4)

文章存档

2014年(3)

2011年(1)

我的朋友

分类: Android平台

2014-04-04 11:49:03

通过之前解的一个bug,在通话过程中要切换Audio Output Path从蓝牙耳机到Speaker,但是却第一次却切换到了earpiece,再切换一次才切到Speaker,我就根据这个bug的分析,来熟悉下音频通道的切换过程;

首先还是需要看下google对于android audio系统架构分析;

http://source.android.com/devices/audio.html

android的HAL(Hardware Abstraction Layer)是连接上层的audio-specific framework APIs和底层的audio驱动和硬体的关键;下面这张图展示了这样的架构以及每层的代码分布:

一、Application framework

Application framework层级是app层的code,是通过android.media提供的API来与audio硬件进行交互动作,这部分的代码是通过audio JNI来调用native代码从而达到影响硬件的效果;

二、JNI

JNI部分的代码是位于 frameworks/base/core/jni/frameworks/base/media/jni 目录下的;

三、Native framework

Native framework定义在 frameworks/av/media/libmedia里,它与android.media 包对应并且通过IBinderIPC代理来访问底层audio服务;

四、Binder IPC

Binder IPC通信是跨进程通信的手段,audio的这部分代码位于frameworks/av/media/libmedia目录下,并且命名都是以I开头的;

五、Media Server

Audio Service是隶属Media Server的,其代码位于 frameworks/av/services/audioflinger,它是真正的与HAL层的实现进行交互的;

六、HAL

HAL层定义了Audio Service调用的标准接口,不同的硬件必须根据自己的情况来实现这个接口来让硬件在android中正常的工作,所以可以在不影响应用层系统调用的情况下,更换不同的硬件。大大减少了系统耦合性;

七、Kernel Driver

Audio驱动是与硬件进行交互,并且实现HAL层的接口供上层正常调用,这里,厂商可以选择ALSA,OSS以及自定义的音频驱动;
NOTE:如果选择ALSAandroid建议使用 external/tinyalsa目录下的实现);

接下来就来说说通话时音频通道的切换,但是往下看之前必须知道,对于Audio Path的切换,android有一策略管理器来帮我们分配好输入输出的设备,比如当手机播放音乐时,从Speaker播放出来,这时候插入耳机的话会从耳机设备输出;但是有时候我们想要自己去指定的话,就是我们接下来要说的了;

我们在通话时,要是开免提,实际上也就是Audio Path切换到了Speaker,也就是外方喇叭;代码中的话调用一个函数即可,这是强制切换audio Path,不遵从系统的分配:

1

2

AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

        audioManager.setSpeakerphoneOn(true);

中间过程简单不说,最终是调用到了JNIandroid_media_AudioSystem中的android_media_AudioSystem_setForceUse()函数,来看下其具体实现:

1

2

3

4

5

6

7

static int

android_media_AudioSystem_setForceUse(JNIEnv *env, jobject thiz, jint usage, jint config)

{

    SLOGE("jni android_media_AudioSystem_setForceUse()");

    return check_AudioSystem_Command(AudioSystem::setForceUse(static_cast <audio_policy_force_use_t>(usage),

                                                           static_cast <audio_policy_forced_cfg_t>(config)));

}

显而易见,它是调用了AudioSystem.cppsetForceUse()函数,check_AudioSystem_Command()不说,重点看看audio_policy_force_use_taudio_policy_forced_cfg_t这两个结构体;audio_policy_force_use_t说明的是当前的Audio环境,audio_policy_forced_cfg_t表示audio的输入输出设备;它们是专门为setForceUse所用的;

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

/* usages used for audio_policy->set_force_use() */

typedef enum {

    //表示的是通话过程中

    AUDIO_POLICY_FORCE_FOR_COMMUNICATION,

    //媒体

    AUDIO_POLICY_FORCE_FOR_MEDIA,

    //录音

    AUDIO_POLICY_FORCE_FOR_RECORD,

    AUDIO_POLICY_FORCE_FOR_DOCK,

    AUDIO_POLICY_FORCE_FOR_SYSTEM,

 

    AUDIO_POLICY_FORCE_USE_CNT,

    AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1,

} audio_policy_force_use_t;


 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

16

17

18

19

20

/* device categories used for audio_policy->set_force_use() */

typedef enum {

    AUDIO_POLICY_FORCE_NONE,

    AUDIO_POLICY_FORCE_SPEAKER,

    AUDIO_POLICY_FORCE_HEADPHONES,

    AUDIO_POLICY_FORCE_BT_SCO,

    AUDIO_POLICY_FORCE_BT_A2DP,

    AUDIO_POLICY_FORCE_WIRED_ACCESSORY,

    AUDIO_POLICY_FORCE_BT_CAR_DOCK,

    AUDIO_POLICY_FORCE_BT_DESK_DOCK,

    AUDIO_POLICY_FORCE_ANALOG_DOCK,

    AUDIO_POLICY_FORCE_DIGITAL_DOCK,

    AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */

    AUDIO_POLICY_FORCE_SYSTEM_ENFORCED,

 

    AUDIO_POLICY_FORCE_CFG_CNT,

    AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1,

 

    AUDIO_POLICY_FORCE_DEFAULT = AUDIO_POLICY_FORCE_NONE,

} audio_policy_forced_cfg_t;

这时候我们就应该知道,当我想要在通话时打开Speaker,传递的参数就是usageconfig分别是AUDIO_POLICY_FORCE_FOR_COMMUNICATIONAUDIO_POLICY_FORCE_SPEAKER了,这两个参数从上层一直到底层,还是很简单的;

接着往下看就是调用的AudioSystem.cppsetForceUse()函数了;

1

2

3

4

5

6

7

status_t AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)

{

    SLOGE("setForceUse() usage = %d,  config = %d" ,usage , config);

    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();

    if (aps == 0) return PERMISSION_DENIED;

    return aps->setForceUse(usage, config);

}

get_audio_policy_service()函数不做过多解释,就是通过NativeServiceManager来获取audio policyService代理对象,从而实现与audio policy的进程间通讯;

1

2

3

.......

binder = sm->getService(String16("media.audio_policy"));

.......

接下来就是调用frameworks/av/services/audioflinger/AudioPolicyService.cppsetForceUse()函数了;

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

16

17

18

19

status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage,

                                         audio_policy_forced_cfg_t config)

{

    if (mpAudioPolicy == NULL) {

        return NO_INIT;

    }

    if (!settingsAllowed()) {

        return PERMISSION_DENIED;

    }

    if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {

        return BAD_VALUE;

    }

    if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) {

        return BAD_VALUE;

    }

    Mutex::Autolock _l(mLock);

    mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config);

    return NO_ERROR;

}

这个mpAudioPolicy是什么呢?它的set_force_use函数在哪里实现呢?这两个问题需要了解就OK了;

首先mpAudioPolicy它是一个指针,在AudioServicePolicy.cpp的构造函数中被赋值,来看看其赋值过程:

1

2

3

4

5

6

7

8

9

......

const struct hw_module_t *module;

......

rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);

......

rc = audio_policy_dev_open(module, &mpAudioPolicyDev);

......

rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev,&aps_ops,this, &mpAudioPolicy);

......

首先AUDIO_POLICY_HARDWARE_MODULE_ID值是:

1

#define AUDIO_POLICY_HARDWARE_MODULE_ID "audio_policy"

其次module是一个指针,指向的是一个hw_module_t结构体类型,它的作用是调用系统的哪个audio policy module,这个module可以是原始的,也可以由厂商自定义的;

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

typedef struct hw_module_t {

/** tag must be initialized to HARDWARE_MODULE_TAG */

    uint32_t tag;

    uint16_t module_api_version;

    #define version_major module_api_version

    uint16_t hal_api_version;

#define version_minor hal_api_version

 

    /** Identifier of module */

    const char *id;

 

    /** Name of this module */

    const char *name;

 

    /** Author/owner/implementor of the module */

    const char *author;

 

    /** Modules methods */

    struct hw_module_methods_t* methods;

 

    /** module's dso */

    void* dso;

 

    /** padding to 128 bytes, reserved for future use */

    uint32_t reserved[32-7];

} hw_module_t;

再来看看是如何给module赋值的;

1

2

3

4

5

6

hardware.c

 

int hw_get_module(const char *id, const struct hw_module_t **module)

{

    return hw_get_module_by_class(id, NULL, module);

}

看看hw_get_module_by_class方法的实现:

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

hardware.c

 

int hw_get_module_by_class(const char *class_id, const char *inst,

                           const struct hw_module_t **module)

{

    int status;

    int i;

    const struct hw_module_t *hmi = NULL;

    char prop[PATH_MAX];

    char path[PATH_MAX];

    char name[PATH_MAX];

 

    if (inst)

        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);

    else

        strlcpy(name, class_id, PATH_MAX);

 

    /*

     * Here we rely on the fact that calling dlopen multiple times on

     * the same .so will simply increment a refcount (and not load

     * a new copy of the library).

     * We also assume that dlopen() is thread-safe.

     */

 

    /* Loop through the configuration variants looking for a module */

    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {

        if (i < HAL_VARIANT_KEYS_COUNT) {

            if (property_get(variant_keys[i], prop, NULL) == 0) {

                continue;

            }

            snprintf(path, sizeof(path), "%s/%s.%s.so",

                     HAL_LIBRARY_PATH2, name, prop);

            if (access(path, R_OK) == 0) break;

 

            snprintf(path, sizeof(path), "%s/%s.%s.so",

                     HAL_LIBRARY_PATH1, name, prop);

            if (access(path, R_OK) == 0) break;

        } else {

            snprintf(path, sizeof(path), "%s/%s.default.so",

                     HAL_LIBRARY_PATH1, name);

            if (access(path, R_OK) == 0) break;

        }

    }

 

    status = -ENOENT;

    if (i < HAL_VARIANT_KEYS_COUNT+1) {

        /* load the module, if this fails, we're doomed, and we should not try

         * to load a different variant. */

        status = load(class_id, path, module);

    }

 

    return status;

}

方法是找到指定的库文件并且加载;不做详细介绍;这里会得到audio_policy.default.so;这个库正是编译hardware/libhardware_legacy/audio出来的;

再跳回到AudioPolicyService的构造函数中来;接下来

1

rc = audio_policy_dev_open(module, &mpAudioPolicyDev);

它调用的是legacy_ap_dev_open()函数,不做详细介绍:

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

audio_policy_hal.cpp

 

static int legacy_ap_dev_open(const hw_module_t* module, const char* name,

                                    hw_device_t** device)

{

    struct legacy_ap_device *dev;

 

    if (strcmp(name, AUDIO_POLICY_INTERFACE) != 0)

        return -EINVAL;

 

    dev = (struct legacy_ap_device *)calloc(1, sizeof(*dev));

    if (!dev)

        return -ENOMEM;

 

    dev->device.common.tag = HARDWARE_DEVICE_TAG;

    dev->device.common.version = 0;

    dev->device.common.module = const_cast<hw_module_t*>(module);

    dev->device.common.close = legacy_ap_dev_close;

    dev->device.create_audio_policy = create_legacy_ap;

    dev->device.destroy_audio_policy = destroy_legacy_ap;

 

    *device = &dev->device.common;

 

    return 0;

}

create_audio_policy()中的aps_ops参数指针代表的是,它是AudioPolicyService与外界交互的接口:

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

16

17

18

19

20

struct audio_policy_service_ops aps_ops = {

        open_output           : aps_open_output,

        open_duplicate_output : aps_open_dup_output,

        close_output          : aps_close_output,

        suspend_output        : aps_suspend_output,

        restore_output        : aps_restore_output,

        open_input            : aps_open_input,

        close_input           : aps_close_input,

        set_stream_volume     : aps_set_stream_volume,

        set_stream_output     : aps_set_stream_output,

        set_parameters        : aps_set_parameters,

        get_parameters        : aps_get_parameters,

        start_tone            : aps_start_tone,

        stop_tone             : aps_stop_tone,

        set_voice_volume      : aps_set_voice_volume,

        move_effects          : aps_move_effects,

        load_hw_module        : aps_load_hw_module,

        open_output_on_module : aps_open_output_on_module,

        open_input_on_module  : aps_open_input_on_module,

    };

知道了这些,接下来看create_audio_policy()
create_audio_policy()
这个函数作用是创建一个用户自定义的policy_hal模块的接口,因为我们使用的是qcom的芯片,qcom有自己的一套,android原生有自己的一套,就依照原生的来看吧;其实都是差不多的;
刚刚上面分析的legacy_ap_dev_open()函数有这样一句:

1

2

3

......

dev->device.create_audio_policy = create_legacy_ap;

......

那这样我们就来看看其create_legacy_ap()函数吧;我们只需要关注的是其中的那么几小段:

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

16

17

18

19

20

21

static int create_legacy_ap(const struct audio_policy_device *device,

                            struct audio_policy_service_ops *aps_ops,

                            void *service,

                            struct audio_policy **ap)

{

struct legacy_audio_policy *lap;

......

lap = (struct legacy_audio_policy *)calloc(1, sizeof(*lap));

......

lap->policy.set_force_use = ap_set_force_use;

......

lap->service = service;

lap->aps_ops = aps_ops;

lap->service_client =

        new AudioPolicyCompatClient(aps_ops, service);

......

lap->apm = createAudioPolicyManager(lap->service_client);

......

*ap = &lap->policy;

......

}

就这样,AudioPolicyService.cppset_force_use()函数就调用到了这里:

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

audio_policy_hal.cpp

 

    /* force using a specific device category for the specified usage */

static void ap_set_force_use(struct audio_policy *pol,

                          audio_policy_force_use_t usage,

                          audio_policy_forced_cfg_t config)

{

    struct legacy_audio_policy *lap = to_lap(pol);

    lap->apm->setForceUse((AudioSystem::force_use)usage,

                          (AudioSystem::forced_config)config);

}

从之前的create_legacy_ap()函数我们知道apm的由来,

1

lap->apm = createAudioPolicyManager(lap->service_client);

createAudioPolicyManager()函数定义在AudioPolicyInterface.h接口中;

1

extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);

而这个createAudioPolicyManager()由硬件厂商实现,返回其AudioPolicyManagerqcom的实现是在AudioPolicyManagerALSA.cpp中;再往下不做具体分析了,主要是根据不同的策略来切换不同的Outputinput设备以及其他一些操作;如果想进一步分析的话,还需要关注AudioPolicyManagerBase.cpp

其实准确的总结起来是AudioPolicyService是一个壳子,这个壳子的重要关键就是audio_policy,真正的实现可以由厂商来自己实现,当然android也有,就是AudioPolicyManagerDefault

现在回到我的bug中来,bug是这样的,连接蓝牙耳机,在通话过程中,选择切换到Speaker时,output切换到了Earpiece,再进行一次切换,才会成功切换到Speaker,从下面的log可以看出来,audio path最后是切换到了0x1,而且中间也切到了0x2,又被谁给切到了0x1,这里是关键;(0x1代表Earpiece0x2则代表Speaker0x20代表的是蓝牙耳机)

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

01-02 10:50:26.355 D/ALSADevice(  307): route: devices 0x1 in mode 2

01-02 10:50:26.605 D/ALSADevice(  307): route: devices 0x1 in mode 2

01-02 10:50:27.306 D/ALSADevice(  307): route: devices 0x20 in mode 2

01-02 10:50:27.416 D/ALSADevice(  307): route: devices 0x20 in mode 2

01-02 10:50:27.436 D/ALSADevice(  307): route: devices 0x20 in mode 2

01-02 10:50:27.446 D/ALSADevice(  307): route: devices 0x20 in mode 2

01-02 10:50:33.262 D/ALSADevice(  307): route: devices 0x2 in mode 2

01-02 10:50:33.442 D/ALSADevice(  307): route: devices 0x2 in mode 2

01-02 10:50:33.582 D/ALSADevice(  307): route: devices 0x1 in mode 2

01-02 10:50:33.883 D/ALSADevice(  307): route: devices 0x1 in mode 2

01-02 10:50:33.913 D/ALSADevice(  307): route: devices 0x1 in mode 2

01-02 10:50:33.923 D/ALSADevice(  307): route: devices 0x1 in mode 2

中间的分析过程就不说了,直接说结果吧;首先,在Bluetoothapp中有一个HeadsetStateMachine.java,它的作用类似于BTWIFI的状态机,在我从蓝牙耳机切换到Speaker的时候,它受到了EVENT_TYPE_AUDIO_STATE_CHANGED的状态消息的回馈,消息接收到以后,其case走到HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:,因为这时候蓝牙耳机断开;

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:

                    if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {

                        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 

                mAudioManager.setBluetoothScoOn(false);

                        if (mA2dpSuspend) {

                            if ((!isInCall()) && (mPhoneState.getNumber().isEmpty())) {

                                log("Audio is closed,Set A2dpSuspended=false");

                                mAudioManager.setParameters("A2dpSuspended=false");

                                mA2dpSuspend = false;

                            }

                        }

                        broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,

                                            BluetoothHeadset.STATE_AUDIO_CONNECTED);

                    }

                    transitionTo(mConnected);

最悲惨的在于mAudioManager.setBluetoothScoOn(false);mAudioManager.setSpeakerphoneOn(true);一样也是强制切换audio Path,所以这里的setBluetoothScoOn(false)会把audio path的输出设备切换到NONE的状态,底层的处理是在FOR_COMMUNICATION情况下,NONE会切到Earpiece;所以才会导致第一次切换会到Speaker上的情况,而第二次切换并不会收到EVENT_TYPE_AUDIO_STATE_CHANGED的状态消息,也就会正常切换到Speaker,所以这里需要修改成:

1

2

3

4

5

6

if(mAudioManager.isSpeakerphoneOn()){

                            mAudioManager.setBluetoothScoOn(false);

                            mAudioManager.setSpeakerphoneOn(true);

                        } else {

                            mAudioManager.setBluetoothScoOn(false);

                        }

判断SpeakerOn的状态,如果为true,则在关闭Bluetooth SCO通路后,再打开Speaker,这样就会重新又会切换到Speaker,测试成功;

最后再说一下,在分析这个bug的时候在stackoverflow上搜索到的一个问题解决办法把我拉进了深巷,当然,其不是这个bug的解决办法,但是对于有些机器setSpeakerphoneOn调用没有作用的问题可能是一个解答;

 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

if setSpeakerOn(true) is not useful;

Class audioSystemClass = Class.forName("android.media.AudioSystem");

Method setForceUse = audioSystemClass.getMethod("setForceUse", int.class, int.class);

// First 1 == FOR_MEDIA, second 1 == FORCE_SPEAKER. To go back to the default

// behavior, use FORCE_NONE (0).

setForceUse.invoke(null, 1, 1);

The combination FOR_MEDIA, FORCE_SPEAKER is typically only

used internally to route the FM-radio audio to the loudspeaker (since

the FM-radio requires you to have a wired headset / headphone plugged in

 to act as an antenna). Devices that don't have FM-radio functionality

(or uses an alternative implementation) might ignore this combination of

 parameters, so this method would not work on such a device.

声明:eoe文章著作权属于作者,受法律保护,转载时请务必以超链接形式附带如下信息

原文作者: 

原文地址: 

 


阅读(1604) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~