Android/C/C++ 中解决 USB UnMount(禁止USB端口)

引:因为某些特殊需求,需要在某些设备接入车机的时候,动态UnMount USB设备,因为代码其中有一些方法是自定义过的,所以该文章仅供思路参考。

(20200319 更新):在后续跟进中,发现如果卸载U盘的话,(U盘资源未被释放,可以类似正在播放U盘里的音乐和视频或者在扫描U盘,如果需要Unmount U盘的时候)vold检测到了extractor进程正在提取id3,规定时间内没有释放优盘资源,所以vold把它kill了,会执行ProcessKiller ,最后会走到system/vold/Utils.cpp下的KillProcessesUsingPath()下,在KillProcessesUsingPath_1 和KillProcessesUsingPath_2 之间会Sleep 5秒,所以就会导致,广播:android.intent.action.MEDIA_EJECT 和 android.intent.action.MEDIA_UNMOUNTED 相差有6s + ,这里是不可避免的。

status_t KillProcessesUsingPath(const std::string& path) {
 LOG(WARNING) << "KillProcessesUsingPath" ;
    const char* cpath = path.c_str();
    if (Process::killProcessesWithOpenFiles(cpath, SIGINT) == 0) {
        return OK;
    }
     LOG(WARNING) << "KillProcessesUsingPath_1" ;
    if (!VolumeManager::shutting_down) sleep(5);
    LOG(WARNING) << "KillProcessesUsingPath_2" ;
    if (Process::killProcessesWithOpenFiles(cpath, SIGTERM) == 0) {
        return OK;
    }
...............

一:第一种方法是在java层做处理
如下需要涉及到/System/Vold 和frameWork 以及device层的修改。
其实主要是在framework层的修改,用到的方法也很简单,需要用到:

StorageManager
StorageVolume

根据我当前项目的需求,是要求插入某一个device 的时候,判断出是当前需要的设备的时候,卸载/禁止USB2 端口。

里面可能有一些方法是已经重写过的,正规的API 不一定有,比如getUsbId()就没有,不过还是如篇头所说提供下解决思路吧。

思路:为了设备使用期间,USB2不能被使用,除了主动unmount之后,还需要保证下一次USB插拔的时候拦截USB挂载消息。(主动卸载只是生效一次),所以步骤就是2个,先主动unmount,在设置prop属性在Vold中拦截USB挂载消息。

vold简介:
 vold进程接收来自内核的外部设备消息,用于管理和控制Android平台外部存储设备,包括SD插拨、挂载、卸载、格式化等;当外部设备发生变化时,内核通过Netlink发送uEvent格式的消息给用户空间程序,Netlink
是一种基于异步通信机制,在内核与用户应用间进行双向数据传输的特殊 socket,用户态应用使用标准的socket API 就可以使用netlink 提供的强大功能。

涉及的主要文件:
Framework层主要看MountService.java和NativeDaemonConnector.java;
Native层主要看NetlinkManager.cpp, NetlinkHandler.cpp,
VolumeManager.cpp,CommandListener.cpp

核心代码:

**
 * <P>if device Connected Unmount USB2</P>
 * <P>beibei 20191231</P>
 */
private StorageManager mStorageManager;
private final String UNMOUNT_DEVICE_ID = "USB2";
private void StartUnmountUsbAccessory()  {
    Log.d(TAG, "StartUnmountUsbAccessory");
    try {
        if (mStorageManager == null){
            Log.d(TAG, "mStorageManager is null");
            mStorageManager = mContext.getSystemService(StorageManager.class);
        }
        List<StorageVolume> volumes =mStorageManager.getStorageVolumes();
        for (int i = 0; i < volumes.size(); i++) {
            Log.d(TAG, "StorageVolume start foreach");
            //if usb the not's emulated/Primary , It's not an external usb
            if (!volumes.get(i).isEmulated() && !volumes.get(i).isPrimary()) {
                Log.d(TAG, "isEmulated && isPrimary");
                if (UNMOUNT_DEVICE_ID.equals(volumes.get(i).getUsbId())) {
                    Log.d(TAG, "imStorageManager.unmount Success");
                    mStorageManager.unmount(volumes.get(i).getId());
                }
            }
        }
    } catch(Exception e){
       e.printStackTrace();
    }
 }

解释:通过StorageManager 获取到StorageVolume,并通过循环遍历,之后通过isPrimary(不是外部存储卷)和isEmulated (不是模拟卷)过滤后,
下面的就是匹配自己想要UnMount的ID了,匹配成功后,调用mStorageManager.unmount
**注意:**该方法调用会抛出System.error ,需要处理异常。
必须增加 try catch

/**
 * Returns true if the volume is the primary shared/external storage, which is the volume
 * backed by {@link Environment#getExternalStorageDirectory()}.
 */
public boolean isPrimary() {
    return mPrimary;
}

/**
 * Returns true if the volume is emulated.
 *
 * @return is removable
 */
public boolean isEmulated() {
    return mEmulated;
}

然后其次就是 设置 prop

case MSG_PROJECTION_DEVICE_CONNECTED_SUCCESS:
    StartUnmountUsbAccessory();
    SystemProperties.set(UNMOUNT_DEVICE_PROP_STATE,"true");
    break;
case MSG_PROJECTION_DEVICE_DISCONNECTED_SUCCESS:
    SystemProperties.set(UNMOUNT_DEVICE_PROP_STATE,"false");
    break;

关键的拦截USB mount的在/system/vold/ NetlinkHandler.cpp,的onEvent消息事件中。

void NetlinkHandler::onEvent(NetlinkEvent *evt) {
    VolumeManager *vm = VolumeManager::Instance();
    const char *subsys = evt->getSubsystem();

    if (!subsys) {
        SLOGW("No subsystem found in netlink event");
        return;
    }
   /***Add usb2 disable temporary scheme 20191231 start***/
      char args[PROPERTY_VALUE_MAX];
      property_get("vendor.device.usb2",args,"false");
      if (strcmp(args,"true")==0){
          const char *path = evt->findParam("DEVPATH");
          if (strstr(path,USB_2_PATH)!=NULL){
              SLOGW("DISABLE USB2");
              return;
          }
      }
  /***Add usb2 disable temporary scheme 20191231  end***/

    if (!strcmp(subsys, "block")) {
        vm->handleBlockEvent(evt);
    }
}

至于Prop怎么设置,百度下,一般写在device/fsl/… 的 .mk文件中
https://blog.csdn.net/qq_31332467/article/details/104751769
Ps: PRODUCT_PROPERTY_OVERRIDES +=
vendor.device.usb2 = false

二:第二种可以在C++ 层
主要的一个代码就是 echo shell 命令

 #include<stdlib.h>
 system("echo ‘1-1.2" >/sys/bus/usb/drivers/usb/unbind");

思路步骤是这样的:
在C++代码中找到合适的位置,先判断出什么情况下,接入什么设备需要unMound USB,增加tag,当满足的时候就执行system(“echo ‘1-1.2” >/sys/bus/usb/drivers/usb/unbind");
这个我也是在串口命令进行过测试,实际在代码中,我没有测试过,因为有了第一种方法。

测试方法有效性:
串口进入到Android板子内部:
解绑:echo “1-1.2” > unbind
绑定:echo “1-1.2” > bind
具体代码中实际应用没有测试。

在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值