Android4.1.2 耳机插入拔出通知流程分析

因为需要加一套新的耳机设备accessory并且展示耳机图标在状态栏,所以需要去看一下android上层的处理流程。
其实整个耳机插入的流程是:当硬件检测到耳机的插入,kernel上报UEvent到上层,上层接收到event后,调用硬件去切audio path, 硬件切换成功后,kernel发送UEvent给framework, 然后做show图标的动作。
测试过程中,使用ADB命令发送UEvent模拟耳机的插入,kernel接收uevent。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   private static final int SW_HEADPHONE_INSERT = 0x02;
   private static final int SW_MICROPHONE_INSERT = 0x04;
   private static final int SW_LINEOUT_INSERT = 0x06;
0x02&&0x04代表标准3.5mm耳机
0x06&&0x04代表accessory

使用ADB模拟耳机插入事件,如下:
第一个参数代表动作类型(path switch),第二个参数代表“键”,第四个参数代表“值”(1:插入;0:拔出。)。
Accessory 插入
                            sendevent /dev/input/event5 5 4 1
                            sendevent /dev/input/event5 5 6 1
                            sendevent /dev/input/event5 0 0 0
Accessory 拔出
                            sendevent /dev/input/event5 5 4 0
                            sendevent /dev/input/event5 5 6 0
                            sendevent /dev/input/event5 0 0 0
标准3.5耳机插入
                            sendevent /dev/input/event5 5 4 1
                            sendevent /dev/input/event5 5 2 1
                            sendevent /dev/input/event5 0 0 0
标准3.5耳机拔出
                            sendevent /dev/input/event5 5 4 0
                            sendevent /dev/input/event5 5 2 0
                            sendevent /dev/input/event5 0 0 0

1、在系统启动过程中,在SystemService中会启动很多服务,包括一个WiredAccessoryObserver。

1
2
3
4
5
6
7
8
SystemService.java
try {
                Slog.i(TAG, "Wired Accessory Observer");
                // Listen for wired headset changes
                new WiredAccessoryObserver(context);
            } catch (Throwable e) {
                reportWtf("starting WiredAccessoryObserver", e);
            }

在WiredAccessoryObserver中,注册了开机广播,开机后,会开始所有相关的UEvent,并且开始监听。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
WiredAccessoryObserver.java
//所有要监听的UEvent
private static List<UEventInfo> makeObservedUEventList() {
        List<UEventInfo> retVal = new ArrayList<UEventInfo>();
        UEventInfo uei;

        // Monitor h2w
        uei = new UEventInfo("h2w", BIT_HEADSET, BIT_HEADSET_NO_MIC);
        if (uei.checkSwitchExists()) {
            retVal.add(uei);
        } else {
            Slog.w(TAG, "This kernel does not have wired headset support");
        }

        // Monitor USB
        uei = new UEventInfo("usb_audio", BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL);
        if (uei.checkSwitchExists()) {
            retVal.add(uei);
        } else {
            Slog.w(TAG, "This kernel does not have usb audio support");
        }

        // Monitor HDMI
        //
        // If the kernel has support for the "hdmi_audio" switch, use that.  It will be signalled
        // only when the HDMI driver has a video mode configured, and the downstream sink indicates
        // support for audio in its EDID.
        //
        // If the kernel does not have an "hdmi_audio" switch, just fall back on the older "hdmi"
        // switch instead.
        uei = new UEventInfo("hdmi_audio", BIT_HDMI_AUDIO, 0);
        if (uei.checkSwitchExists()) {
            retVal.add(uei);
        } else {
            uei = new UEventInfo("hdmi", BIT_HDMI_AUDIO, 0);
            if (uei.checkSwitchExists()) {
                retVal.add(uei);
            } else {
                Slog.w(TAG, "This kernel does not have HDMI audio support");
            }
        }

        return retVal;
    }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//开始监听
private final class BootCompletedReceiver extends BroadcastReceiver {
      @Override
      public void onReceive(Context context, Intent intent) {
        // At any given time accessories could be inserted
        // one on the board, one on the dock and one on HDMI:
        // observe three UEVENTs
        init();  // set initial status
        for (int i = 0; i < uEventInfo.size(); ++i) {
            UEventInfo uei = uEventInfo.get(i);
            startObserving("DEVPATH="+uei.getDevPath());
        }
      }
    }

在OnEvent中,实时监听UEvent事件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Override
    public void onUEvent(UEventObserver.UEvent event) {
        if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());

        try {
            String devPath = event.get("DEVPATH");
            String name = event.get("SWITCH_NAME");
            int state = Integer.parseInt(event.get("SWITCH_STATE"));
            updateState(devPath, name, state);
        } catch (NumberFormatException e) {
            Slog.e(TAG, "Could not parse switch state from event " + event);
        }
    }

其中这些状态是从
root@android:/sys/class/switch # ls -l
ls -l
lrwxrwxrwx root root 2013-03-01 14:15 hdmi -> ../../devices/virtual/switch/hdmi
lrwxrwxrwx root root 2013-03-01 14:16 hdmi_audio -> ../../devices/virtual/switch/hdmi_audio
lrwxrwxrwx root root 2013-03-01 14:16 usb_audio -> ../../devices/virtual/switch/usb_audio
这些文件中读取的。
设备是定义在/devices/virtual/switch/下的。

最终会调用到AudioManager

1
mAudioManager.setWiredDeviceConnectionState(device, state, headsetName);

AudioManager的setWiredDeviceConnectionState实际是调用AudioService的setWiredDeviceConnectionState方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public void setWiredDeviceConnectionState(int device, int state, String name) {
        synchronized (mConnectedDevices) {
            int delay = checkSendBecomingNoisyIntent(device, state);
            queueMsgUnderWakeLock(mAudioHandler,
                    MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
                    device,
                    state,
                    name,
                    delay);
        }
    }

最终会发送到上层一个广播:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private void sendDeviceConnectionIntent(int device, int state, String name)
    {
        Intent intent = new Intent();

        intent.putExtra("state", state);
        intent.putExtra("name", name);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        int connType = 0;
        if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
            connType = AudioRoutesInfo.MAIN_HEADSET;
            intent.setAction(Intent.ACTION_HEADSET_PLUG);
            intent.putExtra("microphone", 1);
        } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
            connType = AudioRoutesInfo.MAIN_HEADPHONES;
            intent.setAction(Intent.ACTION_HEADSET_PLUG);
            intent.putExtra("microphone", 0);
        } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
            connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
            intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
        } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
            connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
            intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
        } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
            connType = AudioRoutesInfo.MAIN_HDMI;
            intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
        } else if (device == AudioSystem.DEVICE_OUT_ANC_HEADSET) {
            connType = AudioRoutesInfo.MAIN_HEADSET;
            intent.setAction(Intent.ACTION_HEADSET_PLUG);
            intent.putExtra("microphone", 1);
        } else if (device == AudioSystem.DEVICE_OUT_ANC_HEADPHONE) {
            connType = AudioRoutesInfo.MAIN_HEADPHONES;
            intent.setAction(Intent.ACTION_HEADSET_PLUG);
            intent.putExtra("microphone", 0);
        }

        synchronized (mCurAudioRoutes) {
            if (connType != 0) {
                int newConn = mCurAudioRoutes.mMainType;
                if (state != 0) {
                    newConn |= connType;
                } else {
                    newConn &= ~connType;
                }
                if (newConn != mCurAudioRoutes.mMainType) {
                    mCurAudioRoutes.mMainType = newConn;
                    sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
                            SENDMSG_NOOP, 0, 0, null, 0);
                }
            }
        }
        ActivityManagerNative.broadcastStickyIntent(intent, null);
    }



       
       
       
       

要想在状态栏添加耳机插入的图标,在SystemUI里面添加相关逻辑。耳机拔出通知也是一样的流程。

声明:eoe文章著作权属于作者,受法律保护,转载时请务必以超链接形式附带如下信息

原文作者: cnhua5

原文地址: http://my.eoe.cn/cnhua5/archive/1252.html

你可能感兴趣的:(Android4.1.2 耳机插入拔出通知流程分析)