Android 10 USB用途切换失败分析
问题现象
最近处理一个USB的问题,偶现,现象如下:
快速切换USB的用途,会导致切换失败,最后变为不进行数据传输
状态。
源码梳理
首先按照惯例,得先把切换USB的流程梳理清楚,当前界面的UI在packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
里面有四个controller,分别控制该页面的四个部分
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class UsbDetailsFragment extends DashboardFragment {
//省略部分代码
private static List<UsbDetailsController> createControllerList(Context context,
UsbBackend usbBackend, UsbDetailsFragment fragment) {
List<UsbDetailsController> ret = new ArrayList<>();
ret.add(new UsbDetailsHeaderController(context, fragment, usbBackend));
ret.add(new UsbDetailsDataRoleController(context, fragment, usbBackend));
//添加USB用途切换的控制器
ret.add(new UsbDetailsFunctionsController(context, fragment, usbBackend));
ret.add(new UsbDetailsPowerRoleController(context, fragment, usbBackend));
return ret;
}
//省略部分代码
}
控制USB用途的controller为UsbDetailsFunctionsController,该控制器中实现了用途RadioButton的点击监听
/**
* This class controls the radio buttons for choosing between different USB functions.
*/
public class UsbDetailsFunctionsController extends UsbDetailsController
implements RadioButtonPreference.OnClickListener {
protected final UsbBackend mUsbBackend;
//省略部分代码
@Override
public void onRadioButtonClicked(RadioButtonPreference preference) {
String key = preference.getKey();
//获取当前选择的USB用途
final long function = UsbBackend.usbFunctionsFromString(key);
//获取之前的USB用途
final long previousFunction = mUsbBackend.getCurrentFunctions();
Log.d(TAG, "onRadioButtonClicked: currentFunction="+function+", previousFunction:"+previousFunction);
if (function != previousFunction && !Utils.isMonkeyRunning()) {
mPreviousFunction = previousFunction;
if (function == UsbManager.FUNCTION_RNDIS) {
//选择USB网络共享
//Update the UI in advance to make it looks smooth
final RadioButtonPreference prevPref =
(RadioButtonPreference) mProfilesContainer.findPreference(
UsbBackend.usbFunctionsToString(mPreviousFunction));
if (prevPref != null) {
prevPref.setChecked(false);
preference.setChecked(true);
}
// We need to have entitlement check for usb tethering, so use API in
// ConnectivityManager.
mConnectivityManager.startTethering(TETHERING_USB, true /* showProvisioningUi */,
mOnStartTetheringCallback);
} else {
//选择其他用途
mUsbBackend.setCurrentFunctions(function);
}
}
}
//省略部分代码
}
测试中没有切换到USB网络共享
,所以执行mUsbBackend.setCurrentFunctions(function);
,mUsbBackend
是UsbBackend
对象,它提供了对底层系统USB功能的访问
/**
* Provides access to underlying system USB functionality.
*/
public class UsbBackend {
private UsbManager mUsbManager;
//省略部分代码
public void setCurrentFunctions(long functions) {
mUsbManager.setCurrentFunctions(functions);
}
//省略部分代码
}
里面执行了UsbManager
的setCurrentFunctions()
方法,该类允许上层访问USB状态并于USB设备通信
@SystemService(Context.USB_SERVICE)
public class UsbManager {
private final IUsbManager mService;
//省略部分代码
public void setCurrentFunctions(long functions) {
try {
mService.setCurrentFunctions(functions);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//省略部分代码
}
UsbManager
又调用远程UsbService
的setCurrentFunctions()
(使用aidl),UsbService
是一个SystemService
,随着Android系统启动的时候而启动
/**
* UsbService manages all USB related state, including both host and device support.
* Host related events and calls are delegated to UsbHostManager, and device related
* support is delegated to UsbDeviceManager.
*/
public class UsbService extends IUsbManager.Stub {
private UsbDeviceManager mDeviceManager;
//省略部分代码
@Override
public void setCurrentFunctions(long functions) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
Preconditions.checkArgument(UsbManager.areSettableFunctions(functions));
Preconditions.checkState(mDeviceManager != null);
mDeviceManager.setCurrentFunctions(functions);
}
//省略部分代码
}
UsbService
里面执行了UsbDeviceManager
的setCurrentFunctions()
,UsbDeviceManager
用来管理Usb设备的状态
/**
* UsbDeviceManager manages USB state in device mode.
*/
public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
private UsbHandler mHandler;
//省略部分代码
/**
* Adds function to the current USB configuration.
*
* @param functions The functions to set, or empty to set the charging function.
*/
public void setCurrentFunctions(long functions) {
if (DEBUG) {
Slog.d(TAG, "setCurrentFunctions(" + UsbManager.usbFunctionsToString(functions) + ")");
}
if (functions == UsbManager.FUNCTION_NONE) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_CHARGING);
} else if (functions == UsbManager.FUNCTION_MTP) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MTP);
} else if (functions == UsbManager.FUNCTION_PTP) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_PTP);
} else if (functions == UsbManager.FUNCTION_MIDI) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MIDI);
} else if (functions == UsbManager.FUNCTION_RNDIS) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_RNDIS);
} else if (functions == UsbManager.FUNCTION_ACCESSORY) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_ACCESSORY);
}
mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions);
}
//省略部分代码
}
此方法里面通过UsbHandler
发了一条消息,同时在handleMessage()
方法里处理此消息
public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
//省略部分代码
abstract static class UsbHandler extends Handler {
//省略部分代码
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//省略部分代码
case MSG_SET_CURRENT_FUNCTIONS:
long functions = (Long) msg.obj;
setEnabledFunctions(functions, false);
break;
//省略部分代码
}
}
/**
* Evaluates USB function policies and applies the change accordingly.
*/
protected abstract void setEnabledFunctions(long functions, boolean forceRestart);
//省略部分代码
}
//省略部分代码
}
UsbHandler
是UsbDeviceManager
的一个抽象静态内部类,在handleMessage()
方法中调用setEnabledFunctions()
方法来处理消息,而setEnabledFunctions()
是个抽象方法,UsbHandler
有两个子类来实现该方法,分别是UsbDeviceManager.UsbHandlerHal
和UsbDeviceManager.UsbHandlerLegacy
,在UsbDeviceManager
的构造方法中,会根据实际场景来创建相对应的对象
public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
//省略部分代码
public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
UsbSettingsManager settingsManager) {
//省略部分代码
boolean halNotPresent = false;
try {
//获取hal层服务,如果获取成功,说明hal服务准备就绪,否则会抛出RemoteException
IUsbGadget.getService(true);
} catch (RemoteException e) {
Slog.e(TAG, "USB GADGET HAL present but exception thrown", e);
} catch (NoSuchElementException e) {
halNotPresent = true;
Slog.i(TAG, "USB GADGET HAL not present in the device", e);
}
//省略部分代码
if (halNotPresent) {
/**
* Initialze the legacy UsbHandler
*/
mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this,
alsaManager, settingsManager);
} else {
/**
* Initialize HAL based UsbHandler
*/
mHandler = new UsbHandlerHal(FgThread.get().getLooper(), mContext, this,
alsaManager, settingsManager);
}
}
//省略部分代码
}
可以看到,如果USB设备有hal层服务支持,则交由UsbHandlerHal
处理,否则交由UsbHandlerLegacy
处理,此处通过打印日志得知我的设备是没有hal支持的,所以逻辑在UsbHandlerLegacy
中,即上面的MSG_SET_CURRENT_FUNCTIONS
消息在UsbHandlerLegacy
中的setEnabledFunctions()
方法中处理
public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
//省略部分代码
private static final class UsbHandlerLegacy extends UsbHandler {
//省略部分代码
@Override
protected void setEnabledFunctions(long usbFunctions, boolean forceRestart) {
boolean usbDataUnlocked = isUsbDataTransferActive(usbFunctions);
if (DEBUG) {
Slog.d(TAG, "setEnabledFunctions functions=" + usbFunctions + ", "
+ "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
}
if (usbDataUnlocked != mUsbDataUnlocked) {
mUsbDataUnlocked = usbDataUnlocked;
//更新通知栏
updateUsbNotification(false);
forceRestart = true;
}
/**
* Try to set the enabled functions.
*/
final long oldFunctions = mCurrentFunctions;
final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
Log.d("jason", "setEnabledFunctions: first attempt");
//设置新模式
if (trySetEnabledFunctions(usbFunctions, forceRestart)) {
Log.d("jason", "setEnabledFunctions: first attempt success");
return;
}
/**
* Didn't work. Try to revert changes.
* We always reapply the policy in case certain constraints changed such as
* user restrictions independently of any other new functions we were
* trying to activate.
*/
//如果设置失败,则恢复为原来的设置
if (oldFunctionsApplied && oldFunctions != usbFunctions) {
Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
Log.d("jason", "setEnabledFunctions: first attempt fail,second attempt");
if (trySetEnabledFunctions(oldFunctions, false)) {
Log.d("jason", "setEnabledFunctions: second attempt success");
return;
}
}
/**
* Still didn't work. Try to restore the default functions.
*/
//如果仍然失败,则恢复默认的设置,即不进行数据传输
Slog.e(TAG, "Failsafe 2: Restoring default USB functions.");
Log.d("jason", "setEnabledFunctions: second attempt fail,third attempt");
if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
Log.d("jason", "setEnabledFunctions: third attempt success");
return;
}
/**
* Now we're desperate. Ignore the default functions.
* Try to get ADB working if enabled.
*/
//如果还是失败,则表示adb可能挂掉了,此时尝试重启adb
Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled).");
Log.d("jason", "setEnabledFunctions: third attempt fail,try to get adb working");
if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
Log.d("jason", "setEnabledFunctions: adb is successfully working");
return;
}
/**
* Ouch.
*/
//还是失败,说明usb不可用
Log.d("jason", "setEnabledFunctions: usb is unable");
Slog.e(TAG, "Unable to set any USB functions!");
}
//省略部分代码
}
//省略部分代码
}
为防止设置失败,此方法里面尝试三次尝试,第一次设置用户选择的模式,如果设置失败,则恢复到之前的模式,如果还失败,则恢复默认设置,如果仍然失败,则尝试重启adb,如果还是失败,说明usb已经不可用了,不用处理。看一下trySetEnabledFunctions()
的具体处理逻辑
public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
//省略部分代码
private static final class UsbHandlerLegacy extends UsbHandler {
private static final String USB_CONFIG_PROPERTY = "sys.usb.config";
//省略部分代码
private boolean trySetEnabledFunctions(long usbFunctions, boolean forceRestart) {
String functions = null;
if (usbFunctions != UsbManager.FUNCTION_NONE) {
functions = UsbManager.usbFunctionsToString(usbFunctions);
}
mCurrentFunctions = usbFunctions;
if (functions == null || applyAdbFunction(functions)
.equals(UsbManager.USB_FUNCTION_NONE)) {
functions = getSystemProperty(getPersistProp(true),
UsbManager.USB_FUNCTION_NONE);
if (functions.equals(UsbManager.USB_FUNCTION_NONE))
functions = UsbManager.usbFunctionsToString(getChargingFunctions());
}
functions = applyAdbFunction(functions);
String oemFunctions = applyOemOverrideFunction(functions);
if (!isNormalBoot() && !mCurrentFunctionsStr.equals(functions)) {
setSystemProperty(getPersistProp(true), functions);
}
if ((!functions.equals(oemFunctions)
&& !mCurrentOemFunctions.equals(oemFunctions))
|| !mCurrentFunctionsStr.equals(functions)
|| !mCurrentFunctionsApplied
|| forceRestart) {
Slog.i(TAG, "Setting USB config to " + functions);
mCurrentFunctionsStr = functions;
mCurrentOemFunctions = oemFunctions;
mCurrentFunctionsApplied = false;
/**
* Kick the USB stack to close existing connections.
*/
//先关闭现有的usb连接
setUsbConfig(UsbManager.USB_FUNCTION_NONE);
//等待连接关闭成功
if (!waitForState(UsbManager.USB_FUNCTION_NONE)) {
Slog.e(TAG, "Failed to kick USB config");
return false;
}
/**
* Set the new USB configuration.
*/
//设置新模式
setUsbConfig(oemFunctions);
if (mBootCompleted
&& (containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
|| containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
/**
* Start up dependent services.
*/
updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
//等待新模式设置完毕
if (!waitForState(oemFunctions)) {
Slog.e(TAG, "Failed to switch USB config to " + functions);
return false;
}
//标识位设为true,表示完成设置
mCurrentFunctionsApplied = true;
}
return true;
}
private boolean waitForState(String state) {
// wait for the transition to complete.
// give up after 1 second.
String value = null;
//轮询二十次去获取当前usb的配置,每次轮询120毫秒
for (int i = 0; i < 20; i++) {
// State transition is done when sys.usb.state is set to the new configuration
value = getSystemProperty(USB_STATE_PROPERTY, "");
if (state.equals(value)) return true;
//代码1:休眠120毫秒,原值为50
SystemClock.sleep(120);
}
Slog.e(TAG, "waitForState(" + state + ") FAILED: got " + value);
return false;
}
private void setUsbConfig(String config) {
if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
/**
* set the new configuration
* we always set it due to b/23631400, where adbd was getting killed
* and not restarted due to property timeouts on some devices
*/
//USB_CONFIG_PROPERTY的值为sys.usb.config
setSystemProperty(USB_CONFIG_PROPERTY, config);
}
protected void setSystemProperty(String prop, String val) {
SystemProperties.set(prop, val);
}
//省略部分代码
}
//省略部分代码
}
此方法核心思路就是,先关闭现在usb连接,如果当前usb不是不进行数据传输模式,比如当前是文件传输模式,那必定是存在一个usb连接,那么要先关闭这个连接,Android中,开启/关闭连接是一个耗时任务,所以通过轮询的方式去查找当前usb的配置,如果跟目标配置一致,说明任务完成了。然后再设置新模式,然后再次通过轮询的方式等待配置完成。而配置方式就是修改一个系统属性sys.usb.config
的值,系统底层会监听此属性的变化,当此值被修改时,会往上层发送一个uevent消息,上层只要注册了这个监听,就可以收到此消息。底层监听在init.msm.usb.configfs.rc
文件中
//省略部分代码
//此配置表示当sys.usb.config的值为accessory,audio_source并且sys.usb.configfs的值为1时执行下面的逻辑
on property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=1
//写入供应商id
write /config/usb_gadget/g1/idVendor 0x18d1
//写入产品id
write /config/usb_gadget/g1/idProduct 0x2d04
on property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/idVendor 0x18d1
write /config/usb_gadget/g1/idProduct 0x2d05
on property:sys.usb.config=midi && property:sys.usb.configfs=1
write /config/usb_gadget/g1/idVendor 0x18d1
write /config/usb_gadget/g1/idProduct 0x4ee8
on property:sys.usb.config=midi,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/idVendor 0x18d1
write /config/usb_gadget/g1/idProduct 0x4ee9
//省略部分代码
UsbDeviceManager
在初始化时就注册了上层监听
public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
private final UEventObserver mUEventObserver;
//省略部分代码
public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
UsbSettingsManager settingsManager) {
//省略部分代码
//注册USB状态变化监听
// Watch for USB configuration changes
mUEventObserver = new UsbUEventObserver();
mUEventObserver.startObserving(USB_STATE_MATCH);
mUEventObserver.startObserving(USB_STATE_MATCH_SEC);
mUEventObserver.startObserving(ACCESSORY_START_MATCH);
}
/*
* Listens for uevent messages from the kernel to monitor the USB state
*/
private final class UsbUEventObserver extends UEventObserver {
//usb状态变化会回调此方法
@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");
Log.d("jason", "onUEvent: state="+state+", accessory="+accessory);
if (state != null) {
//收到usb状态变化的消息,更新UI
mHandler.updateState(state);
} else if ("START".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory start");
startAccessoryMode();
}
}
}
abstract static class UsbHandler extends Handler {
//省略部分代码
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);
if (connected == 1) removeMessages(MSG_FUNCTION_SWITCH_TIMEOUT);
Message msg = Message.obtain(this, MSG_UPDATE_STATE);
msg.arg1 = connected;
msg.arg2 = configured;
// debounce disconnects to avoid problems bringing up USB tethering
//代码2:如果connected为断开状态,则延迟3秒发送消息,原值为1秒
sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY*3 : 0);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_STATE:
mConnected = (msg.arg1 == 1);
mConfigured = (msg.arg2 == 1);
//更新通知栏
updateUsbNotification(false);
updateAdbNotification(false);
if (mBootCompleted) {
//发送一条usb状态变化的广播
updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
if ((mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) != 0) {
updateCurrentAccessory();
}
if (mBootCompleted) {
if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_TIMEOUT)
&& !hasMessages(MSG_FUNCTION_SWITCH_TIMEOUT)) {
// restore defaults when USB is disconnected
if (!mScreenLocked
&& mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
setScreenUnlockedFunctions();
} else {
setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
}
}
updateUsbFunctions();
} else {
mPendingBootBroadcast = true;
}
break;
//省略部分代码
}
}
//省略部分代码
}
//省略部分代码
}
监听消息无非就是为了更新UI,这里有一点需要说明一下,每次切换USB用途时,其状态都会经历DISCONNECTED->CONNECTED->CONFIGURED
以上就是USB切换的流程,整个流程图如下
问题分析
通过上述分析可以看到,在设置USB新模式时,会执行waitForState()
等待设置生效,这个等待通过轮询的方式,开启了一个20次的for循环,每次等待50毫秒,也就是最大等待时间为20*50=1000毫秒,而在收到UEvent消息之后,也会发送一个延迟消息,延迟时间为1000毫秒,可以看出,这个等待时间就是为了让设置生效之后再通知UI更新。这里有两处设计都使用了延迟的做法,那么我们尝试增大延迟时间,让整个操作有充分的时间去完成,我们将代码1和代码2处的值分别由原来的50ms增大到120ms,1000ms增大到3000ms,发现有效果,bug复现概率大大降低,说明我们的考虑思路是对的,但即便如此,仍然无法完全修复此问题,因为底层处理时间是不可控的,上层不可能无限增大这个延迟时间。
我们进一步反过来想,既然它会恢复默认设置,那必定是设置了一次UsbManager.FUNCTION_NONE
,因此将所有设置此值的代码处打上log,然后看看是在哪儿执行了默认操作,最终发现是在handleMessage()
中执行了
public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
//省略部分代码
abstract static class UsbHandler extends Handler {
//省略部分代码
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_STATE:
mConnected = (msg.arg1 == 1);
mConfigured = (msg.arg2 == 1);
updateUsbNotification(false);
updateAdbNotification(false);
if (mBootCompleted) {
updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
if ((mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) != 0) {
updateCurrentAccessory();
}
if (mBootCompleted) {
if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_TIMEOUT)
&& !hasMessages(MSG_FUNCTION_SWITCH_TIMEOUT)) {
// restore defaults when USB is disconnected
if (!mScreenLocked
&& mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
setScreenUnlockedFunctions();
} else {
Log.d("jason", "handleMessage: update usb state, but the usb connected,mCurrentFunction:"+function2Name(mCurrentFunctions));
//代码3,执行了这里
setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
}
}
updateUsbFunctions();
} else {
mPendingBootBroadcast = true;
}
break;
}
//省略部分代码
}
//省略部分代码
}
//省略部分代码
}
而此处的逻辑是接收到消息后,如果此时usb断开链接,将恢复到默认设置,此时的mConnected为false,那为什么usb会断开连接呢,顺藤摸瓜,这个mConntected是消息中的arg1的值,而这个消息是在接收到uevent消息时发过来的,那么在uevent消息接收处打印该消息的内容
public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
//省略部分代码
private final class UsbUEventObserver extends 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");
//打印state
Log.d("jason", "onUEvent: state="+state);
if (state != null) {
//更新状态
mHandler.updateState(state);
} else if ("START".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory start");
startAccessoryMode();
}
}
}
//省略部分代码
abstract static class UsbHandler extends Handler {
//省略部分代码
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);
if (connected == 1) removeMessages(MSG_FUNCTION_SWITCH_TIMEOUT);
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*3 : 0);
}
//省略部分代码
}
//省略部分代码
}
打印出来的日志为
可以发现,当切换一次usb用途时,usb的状态分别会有DISCONNECTED->CONNECTED->DISCONNECTED->CONNECTED->CONFIGURED,为什么执行两次DISCONNECTED呢,因为我们前面分析了,切换usb用途时会执行两次setUsbConfig()
,第一次是断开现有的usb连接,第二次才是设置新值,所以执行两次DISCONNECTED和CONNECTED,而最终会变成CONFIGURED,表示配置成功了。而我们发现,当bug复现时的日志打印为
可以看到当上一次的操作结果还停留在DISCONNECTED时就切换usb用途,此时会执行代码3处,最终我们看到的现象就是USB恢复默认设置了。
那么问题的根本原因找到了,我们只需要在usb断开连接时不恢复默认操作即可,这里可以有两种做法
- 不作任何操作,即代码3处注释掉
setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
- 保存最后一次的设置,即代码3处改为
setEnabledFunctions(mCurrentFunctions, false);
经自测,两种方式都生效。此问题分析处理完毕。
总结
此问题能复现的根本原因还是在于底层切换usb太慢了,当上层暴力测试时,会出现两次操作的状态混乱,其实Google考虑到了这种情况,但它给的方案是恢复默认设置,我们只需要根据自己的需求更改方案即可。
转载自:https://juejin.cn/post/7202149834362699833