Bug复现条件:手机插着USB到PC上时重启,USB功能选择菜单点不动
Android版本:7.1.2
内核版本:3.18
android-7.1.2_r1\packages\apps\Settings\src\com\android\settings\deviceinfo\UsbModeChooserActivity.java
有一个广播接收器,代码如下:
private BroadcastReceiver mDisconnectedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_STATE.equals(action)) {
boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
boolean hostConnected =
intent.getBooleanExtra(UsbManager.USB_HOST_CONNECTED, false);
if (!connected && !hostConnected) {
mDialog.dismiss();
}
}
}
};
当connected和hostConnected都为false时,该Diaglog会消失。这两个变量都是由广播获取的。通过Log查看,开机时,有时这两个变量都为flase
2.查看广播的发送。代码路径: frameworks\base\services\usb\java\com\android\server\usb\UsbDeviceManager.java
发送广播在函数updateUsbStateBroadcastIfNeeded()中。这是个粘性广播。
手机开机首次调用这个函数是在handleMessage()中,代码如下:
case MSG_SYSTEM_READY:
updateUsbNotification();
updateAdbNotification();
updateUsbStateBroadcastIfNeeded();
updateUsbFunctions();
break;
private void updateUsbStateBroadcastIfNeeded() {
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
intent.putExtra(UsbManager.USB_DATA_UNLOCKED, isUsbTransferAllowed() && mUsbDataUnlocked);
if (mCurrentFunctions != null) {
String[] functions = mCurrentFunctions.split(",");
for (int i = 0; i < functions.length; i++) {
final String function = functions[i];
if (UsbManager.USB_FUNCTION_NONE.equals(function)) {
continue;
}
intent.putExtra(function, true);
}
}
// send broadcast intent only if the USB state has changed
if (!isUsbStateChanged(intent)) {
if (DEBUG) {
Slog.d(TAG, "skip broadcasting " + intent + " extras: " + intent.getExtras());
}
return;
}
if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
mBroadcastedIntent = intent;
}
函数updateUsbStateBroadcastIfNeeded()发送广播时的携带的变量mConnected。
3.查找mConnected第一次赋值的地方。代码如下:
case MSG_UPDATE_STATE:
mConnected = (msg.arg1 == 1);
mConfigured = (msg.arg2 == 1);
updateUsbNotification();
updateAdbNotification();
if (UsbManager.containsFunction(mCurrentFunctions,
UsbManager.USB_FUNCTION_ACCESSORY)) {
updateCurrentAccessory();
} else if (!mConnected) {
// restore defaults when USB is disconnected
setEnabledFunctions(null, false, false);
}
if (mBootCompleted) {
updateUsbStateBroadcastIfNeeded();
updateUsbFunctions();
}
break;
4. 找出发送MSG_UPDATE_STATE消息的地方,
public void updateState(String state) {
int connected, configured;
if ("DISCONNECTED".equals(state)) {
connected = 0;
configured = 0;
} else if ("CONNECTED".equals(state)) {
connected = 1;
configured = 0;
} else if ("CONFIGURED".equals(state)) {
connected = 1;
configured = 1;
} else {
Slog.e(TAG, "unknown state " + state);
return;
}
removeMessages(MSG_UPDATE_STATE);
Message msg = Message.obtain(this, MSG_UPDATE_STATE);
msg.arg1 = connected;
msg.arg2 = configured;
// debounce disconnects to avoid problems bringing up USB tethering
sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
}
5.找到调用updateState函数的地方,有两处,第一处是在onUevent()函数的地方,代码如下:
private final UEventObserver mUEventObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
String state = event.get("USB_STATE");
String accessory = event.get("ACCESSORY");
if (state != null) {
mHandler.updateState(state);
} else if ("START".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory start");
startAccessoryMode();
}
}
};
该mUEventObserver主要是为了监听kernel层发送的uevent事件。
另一处是在UsbHandler的构造函数中,
public UsbHandler(Looper looper) {
super(looper);
try {
// Restore default functions.
mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
UsbManager.USB_FUNCTION_NONE);
mCurrentFunctionsApplied = mCurrentFunctions.equals(
SystemProperties.get(USB_STATE_PROPERTY));
mAdbEnabled = UsbManager.containsFunction(getDefaultFunctions(),
UsbManager.USB_FUNCTION_ADB);
/**
* Remove MTP from persistent config, to bring usb to a good state
* after fixes to b/31814300. This block can be removed after the update
*/
String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY);
if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)) {
SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY,
UsbManager.removeFunction(persisted, UsbManager.USB_FUNCTION_MTP));
}
setEnabledFunctions(null, false, false);
String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
updateState(state);
// register observer to listen for settings changes
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
false, new AdbSettingsObserver());
// Watch for USB configuration changes
mUEventObserver.startObserving(USB_STATE_MATCH);
mUEventObserver.startObserving(ACCESSORY_START_MATCH);
} catch (Exception e) {
Slog.e(TAG, "Error initializing UsbHandler", e);
}
}
6.造成该问题的主要原因是,kernel还未发送uevent事件,以更新状态。该广播已经发送出去了。所以,connected和hostConnected都为false。
7.修改,将updateUsbStateBroadcastIfNeeded()移动到case MSG_BOOT_COMPLETED中,延后广播的发送。
case MSG_SYSTEM_READY:
updateUsbNotification();
updateAdbNotification();
//updateUsbStateBroadcastIfNeeded(); 删掉
updateUsbFunctions();
break;
case MSG_BOOT_COMPLETED:
updateUsbStateBroadcastIfNeeded();//加上
mBootCompleted = true;
if (mCurrentAccessory != null) {
getCurrentSettings().accessoryAttached(mCurrentAccessory);
}
if (mDebuggingManager != null) {
mDebuggingManager.setAdbEnabled(mAdbEnabled);
}
break;
Android O上已经修改过来了。Android O的代码如下,
case MSG_SYSTEM_READY:
updateUsbNotification();
updateAdbNotification();
updateUsbFunctions();
break;
case MSG_BOOT_COMPLETED:
mBootCompleted = true;
if (mPendingBootBroadcast) {
updateUsbStateBroadcastIfNeeded(false);
mPendingBootBroadcast = false;
}
setEnabledFunctions(null, false, false);
if (mCurrentAccessory != null) {
getCurrentSettings().accessoryAttached(mCurrentAccessory);
}
if (mDebuggingManager != null) {
mDebuggingManager.setAdbEnabled(mAdbEnabled);
}
break;
总的来说,出现该问题的主要原因在于kernel与framework之间启动时序有问题,也有可能是kernel识别USB的速度慢造成的。