rk3588 android12 -- 插上耳机上电开机耳机不带mic图标
该问题硬件设计为mic和耳机为es8388 codec,开机时驱动识别到mic的adc通道,并且在work中有上报EXTCON_JACK_HEADPHONE和EXTCON_JACK_MICROPHONE这两个键值,并且热插拔一下耳机mic图标可以显示,所以推断该问题是framework中的逻辑问题。
该问题硬件设计为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图标的变化
修改前:
修改后:
至此,经过验证,该修改有效,问题解决,希望本篇文章所用到的方法能对读者有所帮助。
更多推荐
所有评论(0)