需求:在开启 USB Tethering 后,同时需要开启 diag 端口供 QXDM 调试使用。于是 trace code 大概分析了一下设置 USB Tethering 过程,比较毛糙,如有不正之处,还望帮忙指正。
平台:QCM2150
从 app 到 framework 层进行 trace:
1. TetherSettings.java
packages/apps/Settings/src/com/android/settings/TetherSettings.java
onPreferenceTreeClick()
startTethering(TETHERING_USB);
private ConnectivityManager mCm;
mCm.startTethering(choice, true, mStartTetheringCallback, mHandler);
2. ConnectivityManager.java
frameworks/base/core/java/android/net/ConnectivityManager.java
startTethering()
mService.startTethering(type, wrappedCallback, showProvisioningUi, pkgName);
manager 和service有一个对应关系,固定的规则。manager是为了sdk诞生的,方便app开发者调用。其实可以直接调用service,如mountservice是没有mountmanager的。
service是在系统起来是就被android系统启动的,而manager是后期有需要时实例化起来的。
Service的目录在:/frameworks/base/services/java/com/android/server/
manager的目录在:/frameworks/base/core/java/android
3. ConnectivityService.java
frameworks/base/services/core/java/com/android/server/ConnectivityService.java
startTethering()
mTethering.startTethering(type, receiver, showProvisioningUi);
4. Tethering.java
frameworks/base/services/core/java/com/android/server/connectivity/Tethering.java
startTethering()
enableTetheringInternal()
setUsbTethering(enable);
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
... ...
usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS : UsbManager.FUNCTION_NONE);
通过 service,来获取 manager,进行操作。
5. UsbManager.java
frameworks/base/core/java/android/hardware/usb/UsbManager.java
setCurrentFunctions()
mService.setCurrentFunctions(functions);
6. UsbService.java
frameworks/base/services/usb/java/com/android/server/usb/UsbService.java
setCurrentFunction()
mDeviceManager.setCurrentFunctions(functions);
7. UsbDeviceManager.java
frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
setCurrentFunction()
mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions);
handleMessage()
case MSG_SET_CURRENT_FUNCTIONS:
setEnabledFunctions(functions, false);
trySetEnabledFunctions(usbFunctions, forceRestart)
setUsbConfig(oemFunctions);
setSystemProperty(USB_CONFIG_PROPERTY, config);
其中 USB_CONFIG_PROPERTY = "sys.usb.config";
终于到最后了,可以看到设置 USB 后,最终调用的是 setEnabledFunctions() 这个方法,这个方法本身是一想抽象方法,实现类为 UsbHandlerLegacy。
protected void setEnabledFunctions(long usbFunctions, boolean forceRestart) {
// 判断数据是否解锁,只有MTP和PTP的数据是解锁的
boolean usbDataUnlocked = isUsbDataTransferActive(usbFunctions);
// 处理数据解锁状态改变的情况
if (usbDataUnlocked != mUsbDataUnlocked) {
// 更新数据解锁状态
mUsbDataUnlocked = usbDataUnlocked;
// 更新usb通知
updateUsbNotification(false);
// forceRestart设置为true,表示需要强制重启usb功能
forceRestart = true;
}
// 在设置新usb功能前,先保存旧的状态,以免设置新功能失败,还可以恢复
final long oldFunctions = mCurrentFunctions;
final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
// 尝试设置usb新功能
if (trySetEnabledFunctions(usbFunctions, forceRestart)) {
return;
}
// 如果到这里,就表示新功能设置失败,那么就回退之前的状态
if (oldFunctionsApplied && oldFunctions != usbFunctions) {
Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
if (trySetEnabledFunctions(oldFunctions, false)) {
return;
}
}
// 如果回退还是失败了,那么就设置usb功能为NONE
if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
return;
}
// 如果设置NONE还是失败了,那么再试一次设置NONE
if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
return;
}
// 如果走到这里,就表示异常了。
Slog.e(TAG, "Unable to set any USB functions!");
}
首先判断要设置的新的USB功能的数据是否是解锁状态,只有MTP和PTP模式的数据是解锁状态,这是为何你能在设置MTP或PTP模式后,在PC端能看到手机中的文件,然而这个文件只是手机内存中文件的映射,并不是文件本身。
然后处理数据解锁状态改变的情况,如果是,那么会更新状态,更新usb广播,然后最重要的是设置forceRestart
变量的值为true
,这个变量代表要强制重启usb功能。
最后,设置新usb功能。如果失败了,就回退。现在来看下trySetEnabledFunctions()
方法如何设置新功能。
private boolean trySetEnabledFunctions(long usbFunctions, boolean forceRestart) {
// 1. 把新usb功能转化为字符串
String functions = null;
// 如果新功能不是NONE,就转化
if (usbFunctions != UsbManager.FUNCTION_NONE) {
functions = UsbManager.usbFunctionsToString(usbFunctions);
}
// 保存待设置的新usb功能
mCurrentFunctions = usbFunctions;
// 如果转化后的功能为空,那么就从其它地方获取
if (functions == null || applyAdbFunction(functions)
.equals(UsbManager.USB_FUNCTION_NONE)) {
// 获取persist.sys.usb.config属性值
functions = getSystemProperty(getPersistProp(true),
UsbManager.USB_FUNCTION_NONE);
// 如果persist.sys.usb.config属性值还是为NONE
if (functions.equals(UsbManager.USB_FUNCTION_NONE))
// 如果adb开启,返回adb,否则返回mtp
functions = UsbManager.usbFunctionsToString(getChargingFunctions());
}
// adb开启,就追加adb值,否则移除adb值
functions = applyAdbFunction(functions);
// 2. 获取oem覆盖的usb功能
String oemFunctions = applyOemOverrideFunction(functions);
// 处理非正常启动模式情况,忽略
if (!isNormalBoot() && !mCurrentFunctionsStr.equals(functions)) {
setSystemProperty(getPersistProp(true), functions);
}
// 3. 设置新功能
if ((!functions.equals(oemFunctions)
&& !mCurrentOemFunctions.equals(oemFunctions))
|| !mCurrentFunctionsStr.equals(functions)
|| !mCurrentFunctionsApplied
|| forceRestart) {
Slog.i(TAG, "Setting USB config to " + functions);
// 保存要设置新功能对应的字符串值
mCurrentFunctionsStr = functions;
// 保存oem覆盖功能的字符串值
mCurrentOemFunctions = oemFunctions;
mCurrentFunctionsApplied = false;
// 先断开已经存在的usb连接
setUsbConfig(UsbManager.USB_FUNCTION_NONE);
// 判断是否成功
if (!waitForState(UsbManager.USB_FUNCTION_NONE)) {
Slog.e(TAG, "Failed to kick USB config");
return false;
}
// 设置新功能,注意,这里使用的是oem覆盖的功能
setUsbConfig(oemFunctions);
// 如果新功能包含mtp或ptp,那么就要更新usb状态改变广播
// 广播接收者会映射主内存的文件到PC端
if (mBootCompleted
&& (containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
|| containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
// 等待新功能设置完毕
if (!waitForState(oemFunctions)) {
Slog.e(TAG, "Failed to switch USB config to " + functions);
return false;
}
mCurrentFunctionsApplied = true;
}
return true;
}
这里的逻辑分三步:
第一步,把待设置的USB功能转化为字符串,有两种情况
FUNCTION_NONE
,那么转化后的值从persist.sys.usb.config
获取,如果获取值为NONE,就判断adb是否开启,如果开启了,转化后的值为adb,如果没有开启,转化后的值为mtp。前面分析说过,persist.sys.usb.config
主要包含用于判断adb是否开启在值,然后还包含一些厂商定制且用于测试目的的功能。例如,高通项目,这个值可能为adb,diag
,这个diag就是高通自己的功能。FUNCTION_NONE
,把直接转化。例如新功能为FUNCTION_MTP
,那么转化后的字符串为mtp
。转化字符串后,根据adb是否开启,来决定从转化后的字符串中增加adb属性还是移除adb属性。
第二步,获取oem覆盖的功能。前面说过,默认系统是没有使用覆盖功能,所以这里获取的覆盖后的功能与新功能转化后的字符串是一样的。
我在分析代码的时候,脑海里一直在想,这个覆盖功能如何使用。根据我的对代码的分析,唯一的规则就是主要功能不能覆盖。举个例子,如果新设置的功能的字符串为mtp,那么覆盖数组中的其中一项元素的值应该是normal:mtp:mtp,diag
,其中nomral表示正常启动,mtp表示原始的功能,mtp,diag表示覆盖后的功能,请注意,覆盖后的功能一定要保存mtp这个主功能。当然这只是我个人对代码分析得出的结论,还没验证。这里我要吐槽一下这个功能的设计者,难道写个例子以及注意事项就这么难吗?
第三步,设置新功能。不过设置新功能前,首先要断开已经存在的连接,然后再设置新功能。设置新功能是通过setUsbConfig()
方法,来看下实现。
private void setUsbConfig(String config) {
// 设置sys.usb.config
setSystemProperty(USB_CONFIG_PROPERTY, config);
}
其实就是设置sys.usb.config的属性值,它就是代表当前设置的usb功能,其实可以通过adb shell setprop
命令设置这个属性,从而控制usb功能的切换。
设置这个属性后如何判断设置成功了呢?这就是waitForState()
所做的。
private boolean waitForState(String state) {
String value = null;
for (int i = 0; i < 20; i++) {
// 获取sys.usb.stat值
value = getSystemProperty(USB_STATE_PROPERTY, "");
// 与刚才设置的sys.usb.config属性值相比较
if (state.equals(value)) return true;
SystemClock.sleep(50);
}
return false;
}
这段代码在1秒内执行20次,获取sys.usb.state
属性值,然后与设置的sys.usb.config
属性值相比较,如果相等就表示功能设置成功。
sys.usb.state
属性代表usb实际的功能。
1. 底层如何实现usb功能切换呢?当然是响应 sys.usb.config 属性改变。
device/qcom/common/rootdir/etc/init.qcom.usb.sh
device/qcom/common/rootdir/etc/init.qcom.usb.rc
当设置 USB 为 USB Tethering mode 时,会走到下面两个 property 里:
on property:sys.usb.config=rndis,adb && property:sys.usb.configfs=0
setprop sys.usb.config rndis,${persist.vendor.usb.config.extra},adb
on property:sys.usb.config=rndis,none,adb && property:sys.usb.configfs=0
# 先写0
write /sys/class/android_usb/android0/enable 0
# 写序列号
write /sys/class/android_usb/android0/iSerial 12345678
# 写vid, pid
write /sys/class/android_usb/android0/idVendor 05C6
write /sys/class/android_usb/android0/idProduct 9024
write /sys/class/android_usb/android0/f_rndis/wceis 1
# 设置USB功能为rndis,adb
write /sys/class/android_usb/android0/functions rndis,adb
# 再写1启动功能
write /sys/class/android_usb/android0/enable 1
# 启动adb
start adbd
# 设置 sys.usb.state属性值为sys.usb.config的属性值
setprop sys.usb.state rndis,adb
而 persist.vendor.usb.config.extra 属性并没有设置,所以为 none。通过 adb shell getprop | find "sys.usb.config" 可以查看当前 USB 配置,如
2. 根据需求,将 persist.vendor.usb.config.extra 设置为 diag 即可满足需求。
on property:sys.usb.config=rndis,diag,adb && property:sys.usb.configfs=0
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/iSerial 12345678
write /sys/class/android_usb/android0/idVendor 05C6
write /sys/class/android_usb/android0/idProduct 902D
write /sys/class/android_usb/android0/f_rndis/wceis 1
write /sys/class/android_usb/android0/f_diag/clients diag
write /sys/class/android_usb/android0/functions rndis,diag,adb
write /sys/class/android_usb/android0/enable 1
start adbd
setprop sys.usb.state rndis,adb
参考:
android下usb框架系列文章---(5)Usb setting 中tethering 设置流程:https://blog.csdn.net/u011279649/article/details/17420355
深入理解Android MTP之UsbService启动分析:https://blog.csdn.net/u012165769/article/details/107008566/
android USB端口切换:https://blog.csdn.net/qq_28534581/article/details/80308518
【BUG分析】手机启动时,adb打开较晚:https://blog.csdn.net/u014135607/article/details/79599648
-----------------------
Android USB 开发详解:https://blog.csdn.net/mountain_eyes/article/details/80558834
Android USB 在framework相关源码浅析:https://blog.csdn.net/xxm282828/article/details/50628401