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这个栈的栈顶。
|