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

news2024/11/17 13:45:57

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>

    <application
        android: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-data
                android: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 component
        setUsbDeviceConnectionHandler(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 intent
	mContext.sendBroadcastAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));

	ApplicationInfo appInfo;
	try {
		// Fixed handlers are always for parent user
		appInfo = 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.UsbHostManagementActivity
		mContext.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 {
    @Override
    public 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() {
    @Override
    public 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 {
    @Override
    public Binder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
	
    }

    @Override
    public 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.coloradmin.cn/o/1226732.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

十七、Linux的组管理

1、Linux组基本介绍 在linux中的每个用户必须属于一个组&#xff0c;不能独立于组外。在linux中每个文件所有者、所在组、其它组的概念 1.所有者 2.所在组 3.其他组 4.改变用户所在的组 2、文件/目录 所有者 一般为文件的创建者&#xff0c;谁创建了该文件&#xff0c;就自…

【图解算法】- 快乐数还能这么解?

一 - 前言 介绍&#xff1a;大家好啊&#xff0c;我是hitzaki辰。 社区&#xff1a;&#xff08;完全免费、欢迎加入&#xff09;日常打卡、学习交流、资源共享的知识星球。 自媒体&#xff1a;我会在b站/抖音更新视频讲解 或 一些纯技术外的分享&#xff0c;账号同名&#xff…

MongoDB随记

MongoDB 1、简单介绍2、基本术语3、shard分片概述背景架构路由功能chunk&#xff08;数据分片&#xff09;shard key&#xff08;分片键值&#xff09; 4、常用命令 1、简单介绍 MongoDB是一个分布式文件存储的数据库&#xff0c;介于关系数据库和非关系数据库之间&#xff0c…

第 372 场 LeetCode 周赛题解

A 使三个字符串相等 求三个串的最长公共前缀 class Solution { public:int findMinimumOperations(string s1, string s2, string s3) {int n1 s1.size(), n2 s2.size(), n3 s3.size();int i 0;for (; i < min({n1, n2, n3}); i)if (!(s1[i] s2[i] && s2[i] s…

【智能家居】5、主流程设计以及外设框架编写与测试

目录 一、主流程设计 1、工厂模式结构体定义 &#xff08;1&#xff09;指令工厂 inputCmd.h &#xff08;2&#xff09;外设工厂 controlDevices.h 二、外设框架编写 1、创建外设工厂对象bathroomLight 2、编写相关函数框架 3、将浴室灯相关操作插入外设工厂链表等待被调…

内容运营工具:标签体系

一.分类和标签的区别 ■标签是扁平的&#xff0c;分类是层级的。 ■标签是精确的&#xff0c;分类是粗糙的。 ■标签是多维的&#xff0c;分类是一维的。 二.标签的本质&#xff1a;元数据 事实上&#xff0c;在数据领域&#xff0c;有一个鼎鼎大名的词汇与标签极其雷同&…

再高级的打工人也只是打工人!

再高级的打工人也只是打工人&#xff01; OpenAI CEO 奥特曼被罢免的事情人尽皆知「虽然&#xff0c;今天又复职了。。」&#xff0c;我们能从中学到什么呢&#xff1f; CEO 也能被裁&#xff0c;这应该是最近几年被裁名单里面&#xff0c;职级最高的一个人了吧。你再也不用担…

吐血整理,金融银行测试的“火“到底在哪里?银行测试真正实施...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 银行里的软件测试…

C#WPF中的实现读取和写入文件的几种方式

说明&#xff1a;C#中实现读取和写入的类根据需要来选择。 1、File类 File类是用于操作文件的工具类&#xff0c;提供了对文件进行创建、复制、删除、移动和打开单一文件的静态方法。但需要注意的是&#xff0c;WPF中使用File的类&#xff0c;需要先引用System.IO下的命名空间。…

数据结构【DS】图的遍历

BFS 要点 需要一个辅助队列visited数组&#xff0c;防止重复访问 复杂度 时间复杂度&#xff1a;访问结点的时间访问所有的边的时间 广度优先生成树 邻接表存储的图的表示方式不唯一&#xff0c;生成树也不唯一 DFS 复杂度 时间复杂度&#xff1a;访问结点的时间访问所有…

Java工具包Hutool框架

Hutool是一个Java基础工具类,对文件、流、加密解密、转码、正则、线程、XML 等 JDK 方法进行封装,组成各种 Util 工具类。官网地址:https://www.hutool.cn/。 添加依赖 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artif…

气候更换,气运也会随之变化

天人合一&#xff0c;人天相应&#xff0c;人体与宇宙天体的运行互相感应相通&#xff0c;与大自然的万千变化紧密联系。阴阳转换&#xff0c;带来的气场和磁场的变化&#xff0c;对自然界万事万物和人影响很大。 蒹葭苍苍&#xff0c;白露为霜&#xff0c;所谓伊人&#xff0…

基于未来搜索算法优化概率神经网络PNN的分类预测 - 附代码

基于未来搜索算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于未来搜索算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于未来搜索优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

2023最新最全【Nacos】零基础安装教程

一、下载Nacos1.4.1 二、单机版本安装 2.1 将下载的nacos安装包传输到服务器2.2 解压文件2.3 进入bin目录下 单机版本启动2.4 关闭nacos2.5 访问Nacos地址 IP&#xff1a;8848/nacos 三、集群版本的安装 3.1 复制nacos安装包&#xff0c;修改为nacos8849&#xff0c;nacos88…

HR人才测评,提高招聘效率降低用人风险

随着社会的不断进步&#xff0c;越来越多的企业在人力资源管理中&#xff0c;引入人才测评工具。人才是构成一个企业的基础&#xff0c;是企业不断发展的保障&#xff0c;同时&#xff0c;人才也是一个企业的核心竞争力之一。所以&#xff0c;人才的素质对一个企业至关重要。现…

[ 一刷完结撒花!! ] Day50 力扣单调栈 : 503.下一个更大元素II |42. 接雨水 | 84.柱状图中最大的矩形

Day50 力扣单调栈 : 503.下一个更大元素II &#xff5c;42. 接雨水 | 84.柱状图中最大的矩形 503.下一个更大元素II第一印象看完题解的思路实现中的困难感悟代码 42. 接雨水第一印象看完题解的思路暴力解法单调栈解法 实现中的困难感悟代码 84.柱状图中最大的矩形第一印象看完…

037、目标检测-SSD实现

之——简单实现 目录 之——简单实现 杂谈 正文 1.类别预测层 2.边界框预测 3.多尺度输出联结做预测&#xff08;提高预测效率&#xff09; 4.多尺度实现 5.基本网络块 6.完整模型 杂谈 原理查看&#xff1a;037、目标检测-算法速览-CSDN博客 正文 1.类别预测层 类别…

【力扣面试经典150题】(链表)K 个一组翻转链表

题目描述 力扣原文链接 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只…

“释放视频潜力,批量放大视频尺寸,高效提升视频质量“

在视频制作和编辑的过程中&#xff0c;我们经常需要调整视频的尺寸。然而&#xff0c;一个一个地手动调整不仅耗时&#xff0c;还容易出错。为了解决这个问题&#xff0c;现在有一款全新的视频批量剪辑工具&#xff0c;可以帮助你批量将视频尺寸放大&#xff0c;提升工作效率。…

Argo Rollouts结合Service进行Blue-Green部署

删除03 部署04 rootk8s-master01:~/learning-jenkins-cicd/09-argocd-and-rollout/rollout-demos# kubectl delete -f 03-rollouts-with-prometheus-analysis.yaml rootk8s-master01:~/learning-jenkins-cicd/09-argocd-and-rollout/rollout-demos# kubectl apply -f 04-rol…