Android所有的input设备都会在/dev/input目录下生成对应的设备节点,一旦有任何输入事件产生,便会将事件写到这些节点下,同时对于外部输入设备(鼠标键盘等)的插拔还会引起这些节点的创建和删除
使用adb 命令可以查看当前手机插入的耳机状态,命令为:adb shell cat /sys/class/switch/h2w/state
耳机拔插属于switch类型的按键消息,对于驱动层面,会先到InputDispatcher.cpp中处理
void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("notifySwitch - eventTime=%lld, policyFlags=0x%x, switchValues=0x%08x, switchMask=0x%08x",
args->eventTime, args->policyFlags,
args->switchValues, args->switchMask);
#endif
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
mPolicy->notifySwitch(args->eventTime,
args->switchValues, args->switchMask, policyFlags);
}
上述mPolicy就是NativeInputManager,在com_android_server_input_InputManagerService.cpp中定义的NativeInputManager
class NativeInputManager : public virtual RefBase,
public virtual InputReaderPolicyInterface,
public virtual InputDispatcherPolicyInterface,
public virtual PointerControllerPolicyInterface {
....
....
/* --- InputDispatcherPolicyInterface implementation --- */
virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
uint32_t policyFlags);
virtual void notifyConfigurationChanged(nsecs_t when);
virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
}
所以上述的调用,会在 com_android_server_input_InputManagerService.cpp中继续处理
void NativeInputManager::notifySwitch(nsecs_t when,
uint32_t switchValues, uint32_t switchMask, uint32_t /* policyFlags */) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifySwitch - when=%lld, switchValues=0x%08x, switchMask=0x%08x, policyFlags=0x%x",
when, switchValues, switchMask, policyFlags);
#endif
ATRACE_CALL();
JNIEnv* env = jniEnv();
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifySwitch,
when, switchValues, switchMask);
checkAndClearExceptionFromCallback(env, "notifySwitch");
}
可以看到,上述直接通过CallVoidMethod调用java代码,其中gServiceClassInfo.notifySwitch对应的就是InputManagerService#notifySwitch方法
// 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 (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
switchMask);
}
....
}
上述方法调用有一个mWiredAccessoryCallbacks回调,耳机拔插事件,需要从开机以后就实时监听,所以不想到,mWiredAccessoryCallbacks回调的设置是和系统服务启动时机一样,在SystemServer中可以看到它的身影,在startOtherServices中,如下:
try {
// Listen for wired headset changes
inputManager.setWiredAccessoryCallbacks(
new WiredAccessoryManager(context, inputManager));
} catch (Throwable e) {
reportWtf("starting WiredAccessoryManager", e);
}
上述mWiredAccessoryCallbacks回调其实是WiredAccessoryManager类型对象,继续分析WiredAccessoryManager#notifyWiredAccessoryChanged
@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 | SW_LINEOUT_INSERT_BIT)) {
case 0:
headset = 0;
break;
// 不带mic的耳机
case SW_HEADPHONE_INSERT_BIT:
headset = BIT_HEADSET_NO_MIC;
break;
case SW_LINEOUT_INSERT_BIT:
headset = BIT_LINEOUT;
break;
case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT:
headset = BIT_HEADSET;
break;
// 带mic的耳机
case SW_MICROPHONE_INSERT_BIT:
headset = BIT_HEADSET;
break;
default:
headset = 0;
break;
}
updateLocked(NAME_H2W,
(mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);
}
}
上述流程,主要先获取headset的类型,最终会跳到updateLocked方法,用来检查当前模式是否发生变化
private void updateLocked(String newName, int newState) {
....
Log.i(TAG, "MSG_NEW_DEVICE_STATE");
Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
mHeadsetState, "");
mHandler.sendMessage(msg);
mHeadsetState = headsetState;
}
发送 消息交给mHandler处理
private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_NEW_DEVICE_STATE:
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)) {
....
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);
}
}
}
上述mAudioManager会跨进程通信,最终调用到AudioService#setWiredDeviceConnectionState
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);
}
}
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);
}
上述msg对应的是MSG_SET_WIRED_DEVICE_CONNECTION_STATE消息,该消息在AudioService$AudioHandler处理
case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
{ WiredDeviceConnectionState connectState =
(WiredDeviceConnectionState)msg.obj;
mWiredDevLogger.log(new WiredDevConnectEvent(connectState));
onSetWiredDeviceConnectionState(connectState.mType, connectState.mState,
connectState.mAddress, connectState.mName, connectState.mCaller);
mAudioEventWakeLock.release();
}
break;
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 & 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;
}
if (state != 0) {
if ((device & DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG) != 0) {
setBluetoothA2dpOnInt(false, "onSetWiredDeviceConnectionState state not 0");
}
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;
}
}
}
}
sendDeviceConnectionIntent(device, state, address, deviceName);
updateAudioRoutes(device, state);
}
}
最后就是通过 ActivityManager发送ACTION_HEADSET_PLUG广播
private void sendDeviceConnectionIntent(int device, int state, String address,
String deviceName) {
if (DEBUG_DEVICES) {
Slog.i(TAG, "sendDeviceConnectionIntent(dev:0x" + Integer.toHexString(device) +
" state:0x" + Integer.toHexString(state) + " address:" + address +
" name:" + deviceName + ");");
}
Intent intent = new Intent();
if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 1);
} else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE ||
device == AudioSystem.DEVICE_OUT_LINE) {
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 0);
} else if (device == AudioSystem.DEVICE_OUT_USB_HEADSET) {
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone",
AudioSystem.getDeviceConnectionState(AudioSystem.DEVICE_IN_USB_HEADSET, "")
== AudioSystem.DEVICE_STATE_AVAILABLE ? 1 : 0);
} else if (device == AudioSystem.DEVICE_IN_USB_HEADSET) {
if (AudioSystem.getDeviceConnectionState(AudioSystem.DEVICE_OUT_USB_HEADSET, "")
== AudioSystem.DEVICE_STATE_AVAILABLE) {
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 1);
} else {
// do not send ACTION_HEADSET_PLUG when only the input side is seen as changing
return;
}
} else if (device == AudioSystem.DEVICE_OUT_HDMI ||
device == AudioSystem.DEVICE_OUT_HDMI_ARC) {
configureHdmiPlugIntent(intent, state);
}
if (intent.getAction() == null) {
return;
}
intent.putExtra(CONNECT_INTENT_KEY_STATE, state);
intent.putExtra(CONNECT_INTENT_KEY_ADDRESS, address);
intent.putExtra(CONNECT_INTENT_KEY_PORT_NAME, deviceName);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
final long ident = Binder.clearCallingIdentity();
try {
ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL); //向上层发送广播
} finally {
Binder.restoreCallingIdentity(ident);
}
}
可以看到,上述在AudioService中发送广播,并不是通过Context上下文来发送的,而是直接通过AMS#broadcastIntent,其实Context最终也会通过AMS去发送广播的
上述分析得到,当耳机拔插状态发生变化时候,会在fwk中发送广播
ACTION_HEADSET_PLUG = android.media.AudioManager.ACTION_HEADSET_PLUG;
应用只需要注册该广播,在做具体的操作即可
上述笔记比较杂乱,下面简单对调用流程zuoxia
WiredAccessoryManager#notifyWiredAccessoryChanged
….
AudioService#setWiredDeviceConnectionState