package org.droidplanner.services.android.impl.communication.connection.usb;
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.UsbManager;
import android.os.Bundle;
import android.util.Log;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialProber;
import com.o3dr.services.android.lib.gcs.link.LinkConnectionStatus;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import timber.log.Timber;
class UsbCDCConnection extends UsbConnection.UsbConnectionImpl {
private static final String TAG = UsbCDCConnection.class.getSimpleName();
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
private static final IntentFilter intentFilter = new IntentFilter(ACTION_USB_PERMISSION);
private final AtomicReference<UsbSerialDriver> serialDriverRef = new AtomicReference<>();
private final PendingIntent usbPermissionIntent;
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
removeWatchdog();
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if (device != null) {
//call method to set up device communication
try {
openUsbDevice(device, extrasHolder.get());
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
} else {
LinkConnectionStatus connectionStatus = LinkConnectionStatus
.newFailedConnectionStatus(LinkConnectionStatus.LINK_UNAVAILABLE, "Unable to access usb device.");
onUsbConnectionStatus(connectionStatus);
}
} else {
Log.d(TAG, "permission denied for device " + device);
LinkConnectionStatus connectionStatus = LinkConnectionStatus
.newFailedConnectionStatus(LinkConnectionStatus.PERMISSION_DENIED, "USB Permission denied.");
onUsbConnectionStatus(connectionStatus);
}
}
}
};
private final Runnable permissionWatchdog = new Runnable() {
@Override
public void run() {
Log.d(TAG, "Permission request timeout.");
LinkConnectionStatus connectionStatus = LinkConnectionStatus
.newFailedConnectionStatus(LinkConnectionStatus.TIMEOUT, "Unable to get usb access.");
onUsbConnectionStatus(connectionStatus);
removeWatchdog();
}
};
private final AtomicReference<Bundle> extrasHolder = new AtomicReference<>();
private ScheduledExecutorService scheduler;
protected UsbCDCConnection(Context context, UsbConnection parentConn, int baudRate) {
super(context, parentConn, baudRate);
this.usbPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
}
private void registerUsbPermissionBroadcastReceiver() {
mContext.registerReceiver(broadcastReceiver, intentFilter);
}
private void unregisterUsbPermissionBroadcastReceiver() {
try {
mContext.unregisterReceiver(broadcastReceiver);
}catch(IllegalArgumentException e){
Timber.e(e, "Receiver was not registered.");
}
}
private void removeWatchdog() {
if (scheduler != null) {
scheduler.shutdown();
scheduler = null;
}
}
@Override
protected void openUsbConnection(Bundle extras) throws IOException {
extrasHolder.set(extras);
registerUsbPermissionBroadcastReceiver();
// Get UsbManager from Android.
UsbManager manager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
//Get the list of available devices
List<UsbDevice> availableDevices = UsbSerialProber.getAvailableSupportedDevices(manager);
if (availableDevices.isEmpty()) {
Log.d(TAG, "No Devices found");
throw new IOException("No Devices found");
}
//Pick the first device
UsbDevice device = availableDevices.get(0);
if (manager.hasPermission(device)) {
openUsbDevice(device, extras);
} else {
removeWatchdog();
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.schedule(permissionWatchdog, 15, TimeUnit.SECONDS);
Log.d(TAG, "Requesting permission to access usb device " + device.getDeviceName());
manager.requestPermission(device, usbPermissionIntent);
}
}
private void openUsbDevice(UsbDevice device, Bundle extras) throws IOException {
// Get UsbManager from Android.
UsbManager manager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
// Find the first available driver.
final UsbSerialDriver serialDriver = UsbSerialProber.openUsbDevice(manager, device);
if (serialDriver == null) {
Log.d(TAG, "No Devices found");
throw new IOException("No Devices found");
} else {
Log.d(TAG, "Opening using Baud rate " + mBaudRate);
try {
serialDriver.open();
serialDriver.setParameters(mBaudRate, 8, UsbSerialDriver.STOPBITS_1, UsbSerialDriver.PARITY_NONE);
serialDriverRef.set(serialDriver);
onUsbConnectionOpened(extras);
} catch (IOException e) {
Log.e(TAG, "Error setting up device: " + e.getMessage(), e);
try {
serialDriver.close();
} catch (IOException e2) {
// Ignore.
}
}
}
}
@Override
protected int readDataBlock(byte[] readData) throws IOException {
// Read data from driver. This call will return up to readData.length bytes.
// If no data is received it will timeout after 200ms (as set by parameter 2)
final UsbSerialDriver serialDriver = serialDriverRef.get();
if(serialDriver == null)
throw new IOException("Device is unavailable.");
int iavailable = 0;
try {
iavailable = serialDriver.read(readData, 200);
} catch (NullPointerException e) {
final String errorMsg = "Error Reading: " + e.getMessage()
+ "\nAssuming inaccessible USB device. Closing connection.";
Log.e(TAG, errorMsg, e);
throw new IOException(errorMsg, e);
}
if (iavailable == 0)
iavailable = -1;
return iavailable;
}
@Override
protected void sendBuffer(byte[] buffer) {
// Write data to driver. This call should write buffer.length bytes
// if data cant be sent , then it will timeout in 500ms (as set by
// parameter 2)
final UsbSerialDriver serialDriver = serialDriverRef.get();
if (serialDriver != null) {
try {
serialDriver.write(buffer, 500);
} catch (IOException e) {
Log.e(TAG, "Error Sending: " + e.getMessage(), e);
}
}
}
@Override
protected void closeUsbConnection() throws IOException {
unregisterUsbPermissionBroadcastReceiver();
final UsbSerialDriver serialDriver = serialDriverRef.getAndSet(null);
if (serialDriver != null) {
try {
serialDriver.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
}
}
@Override
public String toString() {
return TAG;
}
}