mirror of
https://github.com/danog/termux-api.git
synced 2024-11-30 04:19:20 +01:00
Merge pull request #294 from bndeff/usbapi
Add a USB API compatible with libusb
This commit is contained in:
commit
10320ac130
@ -34,6 +34,7 @@
|
||||
<uses-feature android:name="android.hardware.microphone" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.telephony" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.wifi" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
|
||||
|
||||
<!-- This permission is not used, but a permission is needed on the sharedfiles contentprovider,
|
||||
which will always use FLAG_GRANT_READ_URI_PERMISSION. -->
|
||||
|
@ -174,6 +174,9 @@ public class TermuxApiReceiver extends BroadcastReceiver {
|
||||
case "Torch":
|
||||
TorchAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "Usb":
|
||||
UsbAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
case "Vibrate":
|
||||
VibrateAPI.onReceive(this, context, intent);
|
||||
break;
|
||||
|
165
app/src/main/java/com/termux/api/UsbAPI.java
Normal file
165
app/src/main/java/com/termux/api/UsbAPI.java
Normal file
@ -0,0 +1,165 @@
|
||||
package com.termux.api;
|
||||
|
||||
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.Looper;
|
||||
import android.util.JsonWriter;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.termux.api.util.ResultReturner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class UsbAPI {
|
||||
|
||||
private static SparseArray<UsbDeviceConnection> openDevices = new SparseArray<>();
|
||||
|
||||
static void onReceive(final TermuxApiReceiver apiReceiver, final Context context, final Intent intent) {
|
||||
UsbDevice device;
|
||||
String action = intent.getAction();
|
||||
if (action == null) {
|
||||
ResultReturner.returnData(apiReceiver, intent, out -> out.append("Missing action\n"));
|
||||
} else {
|
||||
switch (action) {
|
||||
case "list":
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultReturner.ResultJsonWriter() {
|
||||
@Override
|
||||
public void writeJson(JsonWriter out) throws Exception {
|
||||
listDevices(context, out);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "permission":
|
||||
device = getDevice(apiReceiver, context, intent);
|
||||
if (device == null) return;
|
||||
ResultReturner.returnData(apiReceiver, intent, out -> {
|
||||
boolean result = getPermission(device, context, intent);
|
||||
out.append(result ? "yes\n" : "no\n");
|
||||
});
|
||||
break;
|
||||
case "open":
|
||||
device = getDevice(apiReceiver, context, intent);
|
||||
if (device == null) return;
|
||||
ResultReturner.returnData(apiReceiver, intent, new ResultReturner.WithAncillaryFd() {
|
||||
@Override
|
||||
public void writeResult(PrintWriter out) {
|
||||
if (getPermission(device, context, intent)) {
|
||||
int result = open(device, context);
|
||||
if (result < 0) {
|
||||
out.append("Failed to open device\n");
|
||||
} else {
|
||||
this.setFd(result);
|
||||
out.append("@"); // has to be non-empty
|
||||
}
|
||||
} else out.append("No permission\n");
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
default:
|
||||
ResultReturner.returnData(apiReceiver, intent, out -> out.append("Invalid action\n"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void listDevices(final Context context, JsonWriter out) throws IOException {
|
||||
final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
|
||||
Iterator<String> deviceIterator = deviceList.keySet().iterator();
|
||||
out.beginArray();
|
||||
while (deviceIterator.hasNext()) {
|
||||
out.value(deviceIterator.next());
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
|
||||
private static UsbDevice getDevice(final TermuxApiReceiver apiReceiver, final Context context, final Intent intent) {
|
||||
String deviceName = intent.getStringExtra("device");
|
||||
final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
|
||||
UsbDevice device = deviceList.get(deviceName);
|
||||
if (device == null) {
|
||||
ResultReturner.returnData(apiReceiver, intent, out -> out.append("No such device\n"));
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
private static boolean hasPermission(final @NonNull UsbDevice device, final Context context) {
|
||||
final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||
return usbManager.hasPermission(device);
|
||||
}
|
||||
|
||||
private static boolean requestPermission(final @NonNull UsbDevice device, final Context context) {
|
||||
Looper.prepare();
|
||||
Looper looper = Looper.myLooper();
|
||||
final boolean[] result = new boolean[1];
|
||||
|
||||
final String ACTION_USB_PERMISSION = "com.termux.api.USB_PERMISSION";
|
||||
final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context usbContext, final Intent usbIntent) {
|
||||
String action = usbIntent.getAction();
|
||||
if (ACTION_USB_PERMISSION.equals(action)) {
|
||||
synchronized (this) {
|
||||
UsbDevice device = usbIntent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
if (usbIntent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
|
||||
if (device != null) {
|
||||
result[0] = true;
|
||||
if (looper != null) looper.quit();
|
||||
}
|
||||
} else {
|
||||
result[0] = false;
|
||||
if (looper != null) looper.quit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||
PendingIntent permissionIntent = PendingIntent.getBroadcast(context, 0,
|
||||
new Intent(ACTION_USB_PERMISSION), 0);
|
||||
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
|
||||
context.getApplicationContext().registerReceiver(usbReceiver, filter);
|
||||
usbManager.requestPermission(device, permissionIntent);
|
||||
Looper.loop();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private static boolean getPermission(final @NonNull UsbDevice device, final Context context, final Intent intent) {
|
||||
boolean request = intent.getBooleanExtra("request", false);
|
||||
if(request) {
|
||||
return requestPermission(device, context);
|
||||
} else {
|
||||
return hasPermission(device, context);
|
||||
}
|
||||
}
|
||||
|
||||
private static int open(final @NonNull UsbDevice device, final Context context) {
|
||||
final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||
UsbDeviceConnection connection = usbManager.openDevice(device);
|
||||
if (connection == null)
|
||||
return -2;
|
||||
int fd = connection.getFileDescriptor();
|
||||
if (fd == -1) {
|
||||
connection.close();
|
||||
return -1;
|
||||
}
|
||||
openDevices.put(fd, connection);
|
||||
return fd;
|
||||
}
|
||||
|
||||
}
|
@ -7,9 +7,11 @@ import android.content.BroadcastReceiver.PendingResult;
|
||||
import android.content.Intent;
|
||||
import android.net.LocalSocket;
|
||||
import android.net.LocalSocketAddress;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -66,6 +68,18 @@ public abstract class ResultReturner {
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class WithAncillaryFd implements ResultWriter {
|
||||
private int fd = -1;
|
||||
|
||||
public final void setFd(int newFd) {
|
||||
fd = newFd;
|
||||
}
|
||||
|
||||
public final int getFd() {
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class ResultJsonWriter implements ResultWriter {
|
||||
@Override
|
||||
public final void writeResult(PrintWriter out) throws Exception {
|
||||
@ -102,6 +116,7 @@ public abstract class ResultReturner {
|
||||
|
||||
final Runnable runnable = () -> {
|
||||
try {
|
||||
final ParcelFileDescriptor[] pfds = { null };
|
||||
try (LocalSocket outputSocket = new LocalSocket()) {
|
||||
String outputSocketAdress = intent.getStringExtra(SOCKET_OUTPUT_EXTRA);
|
||||
outputSocket.connect(new LocalSocketAddress(outputSocketAdress));
|
||||
@ -117,9 +132,20 @@ public abstract class ResultReturner {
|
||||
} else {
|
||||
resultWriter.writeResult(writer);
|
||||
}
|
||||
if(resultWriter instanceof WithAncillaryFd) {
|
||||
int fd = ((WithAncillaryFd) resultWriter).getFd();
|
||||
if (fd >= 0) {
|
||||
pfds[0] = ParcelFileDescriptor.adoptFd(fd);
|
||||
FileDescriptor[] fds = { pfds[0].getFileDescriptor() };
|
||||
outputSocket.setFileDescriptorsForSend(fds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pfds[0] != null) {
|
||||
pfds[0].close();
|
||||
}
|
||||
|
||||
if (asyncResult != null) {
|
||||
asyncResult.setResultCode(0);
|
||||
|
Loading…
Reference in New Issue
Block a user