1
0
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:
Alessandro Caputo 2019-10-06 16:41:18 +02:00 committed by GitHub
commit 10320ac130
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 195 additions and 0 deletions

View File

@ -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. -->

View File

@ -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;

View 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;
}
}

View File

@ -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);