package com.bg7yoz.ft8cn.connector; /** * 用于USB串口操作的类。USB串口驱动在serialport目录中,主要是CDC、CH34x、CP21xx、FTDI等。 * * @author BGY70Z * @date 2023-03-20 */ import android.annotation.SuppressLint; import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import android.os.Build; import android.util.Log; import com.bg7yoz.ft8cn.BuildConfig; import com.bg7yoz.ft8cn.serialport.CdcAcmSerialDriver; import com.bg7yoz.ft8cn.serialport.UsbSerialDriver; import com.bg7yoz.ft8cn.serialport.UsbSerialPort; import com.bg7yoz.ft8cn.serialport.UsbSerialProber; import com.bg7yoz.ft8cn.serialport.util.SerialInputOutputManager; import java.io.IOException; import java.util.ArrayList; import java.util.EnumSet; public class CableSerialPort { private static final String TAG = "CableSerialPort"; private OnConnectorStateChanged onStateChanged; public static final int SEND_TIMEOUT = 2000; private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID; public enum UsbPermission {Unknown, Requested, Granted, Denied} private UsbPermission usbPermission = UsbPermission.Unknown; private BroadcastReceiver broadcastReceiver; private final Context context; private int vendorId = 0x0c26;//设备号 private int portNum = 0;//端口号 private int baudRate = 19200;//波特率 private UsbSerialPort usbSerialPort; private SerialInputOutputManager usbIoManager; public SerialInputOutputManager.Listener ioListener = null; private UsbManager usbManager; private UsbDeviceConnection usbConnection; private UsbSerialDriver driver; private boolean connected = false;//是否处于连接状态 public CableSerialPort(Context mContext, SerialPort serialPort, int baud, OnConnectorStateChanged connectorStateChanged) { vendorId = serialPort.vendorId; portNum = serialPort.portNum; baudRate = baud; context = mContext; this.onStateChanged=connectorStateChanged; doBroadcast(); } public CableSerialPort(Context mContext) { context = mContext; doBroadcast(); } private void doBroadcast() { broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (INTENT_ACTION_GRANT_USB.equals(intent.getAction())) { usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) ? UsbPermission.Granted : UsbPermission.Denied; connect(); } } }; } private boolean prepare() { registerRigSerialPort(context); UsbDevice device = null; usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); //此处把connection设成Null,这样在后面来通过是否null判断是否有权限。 usbConnection = null; //此处是不是做个权限判断? if (usbManager == null) { return false; } for (UsbDevice v : usbManager.getDeviceList().values()) { if (v.getVendorId() == vendorId) { device = v; } } if (device == null) { Log.e(TAG, String.format("串口设备打开失败: 没有找到设备0x%04x", vendorId)); return false; } driver = UsbSerialProber.getDefaultProber().probeDevice(device); if (driver == null) { //试着把未知的设备加入到cdc驱动上 driver = new CdcAcmSerialDriver(device); } if (driver.getPorts().size() < portNum) { Log.e(TAG, "串口号不存在,无法打开。"); return false; } Log.d(TAG, "connect: port size:" + String.valueOf(driver.getPorts().size())); usbSerialPort = driver.getPorts().get(portNum); usbConnection = usbManager.openDevice(driver.getDevice()); return true; } //@RequiresApi(api = Build.VERSION_CODES.S) public boolean connect() { connected = false; if (!prepare()) { //return false; } if (driver == null) { if (onStateChanged!=null){ onStateChanged.onRunError("无法连接串口,没有驱动或串口不存在!"); } return false; } if (usbConnection == null && usbPermission == UsbPermission.Unknown && !usbManager.hasPermission(driver.getDevice())) { usbPermission = UsbPermission.Requested; PendingIntent usbPermissionIntent; //在android12 开始,增加了PendingIntent.FLAG_MUTABLE保护机制,所以要做版本判断 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { usbPermissionIntent = PendingIntent.getBroadcast(context, 0 , new Intent(INTENT_ACTION_GRANT_USB), PendingIntent.FLAG_MUTABLE); } else { usbPermissionIntent = PendingIntent.getBroadcast(context, 0 , new Intent(INTENT_ACTION_GRANT_USB), 0); } //PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(context, 0 // , new Intent(INTENT_ACTION_GRANT_USB), PendingIntent.FLAG_MUTABLE); // , new Intent(INTENT_ACTION_GRANT_USB), 0); usbManager.requestPermission(driver.getDevice(), usbPermissionIntent); prepare(); } if (usbConnection == null) { if (onStateChanged!=null){ onStateChanged.onRunError("无法连接串口,可能没有访问USB设备的权限!"); } return false; } try { usbSerialPort.open(usbConnection); //波特率、停止位 usbSerialPort.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); usbIoManager = new SerialInputOutputManager(usbSerialPort, new SerialInputOutputManager.Listener() { @Override public void onNewData(byte[] data) { if (ioListener != null) { ioListener.onNewData(data); } } @Override public void onRunError(Exception e) { if (ioListener != null) { ioListener.onRunError(e); } disconnect(); } }); usbIoManager.start(); Log.d(TAG, "串口打开成功!"); connected = true; if (onStateChanged!=null){ onStateChanged.onConnected(); } } catch (Exception e) { Log.e(TAG, "串口打开失败: " + e.getMessage()); if (onStateChanged!=null){ onStateChanged.onRunError("串口打开失败: " + e.getMessage()); } disconnect(); return false; } return true; } public boolean sendData(final byte[] src) { if (usbSerialPort != null) { try { usbSerialPort.write(src, SEND_TIMEOUT); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "发送数据出错:" + e.getMessage()); return false; } return true; } else { Log.e(TAG, "无法发送数据,串口没有打开。"); return false; } } public void disconnect() { connected = false; if (onStateChanged!=null){ onStateChanged.onDisconnected(); } if (usbIoManager != null) { usbIoManager.setListener(null); usbIoManager.stop(); } usbIoManager = null; try { if (usbSerialPort != null) { usbSerialPort.close(); } } catch (IOException ignored) { } usbSerialPort = null; } public void registerRigSerialPort(Context context) { Log.d(TAG, "registerRigSerialPort: registered!"); context.registerReceiver(broadcastReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB)); } public void unregisterRigSerialPort(Activity activity) { Log.d(TAG, "unregisterRigSerialPort: unregistered!"); activity.unregisterReceiver(broadcastReceiver); } /** * 打开和关闭RTS * * @param rts_on true:打开,false:关闭 */ public void setRTS_On(boolean rts_on) { try { EnumSet controlLines = usbSerialPort.getSupportedControlLines(); if (controlLines.contains(UsbSerialPort.ControlLine.RTS)) { usbSerialPort.setRTS(rts_on); } } catch (IOException e) { e.printStackTrace(); } } public void setDTR_On(boolean dtr_on) { try { EnumSet controlLines = usbSerialPort.getSupportedControlLines(); if (controlLines.contains(UsbSerialPort.ControlLine.DTR)) { usbSerialPort.setDTR(dtr_on); } } catch (IOException e) { e.printStackTrace(); Log.d(TAG, "setDTR_On: " + e.getMessage()); } } public OnConnectorStateChanged getOnStateChanged() { return onStateChanged; } public void setOnStateChanged(OnConnectorStateChanged onStateChanged) { this.onStateChanged = onStateChanged; } public int getVendorId() { return vendorId; } public void setVendorId(int deviceId) { this.vendorId = deviceId; } public int getPortNum() { return portNum; } public void setPortNum(int portNum) { this.portNum = portNum; } public int getBaudRate() { return baudRate; } public void setBaudRate(int baudRate) { this.baudRate = baudRate; } /** * 获取本机可用的串口设备串列表 * * @param context context * @return 串口设备列表 */ public static ArrayList listSerialPorts(Context context) { ArrayList serialPorts = new ArrayList<>(); UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); for (UsbDevice device : usbManager.getDeviceList().values()) { UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(device); if (driver == null) { continue; //试着把未知的设备加入到cdc驱动上 //driver = new CdcAcmSerialDriver(device); } for (int i = 0; i < driver.getPorts().size(); i++) { serialPorts.add(new SerialPort(device.getDeviceId(), device.getVendorId() , device.getProductId(), i)); } } return serialPorts; } public boolean isConnected() { return connected; } public static class SerialPort { public int deviceId = 0; public int vendorId = 0x0c26;//厂商号 public int productId = 0;//设备号 public int portNum = 0;//端口号 public SerialPort(int deviceId, int vendorId, int productId, int portNum) { this.deviceId = deviceId; this.vendorId = vendorId; this.productId = productId; this.portNum = portNum; } @SuppressLint("DefaultLocale") @Override public String toString() { return String.format("SerialPort:deviceId=0x%04X, vendorId=0x%04X, portNum=%d" , deviceId, vendorId, portNum); } @SuppressLint("DefaultLocale") public String information() { return String.format("\\0x%04X\\0x%04X\\0x%04X\\0x%d", deviceId, vendorId, productId, portNum); } } }