Guest User

Untitled

a guest
Jul 26th, 2025
20
0
29 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 10.77 KB | None | 0 0
  1. package org.mrmalinka.usbapp;
  2.  
  3. import android.annotation.SuppressLint;
  4. import android.app.PendingIntent;
  5. import android.content.BroadcastReceiver;
  6. import android.content.Context;
  7. import android.content.Intent;
  8. import android.content.IntentFilter;
  9. import android.hardware.usb.UsbDevice;
  10. import android.hardware.usb.UsbManager;
  11. import android.os.Bundle;
  12. import android.util.Log;
  13. import android.webkit.JavascriptInterface;
  14. import android.webkit.WebSettings;
  15. import android.webkit.WebView;
  16. import android.widget.Toast;
  17. import android.hardware.usb.UsbDeviceConnection;
  18.  
  19. import com.hoho.android.usbserial.driver.UsbSerialDriver;
  20. import com.hoho.android.usbserial.driver.UsbSerialPort;
  21.  
  22. import androidx.appcompat.app.AppCompatActivity;
  23.  
  24. import org.json.JSONObject;
  25.  
  26. import java.io.ByteArrayOutputStream;
  27. import java.io.IOException;
  28. import java.util.Arrays;
  29. import java.util.Base64;
  30.  
  31. public class MainActivity extends AppCompatActivity {
  32.     private enum UsbPermission { Unknown, Requested, Granted, Denied }
  33.  
  34.     private static final String TAG = "UsbApp";
  35.     private static final String ACTION_USB_PERMISSION = "org.mrmalinka.usbapp.USB_PERMISSION";
  36.     private static final boolean ENABLE_DEBUG_LOGGING = true;
  37.  
  38.     private static final int MAX_PACKET_SIZE = 1024; // bytes
  39.  
  40.     private final BroadcastReceiver broadcastReceiver;
  41.  
  42.     private WebView webView;
  43.     private UsbSerialPort usbSerialPort;
  44.     private UsbPermission usbPermission = UsbPermission.Unknown;
  45.  
  46.     public MainActivity() {
  47.         broadcastReceiver = new BroadcastReceiver() {
  48.             @Override
  49.             public void onReceive(Context context, Intent intent) {
  50.                 String action = intent.getAction();
  51.                 internalLog("Broadcast: " + action, false);
  52.  
  53.                 if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
  54.                     setUsbStatus("Attached");
  55.                     try {
  56.                         connect();
  57.                     } catch (Exception e) {
  58.                         internalLog("Connect exception: " + e, true);
  59.                         setUsbStatus(e.toString());
  60.                     }
  61.                 }
  62.                 if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
  63.                     internalLog("Detached", true);
  64.                     setUsbStatus("Detached");
  65.                     disconnect();
  66.                 }
  67.                 if (ACTION_USB_PERMISSION.equals(action)) {
  68.                     if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
  69.                         usbPermission = UsbPermission.Granted;
  70.                         internalLog("Granted", true);
  71.                     } else {
  72.                         usbPermission = UsbPermission.Denied;
  73.                         internalLog("Denied", true);
  74.                     }
  75.                     try {
  76.                         setUsbStatus("Trying");
  77.                         connect();
  78.                     } catch (Exception e) {
  79.                         internalLog("Connect exception: " + e, true);
  80.                         setUsbStatus(e.toString());
  81.                     }
  82.                 }
  83.             }
  84.         };
  85.     }
  86.  
  87.     private void connect() {
  88.         UsbDevice device = null;
  89.         UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
  90.         for (UsbDevice v : usbManager.getDeviceList().values()) {
  91.             if (v.getVendorId() == 0x2E8A) {
  92.                 device = v;
  93.             }
  94.         }
  95.         if (device == null) {
  96.             internalLog("Device not found", true);
  97.             return;
  98.         }
  99.  
  100.         // get driver
  101.         UsbSerialDriver driver = CustomProber.getCustomProber().probeDevice(device);
  102.         if (driver == null) {
  103.             internalLog("No driver", true);
  104.             return;
  105.         }
  106.  
  107.  
  108.         internalLog(driver.getPorts().size() + " ports", false);
  109.         usbSerialPort = driver.getPorts().get(0);
  110.         UsbDeviceConnection usbConnection = usbManager.openDevice(driver.getDevice());
  111.  
  112.         if (usbConnection == null && usbPermission == UsbPermission.Unknown && !usbManager.hasPermission(driver.getDevice())) {
  113.             // ask for permission
  114.             usbPermission = UsbPermission.Requested;
  115.  
  116.             Intent intent = new Intent(ACTION_USB_PERMISSION);
  117.             intent.setPackage(getPackageName());
  118.             PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(
  119.                    this,
  120.                    0,
  121.                     intent,
  122.                     PendingIntent.FLAG_MUTABLE
  123.             );
  124.  
  125.             internalLog("Permission requested", false);
  126.             usbManager.requestPermission(driver.getDevice(), usbPermissionIntent);
  127.             return;
  128.         }
  129.         if (usbConnection == null) {
  130.             if (!usbManager.hasPermission(driver.getDevice()))
  131.                 internalLog("No permission & connection", true);
  132.             else
  133.                 internalLog("No connection", true);
  134.             return;
  135.         }
  136.  
  137.         StringBuilder logHist = new StringBuilder();
  138.         for (UsbSerialPort port : driver.getPorts()) {
  139.             try {
  140.                 port.open(usbConnection);
  141.                 port.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE);
  142.                 usbSerialPort = port;
  143.                 internalLog("Connected to port", false);
  144.                 setUsbStatus("Active");
  145.                 return;
  146.             } catch (Exception e) {
  147.                 logHist.append("|Fail: ").append(e);
  148.                 try { if (port.isOpen()) port.close(); } catch (Exception ignored) {}
  149.             }
  150.         }
  151.  
  152.         try { usbConnection.close(); } catch (Exception ignored) {}
  153.         usbSerialPort = null;
  154.         internalLog(logHist.toString(), true);
  155.     }
  156.     private void disconnect() {
  157.         try {
  158.             usbSerialPort.close();
  159.         } catch (IOException e) {
  160.             internalLog("Exception closing port: " + e, true);
  161.         }
  162.         usbSerialPort = null;
  163.     }
  164.  
  165.     private void write(byte[] data) {
  166.         if (usbSerialPort == null) {
  167.             internalLog("Not connected", true);
  168.             return;
  169.         }
  170.         try {
  171.             // constant timeout of 1 second
  172.             usbSerialPort.write(data, 1000);
  173.         } catch (Exception e) {
  174.             internalLog("Write exception: " + e, true);
  175.         }
  176.     }
  177.  
  178.     private byte[] read(int timeoutPer) throws IOException {
  179.         if (usbSerialPort == null || !usbSerialPort.isOpen()) {
  180.             throw new IOException("Not connected");
  181.         }
  182.  
  183.         ByteArrayOutputStream baos = new ByteArrayOutputStream();
  184.         boolean lenByteRead = false;
  185.         int expectedTotalLen = -1;
  186.  
  187.         do {
  188.             byte[] packet = new byte[MAX_PACKET_SIZE];
  189.             int readLen = usbSerialPort.read(packet, timeoutPer);
  190.             internalLog("Read: " + readLen + "| " + usbSerialPort.getDevice().getProductName(), true);
  191.             if (readLen < 0) {
  192.                 throw new IOException("Error reading from USB port");
  193.             }
  194.             if (readLen == 0) {
  195.                 throw new IOException("Read timed out");
  196.             }
  197.  
  198.             baos.write(packet, 0, readLen);
  199.  
  200.             if (!lenByteRead) {
  201.                 expectedTotalLen = (packet[0] & 0xFF);
  202.                 lenByteRead = true;
  203.             }
  204.         } while (baos.size() < expectedTotalLen);
  205.  
  206.         byte[] full = baos.toByteArray();
  207.         internalLog("Data: " + Arrays.toString(full), true);
  208.         if (full.length > expectedTotalLen)
  209.             return Arrays.copyOf(full, expectedTotalLen);
  210.         else return full;
  211.     }
  212.  
  213.     @Override
  214.     protected void onCreate(Bundle savedInstanceState) {
  215.         super.onCreate(savedInstanceState);
  216.         setupUsb();
  217.         setupWebView();
  218.         webView.loadUrl("file:///android_asset/index.html");
  219.  
  220.         internalLog("onCreate completed", false);
  221.     }
  222.     @Override
  223.     protected void onDestroy() {
  224.         super.onDestroy();
  225.         unregisterReceiver(broadcastReceiver);
  226.     }
  227.  
  228.     private void setupUsb() {
  229.         internalLog("Setting up usb", false);
  230.  
  231.         IntentFilter filter = new IntentFilter();
  232.         filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
  233.         filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
  234.         filter.addAction(ACTION_USB_PERMISSION);
  235.         registerReceiver(broadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
  236.     }
  237.     @SuppressLint("SetJavaScriptEnabled")
  238.     private void setupWebView() {
  239.         internalLog("Setting up WebView", false);
  240.  
  241.         webView = new WebView(this);
  242.         setContentView(webView);
  243.  
  244.         WebSettings webSettings = webView.getSettings();
  245.         webSettings.setJavaScriptEnabled(true);
  246.         webSettings.setDomStorageEnabled(true);
  247.         webSettings.setAllowFileAccess(true);
  248.         webSettings.setAllowContentAccess(true);
  249.  
  250.         webView.addJavascriptInterface(new AndroidInterface(), "Android");
  251.  
  252.         internalLog("WebView setup completed", false);
  253.     }
  254.  
  255.     private void internalLog(String message, boolean important) {
  256.         if (important)
  257.             runOnUiThread(() -> Toast.makeText(this, message, Toast.LENGTH_SHORT).show());
  258.         if (ENABLE_DEBUG_LOGGING)
  259.             Log.d(TAG, message);
  260.     }
  261.     private void setUsbStatus(String status) {
  262.         if (webView != null)
  263.             runOnUiThread(() -> webView.evaluateJavascript(
  264.                     "javascript:window.updateUsbStatusText(" +  JSONObject.quote(status) + ")",
  265.                     null
  266.             ));
  267.     }
  268.  
  269.     public class AndroidInterface {
  270.         @JavascriptInterface
  271.         public void loadAssetToWebView(String path) {
  272.             internalLog("Loading asset: " + path, false);
  273.             runOnUiThread(() -> webView.loadUrl("file:///android_asset/" + path));
  274.         }
  275.  
  276.         @JavascriptInterface
  277.         public void internalLogJS(String message) {
  278.             internalLog(message, true);
  279.         }
  280.  
  281.         @JavascriptInterface
  282.         public String usbRead(int timeoutPer) {
  283.             try {
  284.                 byte[] data = read(timeoutPer);
  285.                 return Base64.getEncoder().encodeToString(data);
  286.             } catch (Exception e) {
  287.                 internalLog("usbRead exception: " + e, true);
  288.                 // im not sure if we can actually return errors to js so
  289.                 // a character that never appears in base64 strings is fine
  290.                 return "!";
  291.             }
  292.         }
  293.  
  294.         @JavascriptInterface
  295.         public void usbWrite(String base64) {
  296.             write(Base64.getDecoder().decode(base64));
  297.         }
  298.  
  299.         @JavascriptInterface
  300.         public boolean isConnected() {
  301.             if (usbSerialPort != null)
  302.                 return usbSerialPort.isOpen();
  303.             else return false;
  304.         }
  305.     }
  306. }
Add Comment
Please, Sign In to add comment