你好!这里是风筝的博客,
欢迎和我一起交流。
耳机上报主要有两种方式:
1.InputEvent
2.UEvent
- 输入子系统(InputEvent):可以上报按键事件也可以上报开关事件(EV_SW),事件类型包括headset、headPhone、lineOut。对于输入设备都需要指定能产生同步类事件EV_SYN.
对于按键事件,输入子系统还需要设置按键值的范围,但是对于开关类事件不需要设置。- switch class子系统:通过uevent向用户空间发送数据,Android中有个线程专门监听这类事件。使用switch dev子系统时,名字必须要设置为"h2w",Android系统监听/sys/class/switch/h2w这个虚拟设备。
两种模式的切换可以使用配置项来完成,Android 系统默认是使用UEvent的方式。
不过一般厂商会在自己的xml文件里进行配置。
例如我这板子配置:
device/softwinner/ceres-spen/overlay/frameworks/base/core/res/res/values/config.xml:140: <bool name="config_useDevInputEventForAudioJack">true</bool>
当config_useDevInputEventForAudioJack为false 则使用UEVent方式,为true则使用InputEvent方式,我这里使用的是InputEvent方式。
public WiredAccessoryManager(Context context, InputManagerService inputManager) {
//这里涉及三个服务:电源子系统、输入输出子系统、音频子系统
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryManager");
mWakeLock.setReferenceCounted(false);
mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
mInputManager = inputManager;
mContext = context;
mHdmiWakeLock=pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, "HdmiWakeLock");
mHdmiWakeLock.setReferenceCounted(false);
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
mObserver = new WiredAccessoryObserver();//创建WiredAccessoryObserver对象
}
mUseDevInputEventForAudioJack即是获取xml文件中配置的config_useDevInputEventForAudioJack,这里为true。
接着创建WiredAccessoryObserver。
WiredAccessoryObserver是WiredAccessoryManager的内部类。
class WiredAccessoryObserver extends UEventObserver {
private final List<UEventInfo> mUEventInfo;
public WiredAccessoryObserver() {
mUEventInfo = makeObservedUEventList();
}
}
继续跟makeObservedUEventList
private List<UEventInfo> makeObservedUEventList() {
List<UEventInfo> retVal = new ArrayList<UEventInfo>();
UEventInfo uei;
// Monitor h2w
if (!mUseDevInputEventForAudioJack) {
uei = new UEventInfo(NAME_H2W, BIT_HEADSET, BIT_HEADSET_NO_MIC, BIT_LINEOUT);
if (uei.checkSwitchExists()) {
retVal.add(uei);
} else {
Slog.w(TAG, "This kernel does not have wired headset support");
}
}
// Monitor USB
uei = new UEventInfo(NAME_USB_AUDIO, BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL, 0);
if (uei.checkSwitchExists()) {
retVal.add(uei);
} else {
Slog.w(TAG, "This kernel does not have usb audio support");
}
// Monitor HDMI
boolean isBOX = "box".equals(SystemProperties.get("ro.target.product", "box"));
if (!isBOX) {
uei = new UEventInfo(NAME_HDMI_AUDIO, BIT_HDMI_AUDIO, 0, 0);
if (uei.checkSwitchExists()) {
retVal.add(uei);
} else {
uei = new UEventInfo(NAME_HDMI, BIT_HDMI_AUDIO, 0, 0);
if (uei.checkSwitchExists()) {
retVal.add(uei);
} else {
Slog.w(TAG, "This kernel does not have HDMI audio support");
}
}
}
// Monitor DP
uei = new UEventInfo(NAME_DP, BIT_HDMI_AUDIO, 0, 0);
if (uei.checkSwitchExists()) {
retVal.add(uei);
} else {
Slog.w(TAG, "This kernel does not have dp audio support");
}
return retVal;
}
通过注释可知,里面主要监听各个节点,那么它是什么时候启动监听的呢?
private void startOtherServices() {
// ...
traceBeginAndSlog("StartWiredAccessoryManager");
try {
// Listen for wired headset changes
inputManager.setWiredAccessoryCallbacks(
new WiredAccessoryManager(context, inputManager));//设置WiredAccessoryCallbacks
} catch (Throwable e) {
reportWtf("starting WiredAccessoryManager", e);
}
// ...
final InputManagerService inputManagerF = inputManager;
// ...
try {
// TODO(BT) Pass parameter to input manager
if (inputManagerF != null) inputManagerF.systemRunning();//调用systemRunning,这里先记下,下面会看到用途
} catch (Throwable e) {
reportWtf("Notifying InputManagerService running", e);
}
}
在startOtherServices里面,setWiredAccessoryCallbacks的入参就是创建WiredAccessoryManager!
@InputManagerService.java
public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) {
mWiredAccessoryCallbacks = callbacks;
}
里面主要是赋值操作,就类似于mWiredAccessoryCallbacks = new WiredAccessoryManager。
全局搜下mWiredAccessoryCallbacks在哪里被调用了,还真找到一处:
// Native callback.
private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
if (DEBUG) {
Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues)
+ ", mask=" + Integer.toHexString(switchMask));
}
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
}
if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
}
if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
switchMask);//这里回调notifyWiredAccessoryChanged方法
}
if ((switchMask & SW_TABLET_MODE_BIT) != 0) {
SomeArgs args = SomeArgs.obtain();
args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
args.argi2 = (int) (whenNanos >> 32);
args.arg1 = Boolean.valueOf((switchValues & SW_TABLET_MODE_BIT) != 0);
mHandler.obtainMessage(MSG_DELIVER_TABLET_MODE_CHANGED,
args).sendToTarget();
}
}
之前我们提及,xml解析出来mUseDevInputEventForAudioJack为true,这里就会进入mWiredAccessoryCallbacks.notifyWiredAccessoryChanged
// FROM: kernal --> InputManagerService --> WiredAccessoryManager.java ---> ,,,
@Override
public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) {
if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos
+ " bits=" + switchCodeToString(switchValues, switchMask)
+ " mask=" + Integer.toHexString(switchMask));
//耳机种类
synchronized (mLock) {
int headset;
mSwitchValues = (mSwitchValues & ~switchMask) | switchValues;
switch (mSwitchValues & (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT)) {
case 0:
headset = 0;
break;
case SW_HEADPHONE_INSERT_BIT:
headset = BIT_HEADSET_NO_MIC;
break;
case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT:
headset = BIT_HEADSET;
break;
case SW_MICROPHONE_INSERT_BIT:
headset = BIT_HEADSET;
break;
default:
headset = 0;
break;
}
updateLocked(NAME_H2W, (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)) | headset);
}
}
/**
* Compare the existing headset state with the new state and pass along accordingly. Note
* that this only supports a single headset at a time. Inserting both a usb and jacked headset
* results in support for the last one plugged in. Similarly, unplugging either is seen as
* unplugging all.
*
* @param newName One of the NAME_xxx variables defined above.
* @param newState 0 or one of the BIT_xxx variables defined above.
*/
private void updateLocked(String newName, int newState) {
// Retain only relevant bits
int headsetState = newState & SUPPORTED_HEADSETS;
int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC);
boolean h2wStateChange = true;
boolean usbStateChange = true;
//打印相关信息,耳机设备名称,最新状态,更新前状态
//这里可以查看log信息
if (LOG) Slog.v(TAG, "newName=" + newName
+ " newState=" + newState
+ " headsetState=" + headsetState
+ " prev headsetState=" + mHeadsetState);
if (mHeadsetState == headsetState) {
Log.e(TAG, "No state change.");
return;
}
// reject all suspect transitions: only accept state changes from:
// - a: 0 headset to 1 headset
// - b: 1 headset to 0 headset
if (h2w_headset == (BIT_HEADSET | BIT_HEADSET_NO_MIC)) {
Log.e(TAG, "Invalid combination, unsetting h2w flag");
h2wStateChange = false;
}
// - c: 0 usb headset to 1 usb headset
// - d: 1 usb headset to 0 usb headset
if (usb_headset_anlg == BIT_USB_HEADSET_ANLG && usb_headset_dgtl == BIT_USB_HEADSET_DGTL) {
Log.e(TAG, "Invalid combination, unsetting usb flag");
usbStateChange = false;
}
if (!h2wStateChange && !usbStateChange) {
Log.e(TAG, "invalid transition, returning ...");
return;
}
mWakeLock.acquire();
//发送消息处理,告知当前状态改变,继续做更新处理。
Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
mHeadsetState, newName);
mHandler.sendMessage(msg);
//刷新保存当前最新状态
mHeadsetState = headsetState;
}
最后会通过Handler发送消息。
private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_NEW_DEVICE_STATE:
//分别对应:new_headSet_status,old_headSet_status,newName
setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
mWakeLock.release();
break;
case MSG_SYSTEM_READY:
onSystemReady();
mWakeLock.release();
break;
}
}
};
private void setDevicesState(
int headsetState, int prevHeadsetState, String headsetName) {
synchronized (mLock) {
int allHeadsets = SUPPORTED_HEADSETS;
for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
if ((curHeadset & allHeadsets) != 0) {
setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName);//继续跟踪
allHeadsets &= ~curHeadset;
}
}
}
}
private void setDeviceStateLocked(int headset,
int headsetState, int prevHeadsetState, String headsetName) {
if ((headsetState & headset) != (prevHeadsetState & headset)) {//状态改变了
int outDevice = 0;
int inDevice = 0;
int state;
//当耳机插入和拔出,上报的event中,分别是1和0因此这里的state应该是决定当前是插入还是拔出耳机
if ((headsetState & headset) != 0) {
state = 1;//插入耳机
} else {
state = 0;//拔出耳机
}
if (headset == BIT_HEADSET) {//这里就是根据耳机的类型,设置相应的音频输入和输出设备了
outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;
inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
} else if (headset == BIT_HEADSET_NO_MIC){
outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
} else if (headset == BIT_LINEOUT){
outDevice = AudioManager.DEVICE_OUT_LINE;
} else if (headset == BIT_USB_HEADSET_ANLG) {
outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
} else if (headset == BIT_USB_HEADSET_DGTL) {
outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
} else if (headset == BIT_HDMI_AUDIO) {
outDevice = AudioManager.DEVICE_OUT_HDMI;
} else {
Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
return;
}
if (LOG) {
Slog.v(TAG, "headsetName: " + headsetName +
(state == 1 ? " connected" : " disconnected"));
}
if (outDevice != 0) {
mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName);//连接输出设备
}
if (inDevice != 0) {
mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName);//连接输入设备
}
//......
}
}
看下这里是如何连接设备的:
@AudioManager.java
public void setWiredDeviceConnectionState(int type, int state, String address, String name) {
IAudioService service = getService();
try {
service.setWiredDeviceConnectionState(type, state, address, name,
mApplicationContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@AudioService.java
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);
queueMsgUnderWakeLock(mAudioHandler,
MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
0,
0,
new WiredDeviceConnectionState(type, state, address, name, caller),//最后会调用到handleMessage处理
delay);
}
}
这里的queueMsgUnderWakeLock代码实现如下:
private void queueMsgUnderWakeLock(Handler handler, int msg,
int arg1, int arg2, Object obj, int delay) {
final long ident = Binder.clearCallingIdentity();
// Always acquire the wake lock as AudioService because it is released by the
// message handler.
mAudioEventWakeLock.acquire();
Binder.restoreCallingIdentity(ident);
sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
}
handler对于MSG_SET_WIRED_DEVICE_CONNECTION_STATE消息进行处理,代码如下:
@Override
public void handleMessage(Message msg) {
// ...
case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
{ WiredDeviceConnectionState connectState =
(WiredDeviceConnectionState)msg.obj;
onSetWiredDeviceConnectionState(connectState.mType, connectState.mState,
connectState.mAddress, connectState.mName, connectState.mCaller);
mAudioEventWakeLock.release();
}
break;
break;
// ...
}
//called from AudioHandler::handleMessage(Message msg)
private void onSetWiredDeviceConnectionState(int device, int state, String address,
String deviceName, String caller) {
if (DEBUG_DEVICES) {
Slog.i(TAG, "onSetWiredDeviceConnectionState(dev:" + Integer.toHexString(device)
+ " state:" + Integer.toHexString(state)
+ " address:" + address
+ " deviceName:" + deviceName
+ " caller: " + caller + ");");
}
synchronized (mConnectedDevices) {
if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
(device == AudioSystem.DEVICE_OUT_LINE))) {
setBluetoothA2dpOnInt(true);
}
boolean isUsb = ((device & ~AudioSystem.DEVICE_OUT_ALL_USB) == 0) ||
(((device & AudioSystem.DEVICE_BIT_IN) != 0) &&
((device & ~AudioSystem.DEVICE_IN_ALL_USB) == 0));
if (!handleDeviceConnection(state == 1, device, address, deviceName)) {//关键点1:设备切换入口
// change of connection state failed, bailout
return;
}
if (state != 0) {//连接状态
if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
(device == AudioSystem.DEVICE_OUT_LINE)) {
setBluetoothA2dpOnInt(false);
}
if ((device & mSafeMediaVolumeDevices) != 0) {
sendMsg(mAudioHandler,
MSG_CHECK_MUSIC_ACTIVE,
SENDMSG_REPLACE,
0,
0,
caller,
MUSIC_ACTIVE_POLL_PERIOD_MS);
}
// Television devices without CEC service apply software volume on HDMI output
if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_HDMI;
checkAllFixedVolumeDevices();
if (mHdmiManager != null) {
synchronized (mHdmiManager) {
if (mHdmiPlaybackClient != null) {
mHdmiCecSink = false;
mHdmiPlaybackClient.queryDisplayStatus(mHdmiDisplayStatusCallback);
}
}
}
}
} else {
if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
if (mHdmiManager != null) {
synchronized (mHdmiManager) {
mHdmiCecSink = false;
}
}
}
}
if (!isUsb && device != AudioSystem.DEVICE_IN_WIRED_HEADSET) {
//可以看到耳机插入,这里发送广播给上层app去接收。systemui会接收该广播,显示耳机插入图标。
//当然可以继续往下跟。直到sendbroadcast。
sendDeviceConnectionIntent(device, state, address, deviceName);//关键点2:通过AMS上报intent
}
}
}
先看handleDeviceConnection,这里主要是对于插入耳机之后要做设备切换:
private boolean handleDeviceConnection(boolean connect, int device, String address,
String deviceName) {
if (DEBUG_DEVICES) {
Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:" + Integer.toHexString(device)
+ " address:" + address + " name:" + deviceName + ")");
}
synchronized (mConnectedDevices) {
String deviceKey = makeDeviceListKey(device, address);
if (DEBUG_DEVICES) {
Slog.i(TAG, "deviceKey:" + deviceKey);
}
DeviceListSpec deviceSpec = mConnectedDevices.get(deviceKey);
boolean isConnected = deviceSpec != null;
if (DEBUG_DEVICES) {
Slog.i(TAG, "deviceSpec:" + deviceSpec + " is(already)Connected:" + isConnected);
}
boolean isSpdifOrHdmi = ((device == AudioSystem.DEVICE_OUT_SPDIF) ||
(device == AudioSystem.DEVICE_OUT_AUX_DIGITAL));
if (isSpdifOrHdmi) {//我们既不是spdif也不是hdmi,先不用管
if (!connect) {
AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName);
mConnectedDevices.remove(deviceKey);
return true;
} else {
AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName);
mConnectedDevices.put(deviceKey, new DeviceListSpec(device, deviceName, address));
return true;
}
}
if (connect && !isConnected) {//连接处理
final int res = AudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName);
if (res != AudioSystem.AUDIO_STATUS_OK) {
Slog.e(TAG, "not connecting device 0x" + Integer.toHexString(device) +
" due to command error " + res );
return false;
}
mConnectedDevices.put(deviceKey, new DeviceListSpec(device, deviceName, address));
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,仅仅是参数不同而已!
@AudioSystem.java
public static native int setDeviceConnectionState(int device, int state,
String device_address, String device_name);
这里setDeviceConnectionState为native方法,所以去看它jni对应的实现,它的JNI映射关系为:
{"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState}
继续分析android_media_AudioSystem_setDeviceConnectionState的实现:
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::setDeviceConnectionState
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);
}
嗯,这里终于看到熟悉的aps(AudioPolicyService)了,泪目~
先获取对应的AudioPolicyService,接着调用setDeviceConnectionState
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 (!audio_is_output_device(device) && !audio_is_input_device(device)) {
return BAD_VALUE;
}
if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE &&
state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
return BAD_VALUE;
}
ALOGV("setDeviceConnectionState()");
Mutex::Autolock _l(mLock);
return mAudioPolicyManager->setDeviceConnectionState(device, state,
device_address, device_name);
}
继续调用AudioPolicyManager对应setDeviceConnectionState
status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
const char *device_name)
{
return setDeviceConnectionStateInt(device, state, device_address, device_name);
}
status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address,
const char *device_name)
{
ALOGV("setDeviceConnectionStateInt() device: 0x%X, state %d, address %s name %s",
- device, state, device_address, 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)
{
// handle output device connection
case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: {
if (index >= 0) {
ALOGW("setDeviceConnectionState() device already connected: %x", device);
return INVALID_OPERATION;
}
ALOGV("setDeviceConnectionState() connecting device %x", device);
// register new device as available
index = mAvailableOutputDevices.add(devDesc);
if (index >= 0) {
sp<HwModule> module = mHwModules.getModuleForDevice(device);
if (module == 0) {
ALOGD("setDeviceConnectionState() could not find HW module for device %08x",
device);
mAvailableOutputDevices.remove(devDesc);
return INVALID_OPERATION;
}
mAvailableOutputDevices[index]->attach(module);
} else {
return NO_MEMORY;
}
if (checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress) != NO_ERROR) {
mAvailableOutputDevices.remove(devDesc);
return INVALID_OPERATION;
}
// Propagate device availability to Engine
mEngine->setDeviceConnectionState(devDesc, state);
// outputs should never be empty here
ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():"
"checkOutputsForDevice() returned no outputs but status OK");
ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs",
outputs.size());
// Send connect to HALs
AudioParameter param = AudioParameter(devDesc->mAddress);
param.addInt(String8(AUDIO_PARAMETER_DEVICE_CONNECT), device);
mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());
} break;
// handle output device disconnection
case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: {
if (index < 0) {
ALOGW("setDeviceConnectionState() device not connected: %x", device);
return INVALID_OPERATION;
}
ALOGV("setDeviceConnectionState() disconnecting output device %x", device);
// Send Disconnect to HALs
AudioParameter param = AudioParameter(devDesc->mAddress);
param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device);
mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());
// remove device from available output devices
mAvailableOutputDevices.remove(devDesc);
checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress);
// Propagate device availability to Engine
mEngine->setDeviceConnectionState(devDesc, state);
} break;
default:
ALOGE("setDeviceConnectionState() invalid state: %x", state);
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 (size_t i = 0; i < outputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
// 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(outputs[i]);
}
}
// 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
// handle input devices
if (audio_is_input_device(device)) {
SortedVector <audio_io_handle_t> inputs;
ssize_t index = mAvailableInputDevices.indexOf(devDesc);
switch (state)
{
// handle input device connection
case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: {
if (index >= 0) {
ALOGW("setDeviceConnectionState() device already connected: %d", device);
return INVALID_OPERATION;
}
sp<HwModule> module = mHwModules.getModuleForDevice(device);
if (module == NULL) {
ALOGW("setDeviceConnectionState(): could not find HW module for device %08x",
device);
return INVALID_OPERATION;
}
if (checkInputsForDevice(devDesc, state, inputs, devDesc->mAddress) != NO_ERROR) {
return INVALID_OPERATION;
}
index = mAvailableInputDevices.add(devDesc);
if (index >= 0) {
mAvailableInputDevices[index]->attach(module);
} else {
return NO_MEMORY;
}
// Set connect to HALs
AudioParameter param = AudioParameter(devDesc->mAddress);
param.addInt(String8(AUDIO_PARAMETER_DEVICE_CONNECT), device);
mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());
// Propagate device availability to Engine
mEngine->setDeviceConnectionState(devDesc, state);
} break;
// handle input device disconnection
case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: {
if (index < 0) {
ALOGW("setDeviceConnectionState() device not connected: %d", device);
return INVALID_OPERATION;
}
ALOGV("setDeviceConnectionState() disconnecting input device %x", device);
// Set Disconnect to HALs
AudioParameter param = AudioParameter(devDesc->mAddress);
param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device);
mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());
checkInputsForDevice(devDesc, state, inputs, devDesc->mAddress);
mAvailableInputDevices.remove(devDesc);
// Propagate device availability to Engine
mEngine->setDeviceConnectionState(devDesc, state);
} break;
default:
ALOGE("setDeviceConnectionState() invalid state: %x", state);
return BAD_VALUE;
}
closeAllInputs();
// As the input device list can impact the output device selection, update
// getDeviceForStrategy() cache
updateDevicesAndOutputs();
if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
updateCallRouting(newDevice);
}
if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
cleanUpForDevice(devDesc);
}
mpClientInterface->onAudioPortListUpdate();
return NO_ERROR;
} // end if is input device
ALOGW("setDeviceConnectionState() invalid device: %x", device);
return BAD_VALUE;
}
这玩意还真有点多,避免累诉过多,见注释,这里以输出设备为例:
- 同一时间,只能连接输入或者输出设备;
- 获取设备描述符,注册新设备到mAvailableOutputDevices,绑定其HwModules;
- 通过device获取module。
这个module很重要,它对应了不同的hal层库,比如我的系统当前有三个hal层库,分别是primary,usb和a2dp(即蓝牙,支持立体声播放的),既然找到了module,那么必然要尝试按照这个device去hal层打开输出流,看是否OK,不同的module对应的device在哪里看,也就是audiopolicyservice启机时候加载的audio_policy.conf文件,如果在实际问题处理中,你认为的device没有进入到对应的hal层时,请注意在audio_policy.conf中对应的module下面去添加device。
- checkOutputsForDevice 检查设备支持profile,hal层会去打开这个output,更新output参数;
- 发送连接到HAL层;
- checkA2dpSuspend 检查是否支持a2dp;
- checkOutputForAllStrategies检查所有音频 strategy,并将不一致outputs,对应track置为无效;
- closeOutput 设备断开连接,执行checkOutputForAllStrategies()后必须关闭输出。
- updateDevicesAndOutputs 更新devices 和outputs;
- setOutputDevice 设置输出设备;
插入耳机,最重要的当然是切换设备了,所以我们重点看下checkOutputsForDevice函数:
status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> devDesc,
audio_policy_dev_state_t state,
SortedVector<audio_io_handle_t>& outputs,
const String8 address)
{
audio_devices_t device = devDesc->type();
sp<SwAudioOutputDescriptor> desc;
if (audio_device_is_digital(device)) {
// erase all current sample rates, formats and channel masks
devDesc->clearAudioProfiles();
}
if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
// first list already open outputs that can be routed to this device
//先列举出已经打开的能routed到这个device的outputs
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)) {
ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i));
outputs.add(mOutputs.keyAt(i));
} else {
ALOGV(" checking address match due to device 0x%x", device);
findIoHandlesByAddress(desc, device, address, outputs);
}
}
}
// then look for output profiles that can be routed to this device
//然后查找能routed到此device的output profiles
SortedVector< sp<IOProfile> > profiles;
for (size_t i = 0; i < mHwModules.size(); i++)
{
if (mHwModules[i]->mHandle == 0) {
continue;
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
{
sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
if (profile->supportDevice(device)) {
if (!device_distinguishes_on_address(device) ||
profile->supportDeviceAddress(address)) {
profiles.add(profile);
ALOGV("checkOutputsForDevice(): adding profile %zu from module %zu", j, i);
}
}
}
}
//......
// open outputs for matching profiles if needed. Direct outputs are also opened to
// query for dynamic parameters and will be closed later by setDeviceConnectionState()
//如果需要的话给匹配的profiles打开outputs(已经打开过就啥也不干)
for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) {
sp<IOProfile> profile = profiles[profile_index];
// nothing to do if one output is already opened for this profile
size_t j;
for (j = 0; j < outputs.size(); j++) {
desc = mOutputs.valueFor(outputs.itemAt(j));
if (!desc->isDuplicated() && desc->mProfile == profile) {
// matching profile: save the sample rates, format and channel masks supported
// by the profile in our device descriptor
if (audio_device_is_digital(device)) {
devDesc->importAudioPort(profile);
}
break;
}
}
if (j != outputs.size()) {
continue;
}
ALOGV("opening output for device %08x with params %s profile %p",
device, address.string(), profile.get());
desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
desc->mDevice = device;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = desc->mSamplingRate;
config.channel_mask = desc->mChannelMask;
config.format = desc->mFormat;
config.offload_info.sample_rate = desc->mSamplingRate;
config.offload_info.channel_mask = desc->mChannelMask;
config.offload_info.format = desc->mFormat;
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
//打开输出设备
status_t status = mpClientInterface->openOutput(profile->getModuleHandle(),
&output,
&config,
&desc->mDevice,
address,
&desc->mLatency,
desc->mFlags);
if (status == NO_ERROR) {
desc->mSamplingRate = config.sample_rate;
desc->mChannelMask = config.channel_mask;
desc->mFormat = config.format;
// Here is where the out_set_parameters() for card & device gets called
if (!address.isEmpty()) {
char *param = audio_device_address_to_parameter(device, address);
mpClientInterface->setParameters(output, String8(param));
free(param);
}
updateAudioProfiles(device, output, profile->getAudioProfiles());
if (!profile->hasValidAudioProfile()) {
ALOGW("checkOutputsForDevice() missing param");
mpClientInterface->closeOutput(output);
output = AUDIO_IO_HANDLE_NONE;
} else if (profile->hasDynamicAudioProfile()) {
mpClientInterface->closeOutput(output);
output = AUDIO_IO_HANDLE_NONE;
profile->pickAudioProfile(config.sample_rate, config.channel_mask, config.format);
config.offload_info.sample_rate = config.sample_rate;
config.offload_info.channel_mask = config.channel_mask;
config.offload_info.format = config.format;
status = mpClientInterface->openOutput(profile->getModuleHandle(),
&output,
&config,
&desc->mDevice,
address,
&desc->mLatency,
desc->mFlags);
if (status == NO_ERROR) {
desc->mSamplingRate = config.sample_rate;
desc->mChannelMask = config.channel_mask;
desc->mFormat = config.format;
} else {
output = AUDIO_IO_HANDLE_NONE;
}
}
if (output != AUDIO_IO_HANDLE_NONE) {
addOutput(output, desc);
if (device_distinguishes_on_address(device) && address != "0") {
sp<AudioPolicyMix> policyMix;
if (mPolicyMixes.getAudioPolicyMix(address, policyMix) != NO_ERROR) {
ALOGE("checkOutputsForDevice() cannot find policy for address %s",
address.string());
}
policyMix->setOutput(desc);
desc->mPolicyMix = policyMix->getMix();
} else if (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) &&
hasPrimaryOutput()) {
// no duplicated output for direct outputs and
// outputs used by dynamic policy mixes
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
// set initial stream volume for device
applyStreamVolumes(desc, device, 0, true);
//TODO: configure audio effect output stage here
// open a duplicating output thread for the new output and the primary output
duplicatedOutput =
mpClientInterface->openDuplicateOutput(output,
mPrimaryOutput->mIoHandle);
if (duplicatedOutput != AUDIO_IO_HANDLE_NONE) {
// add duplicated output descriptor
sp<SwAudioOutputDescriptor> dupOutputDesc =
new SwAudioOutputDescriptor(NULL, mpClientInterface);
dupOutputDesc->mOutput1 = mPrimaryOutput;
dupOutputDesc->mOutput2 = desc;
dupOutputDesc->mSamplingRate = desc->mSamplingRate;
dupOutputDesc->mFormat = desc->mFormat;
dupOutputDesc->mChannelMask = desc->mChannelMask;
dupOutputDesc->mLatency = desc->mLatency;
addOutput(duplicatedOutput, dupOutputDesc);
applyStreamVolumes(dupOutputDesc, device, 0, true);
} else {
ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
mPrimaryOutput->mIoHandle, output);
mpClientInterface->closeOutput(output);
removeOutput(output);
nextAudioPortGeneration();
output = AUDIO_IO_HANDLE_NONE;
}
}
}
} else {
output = AUDIO_IO_HANDLE_NONE;
}
if (output == AUDIO_IO_HANDLE_NONE) {
ALOGW("checkOutputsForDevice() could not open output for device %x", device);
profiles.removeAt(profile_index);
profile_index--;
} else {
outputs.add(output);
// Load digital format info only for digital devices
if (audio_device_is_digital(device)) {
devDesc->importAudioPort(profile);
}
if (device_distinguishes_on_address(device)) {
ALOGV("checkOutputsForDevice(): setOutputDevice(dev=0x%x, addr=%s)",
device, address.string());
setOutputDevice(desc, device, true/*force*/, 0/*delay*/,
NULL/*patch handle*/, address.string());
}
ALOGV("checkOutputsForDevice(): adding output %d", output);
}
}
if (profiles.isEmpty()) {
ALOGW("checkOutputsForDevice(): No output available for device %04x", device);
return BAD_VALUE;
}
} else { // Disconnect
// check if one opened output is not needed any more after disconnecting one device
for (size_t i = 0; i < mOutputs.size(); i++) {
desc = mOutputs.valueAt(i);
if (!desc->isDuplicated()) {
// exact match on device
if (device_distinguishes_on_address(device) &&
(desc->supportedDevices() == device)) {
findIoHandlesByAddress(desc, device, address, outputs);
} else if (!(desc->supportedDevices() & mAvailableOutputDevices.types())) {
ALOGV("checkOutputsForDevice(): disconnecting adding output %d",
mOutputs.keyAt(i));
outputs.add(mOutputs.keyAt(i));
}
}
}
// Clear any profiles associated with the disconnected device.
for (size_t i = 0; i < mHwModules.size(); i++)
{
if (mHwModules[i]->mHandle == 0) {
continue;
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
{
sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
if (profile->supportDevice(device)) {
ALOGV("checkOutputsForDevice(): "
"clearing direct output profile %zu on module %zu", j, i);
profile->clearAudioProfiles();
}
}
}
}
return NO_ERROR;
}
切换设备对于audio来说,是很重要的一个知识点,这里我也没有吃太透,后续等我理解了再专门来写吧~
不过可以简单看出,里面会调用到mpClientInterface->openOutput,这里最后的openOutput流程,就是打开输出了,可以参考之前的文章:Android音频子系统(一)------openOutput打开流程
最后看下图标显示吧~
@frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
private void updateHeadsetPlug(Intent intent) {
boolean connected = intent.getIntExtra("state", 0) != 0;
boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
Log.w(TAG, "updateHeadsetPlug connected: " + connected + " hasMic: " + hasMic);
if (connected) {
String contentDescription = mContext.getString(hasMic
? R.string.accessibility_status_bar_headset
: R.string.accessibility_status_bar_headphones);
mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic
: R.drawable.stat_sys_headset, contentDescription);
mIconController.setIconVisibility(mSlotHeadset, true);
} else {
mIconController.setIconVisibility(mSlotHeadset, false);
}
}
updateHeadsetPlug里面获取耳机挂载的广播,判断是headset(带麦耳机)还是headphones,在状态栏显示对应的图标!
headset对应R.drawable.stat_sys_headset_mic;headphones对应R.drawable.stat_sys_headset。
我的这个项目里面,耳机图标路径在:frameworks/base/packages/SystemUI/res/drawable/stat_sys_headset_mic.xml (drawable目录)
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="2.5dp"
android:insetRight="2.5dp"
android:drawable="@drawable/ic_headset_mic" />