该问题硬件设计为mic和耳机为es8388 codec,开机时驱动识别到mic的adc通道,并且在work中有上报EXTCON_JACK_HEADPHONE和EXTCON_JACK_MICROPHONE这两个键值,并且热插拔一下耳机mic图标可以显示,所以推断该问题是framework中的逻辑问题。

        我是根据耳机插入判断的逻辑一步步逆推所得,所以只是涉及耳机mic部分代码,并不分析audio框架,希望对后续遇到该问题的同时有所帮助!

首先我们看状态栏显示代码 framework/basepackages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java

 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            switch (action) {
                case Intent.ACTION_SIM_STATE_CHANGED:
                    // Avoid rebroadcast because SysUI is direct boot aware.
                    if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
                        break;
                    }
                    break;
                case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
                    updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
                            TelecomManager.TTY_MODE_OFF));
                    break;
                case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
                case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
                case Intent.ACTION_MANAGED_PROFILE_REMOVED:
                    updateManagedProfile();
                    break;
                case AudioManager.ACTION_HEADSET_PLUG:
            Log.d(TAG, "cgl ==== ACTION_HEADSET_PLUG");
                    updateHeadsetPlug(intent);
                    break;
            }
        }
    };

ACTION_HEADSET_PLUG是耳机插入的广播,所以我们查看updateHeadsetPlug的函数原型

    private void updateHeadsetPlug(Intent intent) {
        boolean connected = intent.getIntExtra("state", 0) != 0;
        boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
    Log.d(TAG, "updateHeadsetPlug connected = "+connected+" hasMic = "+hasMic);
        if (connected) {
            String contentDescription = mResources.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);
        }
    }

        从这个函数中可以的得知是否有mic图标显示是根据hasMic的true和false来进行判断的,所以我们再找到ACTION_HEADSET_PLUG这个广播发送的位置,查看"microphone"是怎么进行赋值的。

在framework/base/services/core/java/com/android/server/audio/AudioDeviceInventory.java中我们可以看到ACTION_HEADSET_PLUG广播发送的判断

    private void sendDeviceConnectionIntent(int device, int state, String address,
                                            String deviceName) {
        if (AudioService.DEBUG_DEVICES) {
            Slog.i(TAG, "sendDeviceConnectionIntent(dev:0x" + Integer.toHexString(device)
                    + " state:0x" + Integer.toHexString(state) + " address:" + address
                    + " name:" + deviceName + ");");
        }
        Intent intent = new Intent();
    +Log.d(TAG,"device = "+device+" state = "+state+" address = "+address+" deviceName = "+deviceName);
        switch(device) {
            case AudioSystem.DEVICE_OUT_WIRED_HEADSET:
                intent.setAction(Intent.ACTION_HEADSET_PLUG);
                intent.putExtra("microphone", 1);
                break;
            case AudioSystem.DEVICE_OUT_WIRED_HEADPHONE:
            case AudioSystem.DEVICE_OUT_LINE:
                intent.setAction(Intent.ACTION_HEADSET_PLUG);
                intent.putExtra("microphone", 0);
                break;
            case 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);
                break;
            case 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;
                }
                break;
            case AudioSystem.DEVICE_OUT_HDMI:
            case AudioSystem.DEVICE_OUT_HDMI_ARC:
            case AudioSystem.DEVICE_OUT_HDMI_EARC:
                configureHdmiPlugIntent(intent, state);
                break;
        }

可以看到发送广播时是根据device的值来进行判断"microphone"是为1还是为0的,AudioSystem.DEVICE_OUT_WIRED_HEADSET为带mic,AudioSystem.DEVICE_OUT_WIRED_HEADPHONE不带mic,查看添加的日志信息

04-22 06:50:33.794   434   548 D AudioHardwareTiny: do_out_standby,out = 0xf36c3670,device = 0x2
04-22 06:50:38.716   618   735 D AS.AudioDeviceInventory: device = 4 state = 1 address =  deviceName = 
04-22 06:50:38.722   618   735 D AS.AudioDeviceInventory: device = 8 state = 1 address =  deviceName = 
04-22 06:50:38.729   618   735 D AS.AudioDeviceInventory: device = -2147483632 state = 1 address =  deviceName = 

在frameworl/base/media/java/android/media/AudioSystem.java中可以看到

   

/** @hide */
    @UnsupportedAppUsage
    public static final int DEVICE_OUT_WIRED_HEADSET = 0x4;
    /** @hide */
    @UnsupportedAppUsage
    public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8;

所以是先执行AudioSystem.DEVICE_OUT_WIRED_HEADSET的判断再执行 AudioSystem.DEVICE_OUT_WIRED_HEADPHONE的判断,所以最终呈现出来的是DEVICE_OUT_WIRED_HEADPHONE无mic的状态?所以DEVICE_OUT_WIRED_HEADSET这个状态是有执行到的,并且mic图标也是可以显示的。

在framework/base/services/core/java/com/android/server/audio/AudioDeviceInventory.java中查看sendDeviceConnectionIntent的调用,可以看到是在onSetWiredDeviceConnectionState函数中调用的,下面我们开始追踪onSetWiredDeviceConnectionState的实现过程。

在framework/base/services/core/java/com/android/server/audio/AudioDeviceBroker.java中BrokerHandler调用 mDeviceInventory.onSetWiredDeviceConnectionState,具体代码如下

    private class BrokerHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RESTORE_DEVICES:
                    synchronized (mSetModeLock) {
                        synchronized (mDeviceStateLock) {
                            initCommunicationStrategyId();
                            updateActiveCommunicationDevice();
                            mDeviceInventory.onRestoreDevices();
                            mBtHelper.onAudioServerDiedRestoreA2dp();
                            onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
                        }
                    }
                    break;
                case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
                    synchronized (mDeviceStateLock) {
           +Log.d(TAG,"msg.obj = "+(AudioDeviceInventory.WiredDeviceConnectionState)msg.obj);
                        mDeviceInventory.onSetWiredDeviceConnectionState(
                                (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
                    }
                    break;

MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE这个msg的发送是

    /*package*/ void postSetWiredDeviceConnectionState(
            AudioDeviceInventory.WiredDeviceConnectionState connectionState, int delay) {
        sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay);
    }

在framework/base/services/core/java/com/android/server/audio/AudioDeviceInventory.java中实现的postSetWiredDeviceConnectionState函数

    /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
                                                  String address, String name, String caller) {
        synchronized (mDevicesLock) {
            int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE);
            mDeviceBroker.postSetWiredDeviceConnectionState(
                    new WiredDeviceConnectionState(type, state, address, name, caller),
                    delay);
            return delay;
        }
    }

在framework/base/services/core/java/com/android/server/audio/AudioDeviceBroker.java中实现的mDeviceInventory.setWiredDeviceConnectionState函数

/*package*/ void setWiredDeviceConnectionState(int type,
            @AudioService.ConnectionState int state, String address, String name,
            String caller) {
        //TODO move logging here just like in setBluetooth* methods
        synchronized (mDeviceStateLock) {
            mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
        }
    }

在framework/base/services/core/java/com/android/server/audio/AudioService.java实现 mDeviceBroker.setWiredDeviceConnectionState函数 

   public void setWiredDeviceConnectionState(int type,
            @ConnectionState int state, String address, String name,
            String caller) {
        enforceModifyAudioRoutingPermission();
        if (state != CONNECTION_STATE_CONNECTED
                && state != CONNECTION_STATE_DISCONNECTED) {
            throw new IllegalArgumentException("Invalid state " + state);
        }
        new MediaMetrics.Item(mMetricsId + "setWiredDeviceConnectionState")
                .set(MediaMetrics.Property.ADDRESS, address)
                .set(MediaMetrics.Property.CLIENT_NAME, caller)
                .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(type))
                .set(MediaMetrics.Property.NAME, name)
                .set(MediaMetrics.Property.STATE,
                        state == CONNECTION_STATE_CONNECTED ? "connected" : "disconnected")
                .record();

        if (isBox() && (type == AudioSystem.DEVICE_OUT_AUX_DIGITAL) && (state == CONNECTION_STATE_CONNECTED)) {
            updataFormatForEdid();
        }

        mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller);
    }

在framework/base/media/java/android/media/AudioManager.java中实现service.setWiredDeviceConnectionState函数 

  @UnsupportedAppUsage
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public void setWiredDeviceConnectionState(int type, int state, String address, String name) {
        final IAudioService service = getService();
        try {
            service.setWiredDeviceConnectionState(type, state, address, name,
                    mApplicationContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

在framework/base/services/core/java/com/android/server/WiredAccessoryManager.java中实现mAudioManager.setWiredDeviceConnectionState函数

    private void setDeviceStateLocked(int headset,
            int headsetState, int prevHeadsetState, String headsetName) {
        if ((headsetState & headset) != (prevHeadsetState & headset)) {
            int outDevice = 0;
            int inDevice = 0;
            int 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) {
                Slog.d(TAG, "hdmi_0 plug");
                //Fix HDMI_1 no sound issue when device boot up
                //outDevice = AudioManager.DEVICE_OUT_HDMI;
                outDevice = AudioManager.DEVICE_OUT_HDMI_1;
            } else if (headset == BIT_HDMI_AUDIO_1) {
                Slog.d(TAG, "hdmi_1 plug");
                outDevice = AudioManager.DEVICE_OUT_HDMI_1;
            } else if (headset == BIT_DP_AUDIO) {
                Slog.d(TAG, "dp_0 plug");
                outDevice = AudioManager.DEVICE_OUT_SPDIF;
            } else if (headset == BIT_DP_AUDIO_1) {
                Slog.d(TAG, "dp_1 plug");
                outDevice = AudioManager.DEVICE_OUT_SPDIF_1;
            } else {
                Slog.e(TAG, "setDeviceState() invalid headset type: " + headset);
                return;
            }

            if (LOG) {
                Slog.v(TAG, "headsetName: " + headsetName +
                        (state == 1 ? " connected" : " disconnected"));
            }
        +Log.d(TAG,"outDevice = "+outDevice+" inDevice = "+inDevice);
            if (outDevice != 0) {
                mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName);
            }
            if (inDevice != 0) {
                mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName);
            }
        }
    }

在函数setDeviceStateLocked中我们可以看到最初的device信息是从outDevice这里传过来的,那么outdevice的赋值又是根据headset的值的判断进行赋值的,现在我们在重点看看headset是怎么回事。

    private static final int BIT_HEADSET = (1 << 0);
    private static final int BIT_HEADSET_NO_MIC = (1 << 1);
    private static final int BIT_USB_HEADSET_ANLG = (1 << 2);
    private static final int BIT_USB_HEADSET_DGTL = (1 << 3);
    private static final int BIT_HDMI_AUDIO = (1 << 4);
    private static final int BIT_LINEOUT = (1 << 5);
    private static final int BIT_DP_AUDIO = (1 << 6);
    private static final int BIT_HDMI_AUDIO_1 = (1 << 7);
    private static final int BIT_DP_AUDIO_1 = (1 << 8);
    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET | BIT_HEADSET_NO_MIC |
            BIT_USB_HEADSET_ANLG | BIT_USB_HEADSET_DGTL | BIT_HDMI_AUDIO | BIT_LINEOUT |
            BIT_DP_AUDIO | BIT_HDMI_AUDIO_1 | BIT_DP_AUDIO_1);



    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) {
            +Log.d(TAG,"curHeadset = "+curHeadset+" allHeadsets = "+allHeadsets+" headsetState = "+headsetState+" prevHeadsetState = "+prevHeadsetState+" headsetName = "+headsetName);
                    setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName);
                    allHeadsets &= ~curHeadset;
                }
            }
        }
    }

setDeviceStateLocked的调用是在setDevicesState函数中实现的,可以看到setDevicesState中是根据curHeadset左移的一个for循环,它的逻辑是根据位判断各个音频设备是否存在,并且显示对应的图标,但是有一个问题就是位越高的优先级越高,而我们BIT_HEADSET的优先级是最低的,所以会导致我们的mic是最先获取的,而BIT_HEADSET_NO_MIC的优先级又高于BIT_HEADSET,所以最终呈现的图标就是不带mic。从日志中我们也可以看出来

04-22 05:49:36.502   633   633 D WiredAccessoryManager: curHeadset = 1 allHeadsets = 511 headsetState = 3 prevHeadsetState = 0 headsetName = 
04-22 05:49:36.502   633   633 D WiredAccessoryManager: outDevice = 4 inDevice = -2147483632
04-22 05:49:36.509   633   633 D WiredAccessoryManager: curHeadset = 2 allHeadsets = 510 headsetState = 3 prevHeadsetState = 0 headsetName = 
04-22 05:49:36.509   633   633 D WiredAccessoryManager: outDevice = 8 inDevice = 0
04-22 05:49:36.522   633   633 D WiredAccessoryManager: curHeadset = 4 allHeadsets = 508 headsetState = 3 prevHeadsetState = 0 headsetName = 
04-22 05:49:36.523   633   633 D WiredAccessoryManager: curHeadset = 8 allHeadsets = 504 headsetState = 3 prevHeadsetState = 0 headsetName = 
04-22 05:49:36.523   633   633 D WiredAccessoryManager: curHeadset = 16 allHeadsets = 496 headsetState = 3 prevHeadsetState = 0 headsetName = 
04-22 05:49:36.523   633   633 D WiredAccessoryManager: curHeadset = 32 allHeadsets = 480 headsetState = 3 prevHeadsetState = 0 headsetName = 
04-22 05:49:36.523   633   633 D WiredAccessoryManager: curHeadset = 64 allHeadsets = 448 headsetState = 3 prevHeadsetState = 0 headsetName = 
04-22 05:49:36.523   633   633 D WiredAccessoryManager: curHeadset = 128 allHeadsets = 384 headsetState = 3 prevHeadsetState = 0 headsetName = 
04-22 05:49:36.523   633   633 D WiredAccessoryManager: curHeadset = 256 allHeadsets = 256 headsetState = 3 prevHeadsetState = 0 headsetName = 

要修改的办法也很简单,就是将BIT_HEADSET和BIT_HEADSET_NO_MIC赋值交换,交换优先级

-    private static final int BIT_HEADSET = (1 << 0);
-    private static final int BIT_HEADSET_NO_MIC = (1 << 1);

+    private static final int BIT_HEADSET = (1 << 1);
+    private static final int BIT_HEADSET_NO_MIC = (1 << 0);

最后贴上修改前后耳机mic图标的变化

修改前:

 修改后:

 

至此,经过验证,该修改有效,问题解决,希望本篇文章所用到的方法能对读者有所帮助。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐