需求:在开启 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功能为NONEif (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {return;}// 如果设置NONE还是失败了,那么再试一次设置NONEif (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属性值还是为NONEif (functions.equals(UsbManager.USB_FUNCTION_NONE))// 如果adb开启,返回adb,否则返回mtpfunctions = 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.configsetSystemProperty(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=0setprop sys.usb.config rndis,${persist.vendor.usb.config.extra},adb
on property:sys.usb.config=rndis,none,adb && property:sys.usb.configfs=0# 先写0write /sys/class/android_usb/android0/enable 0# 写序列号write /sys/class/android_usb/android0/iSerial 12345678# 写vid, pidwrite /sys/class/android_usb/android0/idVendor 05C6write /sys/class/android_usb/android0/idProduct 9024write /sys/class/android_usb/android0/f_rndis/wceis 1# 设置USB功能为rndis,adbwrite /sys/class/android_usb/android0/functions rndis,adb# 再写1启动功能write /sys/class/android_usb/android0/enable 1# 启动adbstart 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=0write /sys/class/android_usb/android0/enable 0write /sys/class/android_usb/android0/iSerial 12345678write /sys/class/android_usb/android0/idVendor 05C6write /sys/class/android_usb/android0/idProduct 902Dwrite /sys/class/android_usb/android0/f_rndis/wceis 1write /sys/class/android_usb/android0/f_diag/clients diagwrite /sys/class/android_usb/android0/functions rndis,diag,adbwrite /sys/class/android_usb/android0/enable 1start adbdsetprop 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 Q USB Tethering 端口切换分析相关推荐

  1. Android下USB Accessory的实现分析 (三)--- Android Open AccessoryProtocol

    本文 接着前面的文章 <Android下USB Accessory的实现分析 (二)- 底层驱动设计实现> 2.1.4 Android Open AccessoryProtocol 为了支 ...

  2. Android下USB Accessory的实现分析 (一)--- AOA背景介绍

    摘要 本文介绍了USB Accessory的一些背景知识,并从Linux驱动到Android Framework层,阐述了USB accessory的整个实现过程. 关键词: Android,USB, ...

  3. Android Q 基站刷新接口源码分析 适配双卡手机基站刷新逻辑

    目录 一.获取基站信息的两个关键方法 getAllCellInfo调用流程总结 requestCellInfoUpdate 流程总结 问题 二.双卡手机适配 Android Q requestCell ...

  4. android连接usb外设通讯_Android设备使用USB的硬件接口

    最近业界的发展显示,智能手机/便携系统与自动化系统或机械系统之间存在巨大的市场潜力.2011年春季谷歌引入的Android开放访问架构,开启了基于Android操作系统的设备的巨大可能--允许智能手机 ...

  5. 基于Android Q电池服务分析

    基于Android Q的电池服务分析之充电类型判断 开局先说明一下我的需求和我遇到的难题 问题 插入充电没有提示音和图标更新 插入充电没有任何反应和提示,但是确实是在充电 需求 在设置的电池中增加充电 ...

  6. Android USB tethering相关代码

    1. 代码位置 packages/apps/Settings/src/com/android/settings/TetherSettings.java frameworks/base/services ...

  7. android wifi tethering,新增Wi-Fi/USB Tethering功能

    新增Wi-Fi/USB Tethering功能 对于常常需要在外用电脑上网的人来说,智能手机具备Modem功能,通常都是他们选择手机的一大因素,之前HTC的机种,像是Hero/Legend/Desir ...

  8. 源码分析 merge 标签减少布局层级的秘密(Android Q)

    源码分析 merge 标签减少布局层级的秘密(Android Q) 我在<Android 渲染性能优化--你需要知道的一切!>一文中介绍过,merge 标签用于减少 View 树的层次来优 ...

  9. Android Q 10.1 KeyMaster源码分析(二) - 各家方案的实现

    写在之前 这两篇文章是我2021年3月初看KeyMaster的笔记,本来打算等分析完KeyMaster和KeyStore以后再一起做成一系列贴出来,后来KeyStore的分析中断了,这一系列的文章就变 ...

最新文章

  1. Python正则表达式:match(),search(),findall()与finditer()的用法
  2. L2-006. 树的遍历
  3. uva-11111-栈
  4. 弹出确定_Redmi K30 Pro再剧透:弹出式全面屏,没有高刷
  5. Qt Creator使用Clang代码模型解析C ++文件
  6. mysql 查看导出数据字典
  7. P1152 欢乐的跳( python3实现)
  8. Xshell连接Ubuntu时提示SSH服务器拒绝了密码
  9. PHP教程 数据库和MySQL_PHP教程 - MySQL 创建数据库和表
  10. Linux 工具套件 —— binutils、readelf
  11. Bing Maps进阶系列二:使用GeocodeService进行地理位置检索
  12. 拆解CRM头牌“销售易” | 如何做好客户关系管理?
  13. 计算方法 6.插值法
  14. 路由器服务器修改密码,TP-Link TL-WR842N路由器设置密码
  15. 清除Marco1!$A$1提示软件日志.
  16. python基础教程:用Python秒算24点实现及原理详解
  17. Google 以图搜图 - 相似图片搜索原理 - Java实现
  18. 微信小程序加载第三方字体
  19. android 手机震动功能吗,Android编程实现手机震动功能的方法
  20. tailwindcss 官网(六)定制:配置( `tailwind.config.js `、-p、important、核心插件、`resolveConfig`)、主题 `theme` 配置

热门文章

  1. 为什么要认真准备Java面试,编程语言排行榜告诉你
  2. 笔记本联想(Lenovo)G40-70M加装内存和SSD固态硬盘
  3. linux环境启动tomcat成功后,访问链接一直在转圈
  4. 咸鱼Micropython— network
  5. Photoshop设计精讲精练笔记
  6. .NET MongoDB Driver GridFS 2.2原理及使用示例
  7. Excel 中根据一列查询其他列中的值
  8. 沉浸其境,共赴云栖数智硬核美学
  9. unity 实现物体破碎效果的一些方法 - 细雨淅淅
  10. exsi rh2288hv5 驱动_华为RH2288H服务器引导ServiceCD安装Windows Server操作系统