Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update UsbAPI.java #685

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 75 additions & 82 deletions app/src/main/java/com/termux/api/apis/UsbAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.os.Looper;
import android.util.JsonWriter;
import android.util.SparseArray;
Expand All @@ -19,74 +20,52 @@

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Iterator;

import androidx.annotation.NonNull;

public class UsbAPI {

private static SparseArray<UsbDeviceConnection> openDevices = new SparseArray<>();

private static final String LOG_TAG = "UsbAPI";
private static final String ACTION_USB_PERMISSION = TermuxConstants.TERMUX_API_PACKAGE_NAME + ".USB_PERMISSION";

public static void onReceive(final TermuxApiReceiver apiReceiver, final Context context, final Intent intent) {
Logger.logDebug(LOG_TAG, "onReceive");

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.sendFd(out, result);
}
} else out.append("No permission\n");
}
});

break;
default:
ResultReturner.returnData(apiReceiver, intent, out -> out.append("Invalid action\n"));
}
return;
}

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":
handlePermissionRequest(apiReceiver, context, intent);
break;
case "open":
handleOpenRequest(apiReceiver, context, intent);
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());
for (String deviceName : deviceList.keySet()) {
out.value(deviceName);
}
out.endArray();
}
Expand All @@ -102,63 +81,74 @@ private static UsbDevice getDevice(final TermuxApiReceiver apiReceiver, final Co
return device;
}

private static void handlePermissionRequest(final TermuxApiReceiver apiReceiver, final Context context, final Intent intent) {
UsbDevice device = getDevice(apiReceiver, context, intent);
if (device == null) return;

requestPermissionAsync(device, context, granted -> {
ResultReturner.returnData(apiReceiver, intent, out -> {
out.append(granted ? "yes\n" : "no\n");
});
});
}

private static void handleOpenRequest(final TermuxApiReceiver apiReceiver, final Context context, final Intent intent) {
UsbDevice device = getDevice(apiReceiver, context, intent);
if (device == null) return;

requestPermissionAsync(device, context, granted -> {
ResultReturner.returnData(apiReceiver, intent, new ResultReturner.WithAncillaryFd() {
@Override
public void writeResult(PrintWriter out) {
if (granted) {
int result = open(device, context);
if (result < 0) {
out.append("Failed to open device\n");
} else {
this.sendFd(out, result);
}
} else {
out.append("No permission\n");
}
}
});
});
}

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];
private static void requestPermissionAsync(final @NonNull UsbDevice device, final Context context, final PermissionCallback callback) {
if (hasPermission(device, context)) {
callback.onPermissionResult(true);
return;
}

final String ACTION_USB_PERMISSION = TermuxConstants.TERMUX_API_PACKAGE_NAME + ".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();
}
}

if (ACTION_USB_PERMISSION.equals(usbIntent.getAction())) {
boolean granted = usbIntent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
Logger.logDebug(LOG_TAG, "Permission result: " + granted);
callback.onPermissionResult(granted);
usbContext.unregisterReceiver(this);
}
}
};

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);
PendingIntent permissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
context.getApplicationContext().registerReceiver(usbReceiver, new IntentFilter(ACTION_USB_PERMISSION));
Logger.logDebug(LOG_TAG, "Requesting permission for device: " + device.getDeviceName());
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;
if (connection == null) return -2;
int fd = connection.getFileDescriptor();
if (fd == -1) {
connection.close();
Expand All @@ -168,4 +158,7 @@ private static int open(final @NonNull UsbDevice device, final Context context)
return fd;
}

private interface PermissionCallback {
void onPermissionResult(boolean granted);
}
}