免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2930 | 回复: 0
打印 上一主题 下一主题

14-Android MID Alsa HAL [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-21 08:41 |只看该作者 |倒序浏览
1.耳机监听
系统服务(Systemserver.java)启动,并新建线程(ServerThread.java)开始服务HeadsetObserver。

class HeadsetObserver extends UeventObserver { 
…… 
public HeadsetObserver(Context context) { 
…… 
startObserving(HEADSET_UEVENT_MATCH); 
init(); 
 
2.运行以上程序后会一直监测HEADSET_UEVENT_MATCH路径的事件, 
HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w"; 
如果有事件的变化,则会调用
public void onUEvent(UEventObserver.UEvent event) {
        if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());

        try {
update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
        } catch (NumberFormatException e) {
            Slog.e(TAG, "Could not parse switch state from event " + event);
        }
}
 
3.该方法会从
HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
HEADSET_NAME_PATH = "/sys/class/switch/h2w/name";
路径中获得耳机设备的设备名称和设备状态,并作为参数传输至
private synchronized final void update(String newName, int newState) { 
…… 
mHandler.sendMessageDelayed(mHandler.obtainMessage(0,mHeadsetState, 
mPrevHeadsetState, 
mHeadsetName), 
}

4.最终调用
private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
if ((headsetState & headset) != (prevHeadsetState & headset)) {
//  Pack up the values and broadcast them to everyone
ntent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
int state = 0;
int microphone = 0;

if ((headset & HEADSETS_WITH_MIC) != 0) {
microphone = 1;
}
if ((headsetState & headset) != 0) {
state = 1;
}
intent.putExtra("state", state);
intent.putExtra("name", headsetName);
intent.putExtra("microphone", microphone);
       ……
ActivityManagerNative.broadcastStickyIntent(intent, null);
}
}
并广播一个包含设备信息的意图。


5.AudioService.java将收到这个广播
private class AudioServiceBroadcastReceiver extends BroadcastReceiver  { 
public void onReceive(Context context, Intent intent)  { 
…… 
else if (action.equals(Intent.ACTION_HEADSET_PLUG))  { 
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,AudioSystem.DEVICE_STATE_AVAILABLE,""); 
}

6.经过JNI层后将调用AudioSystem.cpp中的setDeviceConnectionState,
status_t AudioSystem::setDeviceConnectionState(audio_devices device,
                                                  device_connection_state state,
                                                  const char *device_address)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;

    return aps->setDeviceConnectionState(device, state, device_address);
}

7.get_audio_policy_service()这个函数会获取相应的policy的实例,我们的开发板中就会使用AudioPolicyManagerALSA。然后调用AudioPolicyManagerALSA中的setDeviceConnectionState函数。

8.分析这个函数先要先说明几个相关的枚举类型:
enum routing_strategy {
STRATEGY_MEDIA, //多媒体
STRATEGY_PHONE, //通话
STRATEGY_SONIFICATION, //铃声、提提醒音
STRATEGY_DTMF, //按键音
NUM_STRATEGIES //枚举量
};
 
9.setDeviceConnectionState函数:
status_t AudioPolicyManagerALSA::setDeviceConnectionState(
AudioSystem::audio_devices device, 
AudioSystem::device_connection_state state, 
const char *device_address)
 
函数首先从参数中的state来判断设备是否可用。如果设备是可用的,再判断设备是蓝牙设备还是普通耳机。再以isUsedByStrategy()判断此设备是属于哪个strategy的。
isUsedByStrategy()会以strategy作为参数调用getDeviceForStrategy()。getDeviceForStrategy()会根据相应的strategy,返回该strategy的设备列表中的可用的设备。
把新设备与strategy的设备对比,如果是一致的,则把该strategy所属的设备作为参数调用setOutputDevice()。
(在setOutputDevice()中,如果设备是耳机和OUT_SPEAKER都选择了,就会先把STRATEGY_MEDIA中的数据流全部静音。反之,如果之前是耳机和OUT_SPEAKER都被选择了,但现在不是就会取消STRATEGY_MEDIA数据流的静音。)
 // do the routing
    AudioParameter param = AudioParameter();
    param.addInt(String8(AudioParameter::keyRouting), (int)device);
    mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);

10.通过AudioPolicyService中的setParameters(),

void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
                                       const String8& keyValuePairs,
                                       int delayMs)
{
    mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs);
}

调用AudioCommandThread::parametersCommand()这个函数,向AudioPolicyService的命令队列中增加一个设置参数的命令。
status_t AudioPolicyService::AudioCommandThread::parametersCommand(
int ioHandle, 
const String8& keyValuePairs,
int delayMs)
{
……
  AudioCommand *command = new AudioCommand();
    command->mCommand = SET_PARAMETERS;
    ParametersData *data = new ParametersData();
    data->mIO = ioHandle;
    data->mKeyValuePairs = keyValuePairs;
command->mParam = data;
……
insertCommand_l(command, delayMs);
}
 
11.AudioCommandThread::threadLoop()会根据命令队列中的命令,当分析到刚才插入的命令时,会判断出其属于设置参数的命令,因而调用AudioSystem::setParameters()。它会获取AudioFlinger的实例并调用其setParameters()函数。
status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
{
   ……
   result = mAudioHardware->setParameters(keyValuePairs);
   ……
}
 
12.接着,在ALSA中就会调用ALSAStreamOps::setParameters()函数。
status_t ALSAStreamOps::setParameters(const String8& keyValuePairs)
{
    ……
    if (param.getInt(key, device) == NO_ERROR) {
        mParent->mALSADevice->route(mHandle, (uint32_t)device, mParent->mode());
        param.remove(key);
    }
……
}

13.最后一步就是调用Alsa_module.cpp里面的s_route()来对alsa-lib进行操作。
static status_t s_route(alsa_handle_t *handle, uint32_t devices, int mode)
{
    ……
    if (handle->handle && handle->curDev == devices && handle->curMode == mode)
    else if (handle->handle && (handle->devices & devices)) {
        setAlsaControls(handle, devices, mode);
    }
    else {
        status = s_open(handle, devices, mode);
    }
    return status;
}
 
========================================================================
从这里开始可以有两种方法对alsa-lib进行操作:
14.直接在源码中对ALSA进行操作
在s_route()中调用一个自己编写的函数,根据当前设备的需求利用ALSAControl直接配置ALSA,例如:
void setDefaultControls(uint32_t devices, int mode)
{
LOGV("%s", __FUNCTION__);
    ALSAControl control("hw:00");
    if (devices & 0x0000FFFF){
        if (devices & AudioSystem::DEVICE_OUT_SPEAKER) {
            control.set("HeadsetR Mixer AudioR2", 1); // on
            control.set("HeadsetL Mixer AudioL2", 1); // on
        } else {
            control.set("HeadsetR Mixer AudioR2", (unsigned int)0); // off
            control.set("HeadsetL Mixer AudioL2", (unsigned int)0); // off
        }
}
……
}

15.利用ALSA的配置文件进行操作
s_route()中会调用本地的s_open()函数打开设备,其中会调用devieName()来获取设备名称。此时可以修改deviceName()来命名设备,如:
snd_pcm_stream_t direction(alsa_handle_t *handle)
{
    return (handle->devices & AudioSystem::DEVICE_OUT_ALL) ? SND_PCM_STREAM_PLAYBACK: SND_PCM_STREAM_CAPTURE;
}

const char *deviceName(alsa_handle_t *handle, uint32_t device, int mode)
{
……
    if (SND_PCM_STREAM_PLAYBACK == direction(handle))
        return "XIAOMING";//名称自定义
……
}

然后s_open()就会根据ALSA配置文件(asound.conf或者.asoundrc)中的对应的配置来配置ALSA,如:
pcm.XIAOMING {   //名称和上面的return一致
        type hooks
        slave.pcm {
                type hw
                card 0
                device 0
        }
        hooks.0 {
                type ctl_elems
                hook_args [
                {
                        name 'DAC2 Analog Playback Volume'
                        value.0 9
                        value.1 9
                }
              ……
}
}

16.来电响应
在AudioService中会向TelephonyManager注册一个电话状态监听器PhoneStateListener,用来监听当前电话状态。当有来电的时候,即当前电话状态为CALL_STATE_RINGING时,监听器就会获取当前设置的铃声音量,如果音量大于零,则调用requestAudioFocus()。
requestAudioFocus()会把播放铃声数据这个请求放在FocusStackEntry这个栈的栈顶。

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP