Android Q USB Tethering 端口切换分析

需求:在开启 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功能转化为字符串,有两种情况

  1. 如果新功能为FUNCTION_NONE,那么转化后的值从persist.sys.usb.config获取,如果获取值为NONE,就判断adb是否开启,如果开启了,转化后的值为adb,如果没有开启,转化后的值为mtp。前面分析说过,persist.sys.usb.config主要包含用于判断adb是否开启在值,然后还包含一些厂商定制且用于测试目的的功能。例如,高通项目,这个值可能为adb,diag,这个diag就是高通自己的功能。
  2. 如果新功能不为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

你可能感兴趣的:(Android,android,usb)