【Android】Android Framework系列--CarUsbHandler源码分析

Android Framework系列–CarUsbHandler源码分析

  • 本文基于Android12源码。

CarUsbHandler是Android Car提供的服务之一,其用车载USB连接的场景。
车载USB有其特殊应用场景,比如AndroidAuto、CarLife等。而Android的做法是在其原有的USB服务上,扩展了专门针对CarUSB的Service。
进而言之,整个Android Car服务( /packages/services/Car),也是基于这种Extend的想法建立的。

那么CarUsbHandler主要做了哪些事呢?

  • Set CarUsbHandler as the USB handling component。USBHostManager(android usbservice)收到设备连接通知时,会优先交给USB handling component进行处理。
  • 弹出View,让用户选择处理事件的应用。
  • 开机CarUsbHandler 服务启动后,自检当前有无设备连接。如果有符合要求的设备,则弹出View,让用户选择处理事件的应用。

因为车载的AndroidAuto、Carplay、Carlife、USB相关应用一般会由厂商重新开发。所以使用CarUsbHandler的场合并不多。

CarUsbHandler源码分析

USB handling component的设置与处理

源码路径位于 packages/services/Car/car-usb-handler/,属于Application Service。
在这里插入图片描述
在AndroidManifest.xml中,定义了启动Activity、Service、接收的广播、以及交互的应用(queries字段,这里理解为找到处理action最匹配的应用。)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"package="android.car.usb.handler"><queries><intent><action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/></intent></queries><applicationandroid:label="@string/app_name"android:icon="@drawable/ic_launcher"android:directBootAware="true"><activity android:name=".UsbHostManagementActivity"android:theme="@android:style/Theme.DeviceDefault.Dialog"android:launchMode="standard"><meta-dataandroid:name="distractionOptimized"android:value="true"/></activity><service android:name=".BootUsbService"android:exported="false"android:singleUser="true"></service><receiver android:name=".BootUsbScanner"android:directBootAware="true"><intent-filter><action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/></intent-filter></receiver></application>
</manifest>

上面说过,CarUsbHandler的任务之一是Set CarUsbHandler as the USB handling component。为了完成这个任务,CarUsbHandler通过overlay方式复写了相关系统配置。
packages/services/Car/car_product/overlay/frameworks/base/core/res/res/values/config.xml

<!-- Set CarUsbHandler as the USB handling component by default -->
<string name="config_UsbDeviceConnectionHandling_component">android.car.usb.handler/android.car.usb.handler.UsbHostManagementActivity</string>

UsbHostManager在启动时,会根据这个属性设置其 USB handling component

// frameworks/base/services/usb/java/com/android/server/usb/UsbHostManager.java
public UsbHostManager(Context context, UsbAlsaManager alsaManager,UsbPermissionManager permissionManager) {// 读取config_UsbDeviceConnectionHandling_component配置String deviceConnectionHandler = context.getResources().getString(com.android.internal.R.string.config_UsbDeviceConnectionHandling_component);if (!TextUtils.isEmpty(deviceConnectionHandler)) {//  设置USB handling componentsetUsbDeviceConnectionHandler(ComponentName.unflattenFromString(deviceConnectionHandler));}
}public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) {synchronized (mHandlerLock) {mUsbDeviceConnectionHandler = usbDeviceConnectionHandler;}
}private @Nullable ComponentName getUsbDeviceConnectionHandler() {synchronized (mHandlerLock) {return mUsbDeviceConnectionHandler;}
}

然后当车机处于Host模式下,有设备插入的时候,会触发UsbHostManager的usbDeviceAdded函数。

/* Called from JNI in monitorUsbHostBus() to report new USB devices
Returns true if successful, i.e. the USB Audio device descriptors are
correctly parsed and the unique device is added to the audio device list.
*/
@SuppressWarnings("unused")
private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,byte[] descriptors) {
if (DEBUG) {Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
}synchronized (mLock) {UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder();if (newDeviceBuilder == null) {Slog.e(TAG, "Couldn't create UsbDevice object.");} else {// It is fine to call this only for the current user as all broadcasts are// sent to all profiles of the user and the dialogs should only show once.ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();if (usbDeviceConnectionHandler == null) {} else {getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,usbDeviceConnectionHandler);}}
}
return true;
}

通过getUsbDeviceConnectionHandler得到上面设置的USB handling component,然后调用UsbProfileGroupSettingsManagerdeviceAttachedForFixedHandler

public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {final Intent intent = createDeviceAttachedIntent(device);// Send broadcast to running activity with registered intentmContext.sendBroadcastAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));ApplicationInfo appInfo;try {// Fixed handlers are always for parent userappInfo = mPackageManager.getApplicationInfoAsUser(component.getPackageName(), 0,mParentUser.getIdentifier());} catch (NameNotFoundException e) {}Intent activityIntent = new Intent(intent);activityIntent.setComponent(component);try {// 启动USB handling component,也就是// android.car.usb.handler.UsbHostManagementActivitymContext.startActivityAsUser(activityIntent, mParentUser);} catch (ActivityNotFoundException e) {Slog.e(TAG, "unable to start activity " + activityIntent);}
}

首先发送广播,告知相关注册者这次事件。然后启动USB handling component,也就是android.car.usb.handler.UsbHostManagementActivity这个Activity。

CarUsbHandler c启动后的处理

Host模式下,USB接入后。通过USB handling component,启动CarUsbHandler相关的CarUsbHandler。即UsbHostManagementActivity
源码路径packages/services/Car/car-usb-handler/src/android/car/usb/handler/
在这里插入图片描述

public class UsbHostManagementActivity extends Activity {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.usb_host);mUsbHandlersDialog = findViewById(R.id.usb_handlers_dialog);mHandlersList = findViewById(R.id.usb_handlers_list);mHandlerTitle = findViewById(R.id.usb_handler_heading);mListAdapter = new HandlersAdapter(this);mHandlersList.setAdapter(mListAdapter);mHandlersList.setOnItemClickListener(mHandlerClickListener);mController = new UsbHostController(this, new UsbCallbacks());mPackageManager = getPackageManager();}
}

UsbHostManagementActivity启动开始走生命周期,onCreate阶段初始化相关对象。画面相关的内容这里就pass了。重点关注处理逻辑的部分UsbHostController这个对象。

public UsbHostController(Context context, UsbHostControllerCallbacks callbacks) {mContext = context;mCallback = callbacks;mHandler = new UsbHostControllerHandler(Looper.myLooper());mUsbSettingsStorage = new UsbSettingsStorage(context);mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);mUsbResolver = new UsbDeviceHandlerResolver(mUsbManager, mContext, this);IntentFilter filter = new IntentFilter();filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);context.registerReceiver(mUsbBroadcastReceiver, filter);
}

UsbHostController创建了UsbHostControllerHandler,起了一个Handler用来postmessage专门处理事件。注册接收ACTION_USB_DEVICE_ATTACHED和ACTION_USB_DEVICE_DETACHED这两个广播。
回到UsbHostManagementActivity 这个对象,接下来走到onResume的流程。

public void onResume() {super.onResume();UserManager userManager = getSystemService(UserManager.class);if (userManager.isUserUnlocked() || getUserId() == UserHandle.USER_SYSTEM) {processDevice();} else {}}
}

用户已解锁,或者以System用户运行的情况下,调用processDevice。

 private void processDevice() {UsbDevice connectedDevice = getDevice();if (connectedDevice != null) {mController.processDevice(connectedDevice);} else {finish();}}private UsbDevice getDevice() {if (!UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {return null;}return (UsbDevice) getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
}

首先取得当前接入的UsbDevice,如果没有的话。则直接finish掉自己。否则调用UsbHostControllerHandler的processDevice进行处理。

/*** Processes device new device.* <p>* It will load existing settings or resolve supported handlers.*/
public void processDevice(UsbDevice device) {// 查找用于处理这个Device的配置内容。UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(device);if (settings == null) {// 首次是找不到的。所以请求“解决这个Device”resolveDevice(device);} else {}
}

首次弹出时,没有选择谁可以处理这个USBDevice。所以settings为空。接下来调用resolveDevice尝试去处理这个USBDevice。该函数最终会调用到UsbDeviceHandlerResolverdoHandleResolveHandlers函数。

private void doHandleResolveHandlers(UsbDevice device) {Intent intent = createDeviceAttachedIntent(device);List<UsbHandlerPackage> matches = getDeviceMatches(device, intent, false);if (LOCAL_LOGD) {Log.d(TAG, "matches size: " + matches.size());}List<UsbDeviceSettings> settings = new ArrayList<>();for (UsbHandlerPackage pkg : matches) {settings.add(createSettings(device, pkg));}UsbDeviceConnection devConnection = UsbUtil.openConnection(mUsbManager, device);if (devConnection != null && AoapInterface.isSupported(mContext, device, devConnection)) {for (UsbHandlerPackage pkg : getDeviceMatches(device, intent, true)) {if (mAoapServiceManager.isDeviceSupported(device, pkg.mAoapService)) {settings.add(createSettings(device, pkg));}}}// 回调函数,添加到View上。deviceProbingComplete(device, settings);
}// 这个函数通过PMS查找,可以处理该Inten(入参)的应用有哪些。
private List<UsbHandlerPackage> getDeviceMatches(UsbDevice device, Intent intent, boolean forAoap) {return matches;
}

通过getDeviceMatches查找到,可以处理Device事件的应用有哪些。然后添加到自己的Setting里面,然后调用deviceProbingComplete往View上添加显示的内容(内容大体就是可以处理这个Device事件的Component是谁),如果找到的应用list为空,deviceProbingComplete会将UsbHostManagementActivity finish掉(如果过程很快,会闪一下

回到UsbHostManagementActivity,此时该Activity中的View上就显示了,点击view选择处理这个事件的应用(component)

private final AdapterView.OnItemClickListener mHandlerClickListener =new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, final View view, int position, long id) {UsbDeviceSettings settings = (UsbDeviceSettings) parent.getItemAtPosition(position);settings.setDefaultHandler(true);mController.applyDeviceSettings(settings);}
};

调用UsbHostController的applyDeviceSettings

 public void applyDeviceSettings(UsbDeviceSettings settings) {mUsbSettingsStorage.saveSettings(settings);Message msg = mHandler.obtainMessage();msg.obj =new UsbHostControllerHandlerDispatchData(getActiveDevice(), settings, DISPATCH_RETRY_ATTEMPTS, false);msg.what = UsbHostControllerHandler.MSG_DEVICE_DISPATCH;msg.sendToTarget();}

转到handler中,然后会调用到 UsbDeviceHandlerResolver的dispatch函数。在这个函数里,对于Device类型最一些判断(是否是Aoap模式,这个模式用于AndroidAuto),然后通过调用startActivity,把Device连接事件交给目标component处理。

/*** Dispatches device to component.*/
public boolean dispatch(UsbDevice device, ComponentName component, boolean inAoap,StartAoapFailureListener failureListener) {ActivityInfo activityInfo;try {activityInfo = mPackageManager.getActivityInfo(component, PackageManager.GET_META_DATA);} catch (NameNotFoundException e) {Log.e(TAG, "Activity not found: " + component);return false;}Intent intent = createDeviceAttachedIntent(device);if (inAoap) {// }intent.setComponent(component);mUsbManager.grantPermission(device, activityInfo.applicationInfo.uid);mContext.startActivity(intent);mHandler.requestCompleteDeviceDispatch();return true;
}

CarUsbHandler自启动的处理

CarUsbHandler属于android.car.usb.handler这个package,其对应的Service源码是BootUsbService。会在开机阶段接收到广播后启动。

 <service android:name=".BootUsbService"android:exported="false"android:singleUser="true"></service><receiver android:name=".BootUsbScanner"android:directBootAware="true"><intent-filter><action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/></intent-filter></receiver>
public class BootUsbService extends Service {@Overridepublic Binder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {mDeviceList = intent.getParcelableArrayListExtra(USB_DEVICE_LIST_KEY);processDevices();return START_NOT_STICKY;}private void processDevices() {for (UsbDevice device : mDeviceList) {Log.d(TAG, "Processing device: " + device.getProductName());handle(this, device);}stopSelf();}private void handle(Context context, UsbDevice device) {Intent manageDevice = new Intent(context, UsbHostManagementActivity.class);manageDevice.setAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);manageDevice.putExtra(UsbManager.EXTRA_DEVICE, device);manageDevice.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivityAsUser(manageDevice, UserHandle.CURRENT);}
}

onBind返回空,无法通过BindService连接到这个服务(不给外部使用)。其自启动后,其实也是启动UsbHostManagementActivity这个Activity。后续的流程,就跟跟上面通过UsbHostManagere启动UsbHostManagementActivity基本上一样了。

综上,CarUsbHandler的用途大体上就是选择哪个应用来处理接入的Device(Host模式下)。如果没有使用到它的话,可以直接将其剪裁掉(不打包,或者去掉config_UsbDeviceConnectionHandling_component的配置)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/197484.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用html2canvas转换table为图片时合并单元格rowspan失效,无边框显示问题解决(React实现)

最近使用 html2canvas导出Table表单为图片&#xff0c;但是转换出的图片被合并的单元格没有显示边框 查了原因是因为我为tr设置了背景色&#xff0c;然后td设置了rowspan&#xff0c;设置了rowspan的单元格就会出现边框不显示的问题。 解决方法就是取消tr的背景色&#xff0c;然…

力扣 hot100 最长连续序列 哈希去重 双指针

128. 最长连续序列 ⭐ AC code class Solution {public int longestConsecutive(int[] nums) {if (nums.length 0)// 特判为空的数组&#xff0c;返回0return 0; // set实现去重HashSet<Integer> set new HashSet<>();for (int x : nums)set.add(x);Object[] a…

Kafka(四)消费者消费消息

文章目录 如何确保不重复消费消息&#xff1f;消费者业务逻辑重试消费者提交自定义反序列化类消费者参数配置及其说明重要的参数session.time.ms和heartbeat.interval.ms和group.instance.id增加消费者的吞吐量消费者消费的超时时间和poll()方法的关系 消费者消费逻辑启动消费者…

ExcelBDD PHP Guideline

在PHP里面支持利用Excel的BDD&#xff0c;也支持利用Excel进行参数化测试 ExcelBDD Use Excel file as BDD feature file, get example data from Excel files, support automation tests. Features The main features provided by this library are: Read test data acco…

【win32_001】win32命名规、缩写、窗口

整数类型 bool类型 使用注意&#xff1a; 一般bool 的false0&#xff1b;true1 | 2 | …|n false是为0&#xff0c;true是非零 不建议这样用&#xff1a; if (result TRUE) // Wrong! 因为result不一定只返回1&#xff08;true&#xff09;&#xff0c;当返回2时&#xff0c…

集合框架面试题

一、集合容器的概述 1. 什么是集合 集合框架&#xff1a;用于存储数据的容器。 集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。 任何集合框架都包含三大块内容&#xff1a; 对外的接口、接口的实现和对集合运算的算 法。 接口&#xff1a;表示集合的抽象数据…

鸿蒙:实现两个Page页面跳转

效果展示 这篇博文在《鸿蒙&#xff1a;从0到“Hello Harmony”》基础上实现两个Page页面跳转 1.构建第一个页面 第一个页面就是“Hello Harmony”&#xff0c;把文件名和显示内容都改一下&#xff0c;改成“FirstPage”&#xff0c;再添加一个“Next”按钮。 Entry Compone…

Axure9 基本操作(二)

1. 文本框、文本域 文本框&#xff1a;快速实现提示文字与不同类型文字显示的效果。 2. 下拉列表、列表框 下拉列表&#xff1a;快速实现下拉框及默认显示项的效果。 3. 复选框、单选按钮 4. 利用动态面板实现同个按键的不同状态切换

Codewhisperer 使用评价

最近亚⻢逊推出了一款基于机器学习的 AI 编程助手 Amazon CodeWhisperer&#xff0c;可以实时提供代码建议。在编写代码时&#xff0c;它会自动根据现有的代码和注释给出建议。Amazon CodeWhisperer 与GitHub Copilot类似&#xff0c;主要的功能有: 代码补全注释和文档补全代码…

图像分类(四) 全面解读复现GoogleNet_InceptionV1-V4

论文解读 InceptionV1 前言 论文题目: Going Deeper with Convolutions Googlenet论文原文地址:https://arxiv.org/pdf/1409.4842.pdf 之前看过VGG的论文&#xff08;VGG精读直达&#xff09;。当时VGG获得了 2014 ILSVRC 图像分类的第二名&#xff0c;今天来看一下第一名…

Linux | 进程间通信

目录 前言 一、进程间通信的基本概念 二、管道 1、管道的基本概念 2、匿名管道 &#xff08;1&#xff09;原理 &#xff08;2&#xff09;测试代码 &#xff08;3&#xff09;读写控制相关问题 a、读端关闭 b、写端关闭 c、读快写慢 d、读慢些快 &#xff08;4&a…

Linux 系统编程,Binder 学习,文件访问相关的接口

文章目录 Linux 系统编程&#xff0c;Binder 学习&#xff0c;文件访问相关的接口1.概念2.linux文件结构3.文件描述符4.Linux文件系统的两类常用接口&#xff0c;linux系统内置库函数4.1 open4.2 close4.3 read4.4 write 5.标准I/O库函数5.1 fopen Linux 系统编程&#xff0c;B…

NewStarCTF2023 Reverse Week3 EzDLL WP

分析 这里调用了z3h.dll中的encrypt函数。 用ida64载入z3h.dll 直接搜索encrypt 找到了一个XTEA加密。接着回去找key和密文。 发现key 这里用了个调试状态来判断是否正确&#xff0c;v71&#xff0c;要v7&#xff1d;1才会输出Right&#xff0c;即程序要处于飞调试状态。 可…

一、MySQL-Replication(主从复制)

1.1、MySQL Replication 主从复制&#xff08;也称 AB 复制&#xff09;允许将来自一个MySQL数据库服务器&#xff08;主服务器&#xff09;的数据复制到一个或多个MySQL数据库服务器&#xff08;从服务器&#xff09;。 根据配置&#xff0c;您可以复制数据库中的所有数据库&a…

ESP32 Arduino实战协议篇-搭建独立的 Web 服务器

在此项目中,您将创建一个带有 ESP32 的独立 Web 服务器,该服务器使用 Arduino IDE 编程环境控制输出(两个 LED)。Web 服务器是移动响应的,可以使用本地网络上的任何浏览器设备进行访问。我们将向您展示如何创建 Web 服务器以及代码如何逐步工作。 项目概况 在直接进入项目…

基于机器学习的居民消费影响因子分析预测

项目视频讲解: 基于机器学习的居民消费影响因子分析预测_哔哩哔哩_bilibili 主要工作内容: 完整代码: import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import missingno as msno import warnings warnings.filterwarnin…

驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接

参考&#xff1a;https://www.cnblogs.com/sam-snow-v/p/15917898.html eclipse链接SQL Server出现问题 笔者使用Open JDK 17&#xff0c;SQL Server 2016&#xff0c;项目中使用JPA操作数据库。测试环境没问题&#xff0c;生产环境出现如题所示“驱动程序无法通过使用安全套接…

qsort函数使用方法总结

目录 一、qsort函数原型 二、compar参数 三、各种类型的qsort排序 1. int 数组排序 2. 结构体排序 3. 字符串指针数组排序 4. 字符串二维数组排序 四、回调函数 1. 什么是回调函数 2. 为什么要用回调函数&#xff1f; 3. 怎么使用回调函数&#xff1f; 4.下面是…

C++多线程编程(2):四种线程管理方法

文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 文章目录 线程管理get_idsleep_forsleep_untilyield 线程管理 有一个this_thread的名称空间中定义了许多的线程管理方法&#xff1a; get_id&#xff1a;获取当前线程idsleep_for&#xff1a;当前线程休眠一段时间sleep_…

Linux系统编程学习 NO.9——git、gdb

前言 本篇文章简单介绍了Linux操作系统中两个实用的开发工具git版本控制器和gdb调试器。 git 什么是git&#xff1f; git是一款开源的分布式版本控制软件。它不仅具有网络功能&#xff0c;还是服务端与客户端一体的软件。它可以高效的处理程序项目中的版本管理。它是Linux内…