Android耳机拔插事件流程

本文详细解析了Android系统中耳机插拔事件的处理流程,从底层驱动到上层应用,包括InputDispatcher、InputManagerService、AudioService以及广播接收机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android所有的input设备都会在/dev/input目录下生成对应的设备节点,一旦有任何输入事件产生,便会将事件写到这些节点下,同时对于外部输入设备(鼠标键盘等)的插拔还会引起这些节点的创建和删除
使用adb 命令可以查看当前手机插入的耳机状态,命令为:adb shell cat /sys/class/switch/h2w/state


驱动层input事件传递

耳机拔插属于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方法

InputManagerService处理switch事件

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

AudioService传递switch事件

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;

应用只需要注册该广播,在做具体的操作即可

switch流程总结

上述笔记比较杂乱,下面简单对调用流程zuoxia

  • InputDispatcher.cpp #notifySwitch
  • com_android_server_input_InputManagerService.cpp # notifySwitch
  • InputManagerService#notifySwitch
  • WiredAccessoryManager#notifyWiredAccessoryChanged
    ….

  • AudioService#setWiredDeviceConnectionState

  • AudioService#sendDeviceConnectionIntent
  • ActivityManager#broadcastStickyIntent
<think>我们正在讨论Android音频系统中的两个不同方面:声卡注册流程耳机入检测。这两个功能在Android音频系统中扮演不同的角色,并且它们的代码实现位置和机制也有所不同。 ### 1. 声卡注册流程Android系统中,声卡(Sound Card)的注册通常发生在Linux内核层。当系统启动时,内核会检测并初始化硬件设备,包括声卡。声卡驱动会向ALSA(Advanced Linux Sound Architecture)框架注册。 #### 关键步骤: - **驱动初始化**:声卡驱动在模块初始化时(通过`module_init`宏)会调用`snd_card_new`等函数创建声卡对象。 - **注册声卡**:驱动通过`snd_card_register`函数注册声卡。此时,声卡设备会在`/dev/snd/`目录下创建相应的设备文件(如controlC0, pcmC0D0p等)。 - **Android HAL层**:在Android用户空间,Audio HAL(硬件抽象层)会访问这些设备文件。通常,HAL层的实现(如`tinyalsa`)会通过alsa-lib库与内核交互。 例如,在Linux内核中,一个简单的声卡注册代码可能如下: ```c static struct snd_card *card; static int __init mydriver_init(void) { int ret; ret = snd_card_new(&pci_dev, index, id, THIS_MODULE, 0, &card); // ... 设置声卡信息,创建PCM设备等 ret = snd_card_register(card); return 0; } module_init(mydriver_init); ``` ### 2. 耳机入检测 耳机入检测通常涉及硬件中断和输入事件上报。在Android中,耳机入检测的代码实现主要位于内核驱动层和Android框架层。 #### 关键步骤: - **硬件中断**:当耳机入或出时,声卡上的耳机孔会触发一个中断。声卡驱动中需要处理这个中断。 - **驱动上报事件**:驱动通过输入子系统(Input Subsystem)上报事件。例如,使用`input_report_switch`上报`SW_HEADPHONE_INSERT`事件。 - **Android框架层监听**:在Android框架中,`AudioService`会监听来自内核的输入事件(通过`InputReader`和`InputDispatcher`)。当检测到耳机事件时,`AudioService`会调整音频路由。 例如,在内核驱动中处理耳机入中断并上报事件: ```c static irqreturn_t headphone_detect_irq(int irq, void *dev_id) { int state = check_headphone_state(); // 读取硬件状态 input_report_switch(input_dev, SW_HEADPHONE_INSERT, state); input_sync(input_dev); return IRQ_HANDLED; } ``` 在Android框架中,事件最终会传递到`com.android.server.audio.AudioService`,并调用其`setWiredDeviceConnectionState`方法。 ### 区别总结 1. **功能不同**: - 声卡注册是音频设备初始化的过程,将声卡设备暴露给用户空间。 - 耳机入检测是动态事件处理,用于响应耳机状态变化。 2. **代码位置**: - 声卡注册:主要在Linux内核声卡驱动中实现。 - 耳机入检测:内核驱动(中断处理和事件上报)和Android框架(事件处理)共同完成。 3. **机制**: - 声卡注册:静态的初始化过程,通常只在启动时执行一次。 - 耳机入检测:基于中断的异步事件处理。 ### 相关问题 1. 在Android系统中,如何自定义一个音频设备的路由策略? 2. 耳机入检测事件Android框架中是如何传递的? 3. 如何调试Android音频驱动中的声卡注册问题? 4. 耳机出时,Android系统如何自动切换音频输出到扬声器?</think>### Android声卡注册流程耳机入检测的代码实现区别 #### 1. **声卡注册流程(内核层)** 声卡注册发生在Linux内核层,主要涉及ALSA驱动框架: - **驱动初始化** 声卡驱动在`module_init`阶段调用`snd_card_new()`创建声卡对象,通过`snd_pcm_new()`注册PCM设备。核心代码位置: ```c // kernel/sound/core/pcm_native.c int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm); ``` - **设备节点创建** 注册后在`/dev/snd/`生成设备节点(如`pcmC0D0p`),供用户空间访问。 - **HAL层对接** Android的Audio HAL(如`tinyalsa`)通过`open("/dev/snd/...")`操作这些节点实现音频流控制[^1]。 #### 2. **耳机入检测(驱动层+框架层)** 耳机检测涉及硬件中断和用户空间事件传递: - **硬件中断处理** 驱动中注册GPIO中断处理函数,检测状态变化: ```c // kernel/drivers/input/misc/jack_detect.c request_threaded_irq(gpio_to_irq(gpio), NULL, headset_detect_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "headset_detect", NULL); ``` - **事件上报机制** 检测到状态变化后,通过输入子系统上报事件: ```c input_report_switch(input_dev, SW_HEADPHONE_INSERT, inserted); input_sync(input_dev); ``` - **Android框架响应** `InputReader`捕获事件后,`AudioService`调用`AudioSystem.setDeviceConnectionState()`切换音频路由: ```java // frameworks/base/services/core/java/com/android/server/audio/AudioService.java public void setWiredDeviceConnectionState(int device, int state, String name) { // 更新音频设备状态 } ``` #### 关键区别总结 | **维度** | **声卡注册** | **耳机入检测** | |------------------|----------------------------------|--------------------------------| | **代码层级** | Linux内核层 | 内核层+Android框架层 | | **触发时机** | 系统启动时一次性初始化 | 动态响应硬件中断 | | **核心组件** | ALSA驱动框架 | GPIO驱动+Input子系统 | | **用户空间交互** | 通过HAL层操作设备节点 | 通过Input事件传递到AudioService| | **主要目的** | 暴露音频设备能力 | 动态响应外部设备连接状态 | | **调试节点** | `/proc/asound/cards` | `dumpsys audio` 查看设备状态 | #### 典型问题场景 - **声卡注册失败**:内核驱动未正确初始化,导致`/dev/snd/`节点缺失 - **耳机检测异常**:GPIO配置错误或Input事件未正确上报到框架层 --- ### 相关问题 1. Android如何通过UEvent监听耳机事件的内核机制? 2. 在Audio HAL层,声卡参数(如采样率)是如何配置的? 3. 当多个音频设备(如USB声卡)存在时,系统如何选择默认输出设备? 4. 如何定制耳机按键(如音量控制)的Input事件映射逻辑? [^1]: 参见Linux内核文档:*Documentation/sound/alsa/ALSA-Configuration.txt* [^2]: Android源码参考:*frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp*
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值