android apk使用framework中usb接口范例(应用RtkGps分析)

本文详细介绍如何在Android应用中监听并接入特定USB设备的过程。包括配置AndroidManifest.xml以接收USB设备连接事件,通过编写usb_device_filter文件指定目标设备,以及实现USB数据交互的具体步骤。涵盖中断、批量和控制三种数据传输方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、首先需要在AndroidManifest中要监听usb设备插入的activity或service等中加入
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" />
其中usb_device_filter是一个自己编写的文件,里面描述的是希望接管的usb设备信息。如:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Prolific -->
<usb-device vendor-id="1659"product-id="8963" /><!-- 0x067b 0x2303 PL2303 Serial-->
<usb-device vendor-id="1659"product-id="4660" /><!-- 0x067b 0x1234 DCU-11 Phone Cable-->
<usb-device vendor-id="21362"product-id="8963" /><!-- 0x5372 0x2303 Prolific2 PL2303-->
<!-- Generic comunnication device with Abstract Control Model subclass-->
<usb-device class="2"subclass="0"protocol="0" />
</resources>
主要是vendor-id和product-id的值与usb设备一致。

2、首先在MainActivity中当mNavDrawerServerSwitch被checked时,start RtkNaviService,在onStartCommand中执行processStart,在processStart中执行UsbToRtklib.start(),在UsbToRtklib.start()执行UsbReceiver.start(),在UsbReceiver.start()中增加usb事件的检测:
f = new IntentFilter();
f.addAction(ACTION_USB_DEVICE_ATTACHED);
f.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);

f.addAction(ACTION_USB_PERMISSION);

mContext.registerReceiver(mUsbStateListener, f);

mServiceThread = new UsbServiceThread();
mServiceThread.start();
执行UsbServiceThread的run时,子线程UsbServiceThread卡在serialControllerSet.block();等待serialControllerSet.open()来唤醒这个子线程。

主线程之后执行

findSupportedDevice()  ---> probeDevice(),之后就new出UsbPl2303Controller,new UsbPl2303Controller 时就会拿到各种interface和endpoint的信息,可以用来传递数据用。

                    |

1)已连接usb设备:

主线程:

requestPermission()

                    |

ACTION_USB_PERMISSION

                    |

2)没有连接usb设备:

当连接usb设备后,就会执行:

mUsbStateListener下的

ACTION_USB_DEVICE_ATTACHED

                     |

onUsbDeviceAttached();

                    |

probeDevice()  ---> probeDevice(),之后就new出UsbPl2303Controller,requestPermission()

                    |

ACTION_USB_PERMISSION

                    |

3)

主线程:

onUsbPermissionGranted()

                   |

mServiceThread.setController(controller);   其中执行serialControllerSet.open() ,唤醒UsbServiceThread线程。


UsbServiceThread子线程:

mUsbController.attach();

这个执行打开usb设备:

mUsbConnection = mUsbManager.openDevice(mUsbDevice);
if (mUsbConnection == null) {
    throw new UsbControllerException("openDevice() failed");
}

将usb设备和本程序绑定(用本程序代替usb上层驱动来管理usb设备,如:当usb设备是U盘时,代替mass storge驱动来管理u盘,此时u盘会被卸载)

for (int i=0; i< mUsbInterfaces.length; ++i) {
    if (mUsbConnection.claimInterface(mUsbInterfaces[i], true) == false) {
        for (int j=0; j<i; ++j) {
            mUsbConnection.releaseInterface(mUsbInterfaces[j]);
        }
        mUsbConnection.close();
        throw new UsbControllerException("claimInterface() failed");
    }
}

对usb设备的reset和初始化(就是驱动usb设备,发送controlTransfer设置usb设备)

if (!pl2303Reset()) {
    detach();
    throw new UsbControllerException("pl2303Reset() failed");
}
if (!pl2303Init()) {
    detach();
    throw new UsbControllerException("pl2303Init() failed");
}

使用之前拿到的bulk endpoint得到InputStream和OutputStream,为之后数据传输做准备。

inputStream = new UsbSerialInputStream(mUsbConnection, mBulkInEndpoint);
outputStream = new UsbSerialOutputStream(mUsbConnection, mBulkOutEndpoint);


interrupt endpoint 只能用自己封装urb(也就是UsbRequest)的方式传送usb数据给usb device;

bulk endpoint 可以使用mUsbConnection.bulkTransfer传送数据给usb device;

control endpoint可以使用mUsbConnection.controlTransfer传送数据给usb device;(control endpoint一般是endpoint 0,是不直接暴露给用户使用的,必须用这个函数操作。


下面详解各种用法:

1、interrupt 方式数据传输:

protected static class UsbSerialInterruptListener extends Thread {
    
    private boolean cancelRequested = false;
    private UsbDeviceConnection mUsbConnection;
    private ByteBuffer buffer;
    private UsbRequest request;
            
    public UsbSerialInterruptListener(UsbDeviceConnection connection, UsbEndpoint endpoint) {
        this.mUsbConnection = connection;
        this.setName("PL2303InterruptListener");
        buffer =  ByteBuffer.allocate(endpoint.getMaxPacketSize());
        request = new UsbRequest();
        request.initialize(connection, endpoint);

    }

     @Override
     public void run() {
    mainloop: while(!cancelRequested()) {
        request.queue(buffer, buffer.capacity());
        if (mUsbConnection.requestWait() == request) {
            if (D) Log.v(TAG, "Interrupt received: " + buffer.toString() +
                    Arrays.toString(buffer.array()));
            synchronized(this) {
                try {
                    this.wait(100);
                } catch (InterruptedException e) {
                    break mainloop;
                }
                if (cancelRequested) break mainloop;
            }
         }else {
             Log.e(TAG, "requestWait failed, exiting");
             break mainloop;
         }
       }
        Log.d(TAG, "Pl2303InterruptListener thread stopped");
    }

    public synchronized void cancel() {
          cancelRequested = true;
          this.notify();
     }

     private synchronized boolean cancelRequested() {
           return this.cancelRequested;
     }

}


2、bulk的方式传输数据

bulk in:

protected class UsbSerialInputStream extends InputStream {
        
    private static final int DEFAULT_READ_TIMEOUT_MS = 30000;
    private int mTimeout = DEFAULT_READ_TIMEOUT_MS;
            
    private UsbDeviceConnection mUsbConnection;
    private UsbEndpoint mUsbEndpoint;
    private byte rcvPkt[] = null;
            
    public UsbSerialInputStream(UsbDeviceConnection connection,
            UsbEndpoint bulkInEndpoint,
            int writeTmoutMs
            ) {
        mUsbConnection = connection;
        mUsbEndpoint = bulkInEndpoint;
        mTimeout = writeTmoutMs;
        rcvPkt = new byte[mUsbEndpoint.getMaxPacketSize()];
    }

    public UsbSerialInputStream(UsbDeviceConnection connection,
            UsbEndpoint bulkInEndpoint) {
        this(connection, bulkInEndpoint, DEFAULT_READ_TIMEOUT_MS);
    }

    @Override
    public int read() throws IOException {
        synchronized(this) {
        int rcvd = read(rcvPkt, 0, 1);
        if (rcvd == 0) throw new IOException("timeout");
        return rcvPkt[0] & 0xff;
        }
    }

  @Override
  public int read(byte[] buffer, int offset, int count) throws IOException {
    int rcvd;

    synchronized(this) {
        if (offset == 0) {
            rcvd = mUsbConnection.bulkTransfer(mUsbEndpoint, buffer,
                    count, mTimeout);
            if (rcvd < 0) throw new IOException("bulkTransfer() error");
            //if (D) Log.d(TAG, "Received " + rcvd + " bytes aligned");
            return rcvd;
        }else {
            rcvd = mUsbConnection.bulkTransfer(mUsbEndpoint,
                    rcvPkt,
                    Math.min(count, rcvPkt.length),
                    mTimeout);
            if (rcvd < 0) throw new IOException("bulkTransfer() error");
            else if (rcvd > 0) {
                System.arraycopy(rcvPkt, 0, buffer, offset, rcvd);
            }
            if (D) Log.d(TAG, "Received " + rcvd + " bytes");
                return rcvd;
        }
     }
  }
}


bulk out:

protected class UsbSerialOutputStream extends OutputStream {

    private static final int DEFAULT_WRITE_TIMEOUT_MS = 2000;
    private int mTimeout = DEFAULT_WRITE_TIMEOUT_MS;

    private UsbDeviceConnection mUsbConnection;
    private UsbEndpoint mUsbEndpoint;
    private byte sndPkt[] = null;

    public UsbSerialOutputStream(UsbDeviceConnection connection,
            UsbEndpoint bulkOutEndpoint,
            int writeTmoutMs
            ) {
        mUsbConnection = connection;
        mUsbEndpoint = bulkOutEndpoint;
        mTimeout = writeTmoutMs;
        sndPkt = new byte[mUsbEndpoint.getMaxPacketSize()];
    }

    public UsbSerialOutputStream(UsbDeviceConnection connection,
            UsbEndpoint bulkOutEndpoint) {
        this(connection, bulkOutEndpoint, DEFAULT_WRITE_TIMEOUT_MS);
    }

    @Override
    public void write(int arg0) throws IOException {
        write(new byte[] { (byte) arg0 } );
    }
     @Override
     public void write(byte[] buffer, int offset, int count) throws IOException {
         synchronized(this) {
             while(count>0) {
                 /* XXX: timeout */
                 int length = count > sndPkt.length ? sndPkt.length : count;
                 System.arraycopy(buffer, offset, sndPkt, 0, length);
                 int snd = mUsbConnection.bulkTransfer(mUsbEndpoint, sndPkt, length, mTimeout);
                 if (snd<0) throw new IOException("bulkTransfer() failed");
                 count -= snd;
                 offset += snd;
             }
         }
     }
 }


3、control方式传输:

mUsbConnection.controlTransfer(
 UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
 0x01 /* set request */,
 0x0000,
 0x0000,
 null,
 0,
 PL2303_INIT_TIMEOUT_MS) >= 0;

control方式一般需要看芯片手册来确定具体设置值替代上面的参数。


USBgps驱动&测试软件 GPS是一种接收天空上的免费GPS卫星进行定位的设备。 只要卫星不掉下来。一直可以免费使用的。 有朋友以为要收费,实际上是GPS地图正版软件的收费,不是GPS服务收费。 如果你使用我们提供的免费软件。就可以一直免费使用。 我们销售的GPS品种比较多。主要的区别是使用的接收模块不一样。 所以在阅读说明时注意和您购买的GPS相对应。不要选择错参数。 GPS和地图软件连接的参数主要有两个。 1.一个是串口的端口号。这个可以在安装好GPS驱动后,在设备管理器的硬件列表的端口下面的 USB GPS COMM PORT设备后面括号里面找到。 (进入设备列表的方法是:在桌面我的电脑图标上按鼠标右键-选择属性菜单-选择硬件菜单栏-设备管理器-点击列表里面的端口项-USB GPS COMM PORT (COM?),那个问号就是您的GPS端口号)。 2,还有一个参数是波特率。我们销售的GPS对应的波特率如下。 台湾三代USB GPS: 9600 丽台二代USB GPS:4800 真正SIRF三代 USB GPS:38400 丽台三代USB GPS: 38400 sirf新版固件:4800 这个波特率在每个导航软件里面都要使用。而且各自的设置方法不一样。 一定要留意软件的安装说明里面波特率的设置方法。 我们在软件设置里面都重点说明了的。 如果端口号和波特率设置不对。就不能和地图软件对应。会出现不能定位或提示设备正在初始化等错误。 关于导航软件: 我们附送的光盘里面主要是USB GPS驱动程序和信号测试程序。WM模拟器主程序。还有领路人的导航软件。同时提供城际通和凯立德的WM模拟器版的导航软件的下载地址。 这样一起就是:驱动程序。测试程序,领路人PC版地图和转换端口软件,灵图9地图,凯立德地图。 实际安装的步骤是:安装好驱动程序。记住端口号和自己GPS的波特率。 1.阅读GPSDIAG测试软件说明。使用测试软件测试GPS正常。 这个是测试GPS是否正常使用的。 测试正常的话就可以不用测试直接开导航软件就可以了。 2.解压光盘里面的领路人软件到您本地硬盘D盘的根目录。 修改导航软件的配置文件适合自己的电脑配置。 可以参看光盘附带的领路人软件安装图示。 然后直接运行使用就可以了。 具体使用方法可以进入软件后在设置里面查看软件的帮助文档。 3,凯立德地图。 安装和设置凯立德地图前一定要注意退出GPSDIAG测试软件,GPSGATE和领路人导航软件。否则系统会出错。 同样城际通和凯立德也不能同时运行。 领路人,凯立德和灵图是三套不同的导航软件。不能同时使用。 您可以试用后选择合适自己的其中一套使用。其他的两种做备用。 先安装光盘里面的XGPS WM模拟器主程序。阅读设置说明进行设置。 主要注意。在XGPS的配置菜单,设置SDMMC目录在硬盘的位置。在映射菜单里面的第一项 选择GPS的端口号。这个端口号是在硬件列表的实际端口号。 因为CE系统不能保存设置。所以以后每次新打开XGPS模拟器,都要设置一下这两项。 XGPS默认的输出端口是COM1.所以在凯立德地图里面的配置文件都要设置端口为COM1。 否则不能正常工作。 (最新提示!可以看光盘里面的关于端口保存的图示,可以省略上面的每次都要设置外部端口的步骤了)。 安装完成后的实际使用步骤:接好GPS-打开X-GPS-设置SDMMC指向和映射的端口-确认后在模拟器的 一机多图界面选择开启凯立德。 GPS驱动安装步骤: 首先把附带的光盘放到电脑的光驱中。 把软件解压到电脑的硬盘里面。 XP系统下把USB GPS插到电脑的USB接口。 电脑会识别出新的硬件设备并要求安装驱动。 把安装目录指向光盘的《xp驱动》文件夹。 按确认就可以安装好驱动了。 win7下直接运行驱动安装的程序。安装完成后插入GPS就会自己安装好驱动。 安装好驱动后在电脑的硬件列表里面检查新安装的硬件。 (在我的电脑图标按右键选属性,然后点击设备管理器) 在端口类别里面可以看到有个USB GPS设备。后面用括号写着COM加数字。 这个数字就是USB GPS的端口号。 请注意。一般情况GPS在室内基本没有办法实现定位的。 一定要在室外定位。最好是比较空旷的地方。 第一次定位时间会比较长一些。属于正常现象。 定位完成后,以后再使用定位速度就快很多。 全球GPS卫星一共有32个。实际正常使用大约是28个。有几个是备用的。 基本最多能搜索到的卫星是8-12个信号。其他的卫星在地球的另外一边。是不能搜索到的。 这就是有的朋友会问我的GPS信道有20个。为什么只能收到10个的原因。 特别注意!!!由于串口信号不能共享,所以不能同时使用多个GPS地图软件或是信号测试软件。 只能单独运行信号测试软件或是导航软件。否则会引起信号检测不正常导致不能定位。 例如在开启了GPSDIAG测试软件后。就不能再同时开启地图。这样会发生无法搜星的问题。 一定要退出GPSDIAG,才能正常使用导航软件。 同样道理。两个不同的导航软件也不能同时运行。只能运行一个。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值