当前位置: 首页 > news >正文

RK3576-Android15_Usb白名单功能实现篇二

实现USB 白名单功能

文章目录

  • 前言-需求
  • 一、 参考资料
  • 二、 修改文件-修改方案
    • 修改文件
    • 实现方案
    • 配置属性,表示三种模式
    • UsbHostManager 类 openDevice 方法中添加逻辑
    • StorageManagerService 存储服务中-拦截Usb 进行卸载
    • DiskInfo 源码分析
    • 小结
  • 三、源码分析
    • 为什么 UsbHostManager 中 usbDeviceAdded 拦截不起作用
    • StorageManagerService 源码分析
      • IVoldListener 知识点
        • IVoldListener.aidl
        • onVolumeCreatedLocked 方法
        • IVold 回调 IVoldListener接口
      • ServiceManager.getService("vold") 中vold 分析 - VolumeBase.cpp
        • 为什么不再守护进程 VolumeBase.cpp 中实现拦截逻辑
  • 总结


前言-需求

需求:USB 类型设备接入白名单
1) 默认: USB类型设备都可以接入
2) 全部拦截: USB类型设备全部不可用接入
3)根据vid/pid 进行拦截,指定vid pid 类型设备才可以接入使用
备注:USB类型很多的,目前主要分为:U盘、外接UVC 相机、Camera2相机打开外接的USBCamera

一、 参考资料

RK-USB白名单功能实现
Android 12系统源码_存储(二)StorageManagerService服务
Android存储系统源码走读(一):StorageManagerService
Android存储系统源码走读(二):vold

二、 修改文件-修改方案

修改文件

device\rockchip\rk3576\rk3576_u\rk3576_u.mk
frameworks\base\services\usb\java\com\android\server\usb\UsbHostManager.java 
frameworks\base\core\java\android\os\storage\DiskInfo.java
\frameworks\base\services\core\java\com\android\server\StorageManagerService.java 

实现方案

如下只是提供部分思路,实际代码和逻辑还需自己分析

配置属性,表示三种模式

device\rockchip\rk3576\rk3576_u\rk3576_u.mk

添加两个属性:persist.fise.custom.usbmode 、 persist.fise.custom.usbdata 分别用来表示当前USB模式和vid /pid 拦截下USB模式下存储的vid/pid 数据

UsbHostManager 类 openDevice 方法中添加逻辑

在 openDevice 方法中,进行拦截,如下伪代码,可参考: 核心思想就是你在打开USB设备的时候进行拦截过滤

    /***  Opens the specified USB device*/public ParcelFileDescriptor openDevice(String deviceAddress,UsbUserPermissionManager permissions, String packageName, int pid, int uid) {synchronized (mLock) {if (isDenyListed(deviceAddress)) {throw new SecurityException("USB device is on a restricted bus");}UsbDevice device = mDevices.get(deviceAddress);if (device == null) {// if it is not in mDevices, it either does not exist or is denylistedthrow new IllegalArgumentException("device " + deviceAddress + " does not exist or is restricted");}// modify by fangchen start Log.d(TAG,"=========openDevice=======:openDevice   packageName:"+packageName+"   pid:"+pid+"     uid:"+uid);String usbMode = SystemProperties.get(PERSIST_FISE_CUSTOM_USBMODE);int dVid = device.getVendorId();int dPid = device.getProductId();Log.d(TAG,"=========openDevice:=======dVid:"+dVid+"   dPid:"+dPid);if("2".equals(usbMode)){Log.d(TAG,"getDeviceList usbMode ==2  custom vidpidWhiteList filter ");String usbData = SystemProperties.get(PERSIST_FISE_CUSTOM_USBDATA);if (!TextUtils.isEmpty(usbData)) {Log.d(TAG, "getDeviceList======================usbData:==:"+usbData);Log.d(TAG, "getDeviceList==== usbData:"+usbData);List<String> newList = Arrays.asList(usbData.split(","));vidpid_whiteList=newList;}if(!this.vidpid_whiteList.contains( dVid+""+ dPid)){throw new IllegalArgumentException("device " + deviceAddress + " forbiden all not in whiteList");}} if("1".equals(usbMode)){Log.d(TAG," forbiden all ...");throw new IllegalArgumentException("device " + deviceAddress + " does not exist  permission");}Log.d(TAG," zhunbei dakai nativeOpenDevice");// modify by fangchen end permissions.checkPermission(device, packageName, pid, uid);return nativeOpenDevice(deviceAddress);}}

StorageManagerService 存储服务中-拦截Usb 进行卸载

当U盘类型设备插入后 ,内核自动识别到型号, 通过过滤操作,让不符合类型的设备不让挂载掉。 因为挂载逻辑是底层 U盘本身属于热插拔机制,跟内核关联的。 插入后信号uevent 信号到中间层,中间层Framework 调用指令自动挂载了,所以最优解决方案是内核周公来进行拦截信号,但是就不用想了吧… 要user 空间和内核空间交换数据、动态处理存储,算了吧 行不通的。

部分伪代码,参考如下:

................ else if (vol.type == VolumeInfo.TYPE_PUBLIC) {// TODO: only look at first public partitionString fSuid=vol.getFsUuid();int type=vol.getType();DiskInfo diskInfo=vol.getDisk();if(diskInfo!=null){Log.d(TAG," diskInfo!=null ");boolean isUsb=diskInfo.isUsb();boolean isTF=diskInfo.isSd();Log.d(TAG," fSuid:"+fSuid+"   type:"+type+"   isUsb:"+isUsb+"     isTF:"+isTF);String diskSysPath=diskInfo.getSysPath();Log.d(TAG," ===============================diskSysPath="+diskSysPath);String fileFirstStr = diskSysPath.replaceFirst(":.*", "");Log.d(TAG,"================fileFirstStr======:"+fileFirstStr);String fileSecondStr = fileFirstStr.replaceFirst("/[^/]*$", "");Log.d(TAG,"================fileSecondStr======:"+fileSecondStr);String idVendor = readSysFsFile(fileSecondStr+"/idVendor");String idProduct = readSysFsFile(fileSecondStr+"/idProduct");int idVendorInt = Integer.parseInt(idVendor, 16);int idProductInt = Integer.parseInt(idProduct, 16);if (idVendor != null) {Log.d(TAG, "Vendor ID: " + idVendor);} else {Log.e(TAG, "Failed to read Vendor ID");}if (idProduct != null) {Log.d(TAG, "idProduct ID: " + idProduct);} else {Log.e(TAG, "Failed to read idProduct ID");}Log.d(TAG,"=============getUsbVidPid shiliu:="+idVendor+"_"+idProduct);Log.d(TAG,"=============getUsbVidPid shi:="+idVendorInt+"_"+idProductInt);String usbMode = SystemProperties.get(PERSIST_FISE_CUSTOM_USBMODE);Log.d(TAG,"==============================================================");if("1".equals(usbMode)){Log.d(TAG,"=======================usbMode  1 =======forbden all================================");return;}if("2".equals(usbMode)){Log.d(TAG,"usbMode ==2  custom vidpidWhiteList filter ");String usbData = SystemProperties.get(PERSIST_FISE_CUSTOM_USBDATA);Log.d(TAG,"====deviceList usbData:"+usbData);if (!TextUtils.isEmpty(usbData)) {Log.d(TAG, "update==== usbData:"+usbData);List<String> newList = Arrays.asList(usbData.split(","));vidpid_whiteList=newList;Log.d(TAG,"==========vidpid_whiteList:"+vidpid_whiteList);Log.d(TAG,"==========vidpid_whiteList:"+vidpid_whiteList);String realVidPidStr=idVendorInt +""+idProduct;Log.d(TAG,"=====================realVidPidStr 1 : idVendorInt:="+idVendorInt+"   idProduct:"+idProduct);Log.d(TAG,"=====================realVidPidStr 2 :="+realVidPidStr);if(!vidpid_whiteList.contains(realVidPidStr)){Log.d(TAG,"=======================usbMod 2  not in  whiteList return ");return;}else{Log.d(TAG,"=======================in  vidpid_whiteList can mount ");}} else{Log.d(TAG,"====usbMode ==2  usbData is empty, no  usb can mount=");return;}}}else{Log.d(TAG," diskInfo= =null ");}if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)&& vol.disk.isDefaultPrimary()) {Slog.v(TAG, "Found primary storage at " + vol);vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;}// Adoptable public disks are visible to apps, since they meet// public API requirement of being in a stable location.if (vol.disk.isAdoptable()) {vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;}................

DiskInfo 源码分析

    StorageService 服务里面 关联的是USB 类型的U盘,它和UsbManager 先关的USB 设备没有半毛钱关系,UsbManaer侧重于 状态管理和连接 操作。那么 UsbManager 可以获取到设备的设备信息 vid/pid 等,也就是说StorageService 本身是关联不到 vid pid 这些属性的 。
    所以我们在StrorageManagerService 中会遇到致命问题,得不到vid/pid ,还好 看下如下日志和操作:在虚拟文件系统节点路径下 我们可以找到vid / pid ,那么 根据路径读取出来不就行了嘛。
在这里插入图片描述

所以 我们在DiskInfo 里面对相关属性 sys 设置一个get 操作就可以了嘛。 伪代码如下:

	 public String getSysPath() {return sysPath;}public void setSysPath(String sysPath) {this.sysPath = sysPath;}

小结

  • 上面的修改方案足以实现USB类型的拦截功能了,比如打印机、比如串口通讯。
  • 为什么没有实现原生Camera2 拦截UVC类型的USB设备终端? 那是 如 StorageManagerService 一样, Camera2 本身用的CameraService 和 UsbHotManager 本身是两条线,打开相机操作本身是在jni 实现,native 操作 打开的, 无法在Framework层获取vid/pid 来实现拦截操作
  • 对于UsbHostManager 和 其它涉及到Usb 关联服务的区别和联系,在实际操作中才会更深刻理解的。

三、源码分析

为什么 UsbHostManager 中 usbDeviceAdded 拦截不起作用

在 RK-USB白名单功能实现 这篇文章中,我们本身介绍了解决方案,就是在usbDeviceAdded 中实现拦截的,其实这个方案本身是错误的,不行的,下面具体分析。

首先查看一下在哪里调用的:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

总的来说,这里C 层的register 、 add usb 相关的逻辑,只是调用了一次Java层的方法,实际操作都是JNI操作,那么你在Framework层的Java 代码中去拦截是没有意义的,中间层只是一个状态而已。

StorageManagerService 源码分析

参考资料中的讲解的蛮详细的,细品还是能获取很多知识点:
Android 12系统源码_存储(二)StorageManagerService服务
Android存储系统源码走读(一):StorageManagerService
Android存储系统源码走读(二):vold

StorageManagerService 本身知识点蛮多在,这里重点关注挂载相关的内容。

先看类注释

/*** Service responsible for various storage media. Connects to {@code vold} to* watch for and manage dynamically added storage, such as SD cards and USB mass* storage. Also decides how storage should be presented to users on the device.*/
class StorageManagerService extends IStorageManager.Stubimplements Watchdog.Monitor, ScreenObserver
  • 负责各种存储介质的服务。连接到Vold
  • 监控并管理动态添加的存储设备,如SD卡和USB大容量存储设备存储。还决定如何在设备上向用户展示存储情况。

IVoldListener 知识点

源码路径:/system/vold/binder/android/os/IVoldListener.aidl 那么它一定是通过binder 通信机制
看下源码:

IVoldListener.aidl
package android.os;/** {@hide} */
oneway interface IVoldListener {void onDiskCreated(@utf8InCpp String diskId, int flags);void onDiskScanned(@utf8InCpp String diskId);void onDiskMetadataChanged(@utf8InCpp String diskId,long sizeBytes, @utf8InCpp String label, @utf8InCpp String sysPath);void onDiskDestroyed(@utf8InCpp String diskId);void onVolumeCreated(@utf8InCpp String volId,int type, @utf8InCpp String diskId, @utf8InCpp String partGuid, int userId);void onVolumeStateChanged(@utf8InCpp String volId, int state, int userId);void onVolumeMetadataChanged(@utf8InCpp String volId,@utf8InCpp String fsType, @utf8InCpp String fsUuid, @utf8InCpp String fsLabel);void onVolumePathChanged(@utf8InCpp String volId,@utf8InCpp String path);void onVolumeInternalPathChanged(@utf8InCpp String volId,@utf8InCpp String internalPath);void onVolumeDestroyed(@utf8InCpp String volId);
}

这些咋一看不都是 相关Volume相关操作嘛,不同的状态。

onVolumeCreatedLocked 方法

回顾一下 ,我们实现USB 的U盘是在哪里操作的? 如下 再次贴一下伪代码:

   @GuardedBy("mLock")private void onVolumeCreatedLocked(VolumeInfo vol) {final ActivityManagerInternal amInternal =LocalServices.getService(ActivityManagerInternal.class);if (vol.mountUserId >= 0 && !amInternal.isUserRunning(vol.mountUserId, 0)) {Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "+ Integer.toString(vol.mountUserId) + " is no longer running.");return;}if (vol.type == VolumeInfo.TYPE_EMULATED) {........................} else if (vol.type == VolumeInfo.TYPE_PUBLIC) {// TODO: only look at first public partitionString fSuid=vol.getFsUuid();int type=vol.getType();DiskInfo diskInfo=vol.getDisk();if(diskInfo!=null){Log.d(TAG," diskInfo!=null ");boolean isUsb=diskInfo.isUsb();boolean isTF=diskInfo.isSd();Log.d(TAG," fSuid:"+fSuid+"   type:"+type+"   isUsb:"+isUsb+"     isTF:"+isTF);String diskSysPath=diskInfo.getSysPath();Log.d(TAG," ===============================diskSysPath="+diskSysPath);String fileFirstStr = diskSysPath.replaceFirst(":.*", "");Log.d(TAG,"================fileFirstStr======:"+fileFirstStr);String fileSecondStr = fileFirstStr.replaceFirst("/[^/]*$", "");Log.d(TAG,"================fileSecondStr======:"+fileSecondStr);String idVendor = readSysFsFile(fileSecondStr+"/idVendor");String idProduct = readSysFsFile(fileSecondStr+"/idProduct");int idVendorInt = Integer.parseInt(idVendor, 16);int idProductInt = Integer.parseInt(idProduct, 16);if (idVendor != null) {Log.d(TAG, "Vendor ID: " + idVendor);} else {Log.e(TAG, "Failed to read Vendor ID");}if (idProduct != null) {Log.d(TAG, "idProduct ID: " + idProduct);} else {Log.e(TAG, "Failed to read idProduct ID");}Log.d(TAG,"=============getUsbVidPid shiliu:="+idVendor+"_"+idProduct);Log.d(TAG,"=============getUsbVidPid shi:="+idVendorInt+"_"+idProductInt);String usbMode = SystemProperties.get(PERSIST_FISE_CUSTOM_USBMODE);Log.d(TAG,"==============================================================");if("1".equals(usbMode)){Log.d(TAG,"=======================usbMode  1 =======forbden all================================");return;}if("2".equals(usbMode)){Log.d(TAG,"usbMode ==2  custom vidpidWhiteList filter ");String usbData = SystemProperties.get(PERSIST_FISE_CUSTOM_USBDATA);Log.d(TAG,"====deviceList usbData:"+usbData);if (!TextUtils.isEmpty(usbData)) {Log.d(TAG, "update==== usbData:"+usbData);List<String> newList = Arrays.asList(usbData.split(","));vidpid_whiteList=newList;Log.d(TAG,"==========vidpid_whiteList:"+vidpid_whiteList);Log.d(TAG,"==========vidpid_whiteList:"+vidpid_whiteList);String realVidPidStr=idVendorInt +""+idProduct;Log.d(TAG,"=====================realVidPidStr 1 : idVendorInt:="+idVendorInt+"   idProduct:"+idProduct);Log.d(TAG,"=====================realVidPidStr 2 :="+realVidPidStr);if(!vidpid_whiteList.contains(realVidPidStr)){Log.d(TAG,"=======================usbMod 2  not in  whiteList return ");return;}else{Log.d(TAG,"=======================in  vidpid_whiteList can mount ");}} else{Log.d(TAG,"====usbMode ==2  usbData is empty, no  usb can mount=");return;}}}else{Log.d(TAG," diskInfo= =null ");}if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)&& vol.disk.isDefaultPrimary()) {Slog.v(TAG, "Found primary storage at " + vol);vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;}// Adoptable public disks are visible to apps, since they meet// public API requirement of being in a stable location.if (vol.disk.isAdoptable()) {vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;}//--------------------rk-code----------------------if ("false".equals(SystemProperties.get("cts_gts.status", "false")) &&"true".equals(SystemProperties.get("ro.vendor.udisk.visible"))) {Log.d(TAG,"-----for all public volume is visible-----");vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;}//-------------------------------------------------vol.mountUserId = mCurrentUserId;mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();} else if (vol.type == VolumeInfo.TYPE_PRIVATE) {mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();} else if (vol.type == VolumeInfo.TYPE_STUB) {............................mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();} else {Slog.d(TAG, "Skipping automatic mounting of " + vol);}}

从伪代码上看,我们的拦截操作是在 onVolumeCreatedLocked 方法中调用的,那么 onVolumeCreatedLocked 又是被谁调用的呢?

看一下如下代码:

  @Overridepublic void onVolumeCreated(String volId, int type, String diskId, String partGuid,int userId) {Trace.instant(Trace.TRACE_TAG_SYSTEM_SERVER,"SMS.onVolumeCreated: " + volId + ", " + userId);synchronized (mLock) {final DiskInfo disk = mDisks.get(diskId);final VolumeInfo vol = new VolumeInfo(volId, type, disk, partGuid);vol.mountUserId = userId;mVolumes.put(volId, vol);onVolumeCreatedLocked(vol);}}

这不就找到了 onVolumeCreated 这个方法了嘛? 就是 IVoldListener 中的回调方法嘛

IVold 回调 IVoldListener接口

上面知识点,已经知道了 IVoldListener 是一个binder 接口,我们看看实际的定义看看,为什么是怎么回调aidl 接口的:

mVold.setListener(mListener); 源码如下,

  private void connectVold() {IBinder binder = ServiceManager.getService("vold");if (binder != null) {try {binder.linkToDeath(new DeathRecipient() {@Overridepublic void binderDied() {Slog.w(TAG, "vold died; reconnecting");mVold = null;connectVold();}}, 0);} catch (RemoteException e) {binder = null;}}if (binder != null) {mVold = IVold.Stub.asInterface(binder);try {mVold.setListener(mListener);} catch (RemoteException e) {mVold = null;Slog.w(TAG, "vold listener rejected; trying again", e);}} else {Slog.w(TAG, "vold not found; trying again");}...........................}

ServiceManager.getService(“vold”) 中vold 分析 - VolumeBase.cpp

这里细节不再看分析过程了,这个基本知识点。 这里要分析的就是vold 服务,是怎么回调aidl 接口的
查找源码:
在这里插入图片描述
我们看看几处核心的相关回调:

  • onVolumeStateChanged 回调
void VolumeBase::setState(State state) {mState = state;auto listener = getListener();if (listener) {listener->onVolumeStateChanged(getId(), static_cast<int32_t>(mState),static_cast<int32_t>(mMountUserId));}
}
  • onVolumePathChanged 回调
status_t VolumeBase::setPath(const std::string& path) {if (mState != State::kChecking) {LOG(WARNING) << getId() << " path change requires state checking";return -EBUSY;}mPath = path;auto listener = getListener();if (listener) listener->onVolumePathChanged(getId(), mPath);return OK;
}
  • onVolumeInternalPathChanged 回调
status_t VolumeBase::setInternalPath(const std::string& internalPath) {if (mState != State::kChecking) {LOG(WARNING) << getId() << " internal path change requires state checking";return -EBUSY;}mInternalPath = internalPath;auto listener = getListener();if (listener) {listener->onVolumeInternalPathChanged(getId(), mInternalPath);}return OK;
}
  • onVolumeCreated 回调
status_t VolumeBase::create() {CHECK(!mCreated);mCreated = true;status_t res = doCreate();auto listener = getListener();if (listener) {listener->onVolumeCreated(getId(), static_cast<int32_t>(mType), mDiskId, mPartGuid,mMountUserId);}setState(State::kUnmounted);return res;
}
  • onVolumeDestroyed 回调
status_t VolumeBase::destroy() {CHECK(mCreated);if (mState == State::kMounted) {unmount();setState(State::kBadRemoval);} else {setState(State::kRemoved);}auto listener = getListener();if (listener) {listener->onVolumeDestroyed(getId());}status_t res = doDestroy();mCreated = false;return res;
}
为什么不再守护进程 VolumeBase.cpp 中实现拦截逻辑

这个问题其实蛮好的,毕竟底层拦截检测机制不就从源头实现了嘛? 因为这里只是 jni层, 内核拦截不显示。 这里也可以拦截,数据存储、获取、应用通讯都是难点。 假若 有相关技能,其实这里拦截也是可以的。
为什么讲到这个知识点,因为往下分析会分析到 NetLink 、udev、mdev 热插拔机制,那里才是源头解决问题的最优解! 但是技术难度太强,偏底层。

总结

  • StorageMnagerService 服务了解
  • Volume 通信机制、热插拔机制了解
http://www.dtcms.com/a/395675.html

相关文章:

  • Spring中使用Apache Http客户端调第三方系统接口临时查看请求体参数
  • Linux系统-debian系的软件包管理
  • PCB工艺中的深微孔
  • 关于Pycharm中在运行出现语法错误:Non-UTF-8 code starting with
  • 构建AI智能体:四十一、大模型思维链提示工程:技术原理与行业应用案例分析
  • 鸿蒙系统中音视频的采集与播放
  • HTTPS 双向认证抓包实战,原理、难点、工具与可操作的排查流程
  • 开源跨平台文件管理工具,告别杂乱无章的数据世界
  • Node.js事件循环机制
  • Linux---文件系统
  • 循环语句效率与规范的原理及示例解析
  • Three.js 开发实战教程(四):相机系统全解析与多视角控制
  • 介绍一下SQLite的基本语法和常用命令
  • 台式电脑如何恢复出厂设置?Win10 强制重置详细教程
  • 李宏毅2023机器学习作业 HW02实操
  • 【C++实战㉜】深入C++动态内存分配:从理论到实战的进阶之路
  • 小鼠抗新冠病毒N蛋白IgG亚型抗体ELISA检测试剂盒
  • 安防监控中常见的报警类型有哪些?国标GB28181平台EasyGBS的报警能力解析
  • C++ 中 size_t 的用(用于跨平台编译)
  • C++ 拷贝构造函数调用时机
  • 手机镜头参数介绍
  • 区块链技术之《(1)—概述》
  • 复盘与导出工具最新版V31.0版本更新---彻底修复卡死闪退bug,盘中实时丝滑
  • 深入理解JVM类加载与垃圾回收机制
  • Ethernet/IP转ProfiNet网关选型指南:欧姆龙PLC对接研祥工控机最佳实践
  • Java 面试高频手撕题清单
  • 【论文阅读】Long-VLA:释放视觉语言动作模型在机器人操作中的长时程能力
  • Python poplib 库全解析:POP3 邮件收取的完整指南
  • DanceTrack数据集介绍
  • 【无标题】话题学习笔记1,话题基本了解