【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.hqwc.cn/news/195994.html

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

相关文章

【信息安全】浅谈SQL注入攻击的概念、原理和防范措施:简单分析六种常见攻击方式

用户登录 在开发Web应用程序时&#xff0c;用户登录是一个非常常见的功能。然而&#xff0c;不安全的用户登录功能可能会导致安全漏洞&#xff0c;例如SQL注入和跨站脚本攻击。 SQL注入 SQL注入是一种常见的攻击技术&#xff0c;攻击者通过在用户输入的数据中插入恶意SQL代码…

Jmeter做接口测试

1.Jmeter的安装以及环境变量的配置 Jmeter是基于java语法开发的接口测试以及性能测试的工具。 jdk&#xff1a;17 (最新的Jeknins&#xff0c;只能支持到17) jmeter&#xff1a;5.6 官网&#xff1a;http://jmeter.apache.org/download_jmeter.cgi 认识JMeter的目录&#xff1…

华为麒麟服务器--硬盘问题

记录以下今天处理的服务器&#xff1a; 情况说明&#xff1a;linux 系统&#xff0c;不知道什么原因系统就突然不能用了&#xff08;据说是前段时间断电来着&#xff0c;但是机房有应急电源&#xff09;。 系统环境&#xff1a; 服务器&#xff1a;华为RH2288H V3 服务器 服…

leetcode刷题日志-68.文本左右对齐

给定一个单词数组 words 和一个长度 maxWidth &#xff0c;重新排版单词&#xff0c;使其成为每行恰好有 maxWidth 个字符&#xff0c;且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词&#xff1b;也就是说&#xff0c;尽可能多地往每行中放置单词。必要时可…

C++学习笔记(11)——类,友元类、内部类、匿名对象、优化

系列文章目录 C学习笔记 C学习笔记&#xff08;1&#xff09;——C的诞生C学习笔记&#xff08;3&#xff09;——缺省函数与函数重载C学习笔记&#xff08;4&#xff09;——引用C学习笔记(5)——inline内联C学习笔记&#xff08;6&#xff09;——auto关键字C学习笔记&…

Python 自动化(十八)admin后台管理

admin后台管理 什么是admin后台管理 django提供了比较完善的后台数据库的接口&#xff0c;可供开发过程中调用和测试使用 django会搜集所有已注册的模型类&#xff0c;为这些模型类提供数据管理界面&#xff0c;供开发使用 admin配置步骤 创建后台管理账号 该账号为管理后…

[AutoSar]工程中的cpuload陷阱(三)测试

目录 关键词平台说明背景一、 测试结果对比1.1 不带cache1.2 带cache 二、小结 关键词 嵌入式、C语言、autosar 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语言C&#xff0c;C编译器HighTec (GCC) 背景 接着工程中的cpuload陷阱&#xff08;二)中的描述…

边缘计算是如何为元宇宙提供动力的?

构建元宇宙虚拟世界并不简单&#xff0c;也并不便宜&#xff0c;但是还是有许多大型公司正在转移大量资源来开发他们的元宇宙业务&#xff0c;当然大部分企业注意力都围绕着 VR 耳机、AR 眼镜、触觉手套和其他沉浸式虚拟现实体验所需的可穿戴硬件。虽然这种沉浸式的体验是最终结…

【开源】基于Vue.js的智能教学资源库系统

项目编号&#xff1a; S 050 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S050&#xff0c;文末获取源码。} 项目编号&#xff1a;S050&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 课程档案模块2.3 课…

LangChain 4用向量数据库Faiss存储,读取YouTube的视频文本搜索Indexes for information retrieve

接着前面的Langchain&#xff0c;继续实现读取YouTube的视频脚本来问答Indexes for information retrieve LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗…

CICD 持续集成与持续交付——git

git使用 [rootcicd1 ~]# yum install -y git[rootcicd1 ~]# mkdir demo[rootcicd1 ~]# cd demo/ 初始化版本库 [rootcicd1 demo]# git init 查看状态 [rootcicd1 demo]# git status[rootcicd1 demo]# git status -s #简化输出 [rootcicd1 demo]# echo test > README.md[roo…

前端调取摄像头并实现拍照功能

前言 最近接到的一个需求十分有意思&#xff0c;设计整体实现了前端仿 微信扫一扫 的功能。整理了一下思路&#xff0c;做一个分享。 tips: 如果想要实现完整扫一扫的功能&#xff0c;你需要掌握一些前置知识&#xff0c;这次我们先讲如何实现拍照并且保存的功能。 一. wind…