This lets AltosDroid use a USB-connected receiver as well as Bluetooth devices.
Signed-off-by: Keith Packard <keithp@keithp.com>
package="org.altusmetrum.AltosDroid"
android:versionCode="6"
android:versionName="1.5">
- <uses-sdk android:targetSdkVersion="10" android:minSdkVersion="10"/>
+ <uses-sdk android:targetSdkVersion="12" android:minSdkVersion="12"/>
<!-- Google Maps -->
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
android:protectionLevel="signature"/>
<uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/>
+ <!-- Permissions needed to access USB OTG -->
+ <uses-feature android:name="android.hardware.usb.host" />
<application android:label="@string/app_name"
android:icon="@drawable/app_icon"
- android:allowBackup="true" >
+ android:allowBackup="true"
+ android:theme="@style/CustomTheme">
<activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
android:label="@string/app_name"
- android:configChanges="orientation|keyboardHidden" >
+ android:configChanges="orientation|keyboardHidden"
+ android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
+ android:configChanges="orientation|keyboardHidden"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action
+ android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
+ </intent-filter>
+ <meta-data
+ android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+ android:resource="@xml/device_filter" />
+ </activity>
+
<activity android:name=".DeviceListActivity"
android:label="@string/select_device"
android:theme="@android:style/Theme.Dialog"
# project structure.
# Project target.
-target=android-10
+target=android-12
# project structure.
# Project target.
-target=android-10
+target=android-12
android.library.reference.1=google-play-services_lib/
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+ <style name="CustomTheme" parent="android:Theme">
+ <item name="android:windowNoTitle">false</item>
+ </style>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+ <usb-device vendor-id="65534" />
+ <usb-device vendor-id="1027" />
+</resources>
import org.altusmetrum.altoslib_6.*;
-public class AltosBluetooth extends AltosLink {
+public class AltosBluetooth extends AltosDroidLink {
// Debugging
private static final String TAG = "AltosBluetooth";
private static final boolean D = true;
private ConnectThread connect_thread = null;
- private Thread input_thread = null;
-
- private Handler handler;
private BluetoothAdapter adapter;
private BluetoothSocket socket;
// Constructor
public AltosBluetooth(BluetoothDevice device, Handler handler) {
+ super(handler);
// set_debug(D);
adapter = BluetoothAdapter.getDefaultAdapter();
this.handler = handler;
connect_thread.start();
}
- private Object closed_lock = new Object();
- private boolean closed = false;
-
- private boolean closed() {
- synchronized(closed_lock) {
- return closed;
- }
- }
-
- private void connected() {
+ void connected() {
if (closed()) {
if (D) Log.d(TAG, "connected after closed");
return;
if (socket != null) {
input = socket.getInputStream();
output = socket.getOutputStream();
-
- input_thread = new Thread(this);
- input_thread.start();
-
- // Configure the newly connected device for telemetry
- print("~\nE 0\n");
- set_monitor(false);
- if (D) Log.d(TAG, "ConnectThread: connected");
-
- /* Let TelemetryService know we're connected
- */
- handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
-
- /* Notify other waiting threads that we're connected now
- */
- notifyAll();
+ super.connected();
}
}
+ } catch (InterruptedException ie) {
+ connect_failed();
} catch (IOException io) {
connect_failed();
}
return;
}
- close_socket();
+ close_device();
input = null;
output = null;
handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget();
if (D) Log.e(TAG, "ConnectThread: Failed to establish connection");
}
- private void disconnected() {
- if (closed()) {
- if (D) Log.d(TAG, "disconnected after closed");
- return;
- }
-
- if (D) Log.d(TAG, "Sending disconnected message");
- handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
- }
-
- private void close_socket() {
+ void close_device() {
BluetoothSocket tmp_socket;
synchronized(this) {
}
}
+ public void close() {
+ super.close();
+ input = null;
+ output = null;
+ }
+
private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private void create_socket(BluetoothDevice device) {
}
if (socket != null) {
if (D) Log.d(TAG, String.format("Socket already allocated %s", socket.toString()));
- close_socket();
+ close_device();
}
synchronized (this) {
socket = tmp_socket;
}
}
- public double frequency() {
- return frequency;
- }
-
- public int telemetry_rate() {
- return telemetry_rate;
- }
-
- public void save_frequency() {
- AltosPreferences.set_frequency(0, frequency);
- }
-
- public void save_telemetry_rate() {
- AltosPreferences.set_telemetry_rate(0, telemetry_rate);
- }
-
private synchronized void wait_connected() throws InterruptedException, IOException {
if (input == null && socket != null) {
if (D) Log.d(TAG, "wait_connected...");
throw new IOException();
}
- public void print(String data) {
- byte[] bytes = data.getBytes();
- if (D) Log.d(TAG, "print(): begin");
+ int write(byte[] buffer, int len) {
try {
- wait_connected();
- output.write(bytes);
- if (D) Log.d(TAG, "print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");
- } catch (IOException e) {
- disconnected();
- } catch (InterruptedException e) {
- disconnected();
+ output.write(buffer, 0, len);
+ } catch (IOException ie) {
+ return -1;
}
+ return len;
}
- public void putchar(byte c) {
- byte[] bytes = { c };
- if (D) Log.d(TAG, "print(): begin");
+ int read(byte[] buffer, int len) {
try {
- wait_connected();
- output.write(bytes);
- if (D) Log.d(TAG, "print(): Wrote byte: '" + c + "'");
- } catch (IOException e) {
- disconnected();
- } catch (InterruptedException e) {
- disconnected();
- }
- }
-
- private static final int buffer_size = 1024;
-
- private byte[] buffer = new byte[buffer_size];
- private int buffer_len = 0;
- private int buffer_off = 0;
-
- private byte[] debug_chars = new byte[buffer_size];
- private int debug_off;
-
- private void debug_input(byte b) {
- if (b == '\n') {
- Log.d(TAG, " " + new String(debug_chars, 0, debug_off));
- debug_off = 0;
- } else {
- if (debug_off < buffer_size)
- debug_chars[debug_off++] = b;
+ return input.read(buffer, 0, len);
+ } catch (IOException ie) {
+ return -1;
}
}
- public int getchar() {
- while (buffer_off == buffer_len) {
- try {
- wait_connected();
- buffer_len = input.read(buffer);
- buffer_off = 0;
- } catch (IOException e) {
- if (D) Log.d(TAG, "getchar IOException");
- disconnected();
- return AltosLink.ERROR;
- } catch (java.lang.InterruptedException e) {
- if (D) Log.d(TAG, "getchar Interrupted");
- disconnected();
- return AltosLink.ERROR;
- }
- }
- if (D)
- debug_input(buffer[buffer_off]);
- return buffer[buffer_off++];
- }
-
- public void closing() {
- synchronized(closed_lock) {
- if (D) Log.d(TAG, "Marked closed true");
- closed = true;
- }
- }
-
-
- public void close() {
- if (D) Log.d(TAG, "close(): begin");
-
- closing();
-
- close_socket();
-
- synchronized(this) {
-
- if (input_thread != null) {
- if (D) Log.d(TAG, "close(): stopping input_thread");
- try {
- if (D) Log.d(TAG, "close(): input_thread.interrupt().....");
- input_thread.interrupt();
- if (D) Log.d(TAG, "close(): input_thread.join().....");
- input_thread.join();
- } catch (Exception e) {}
- input_thread = null;
- }
- input = null;
- output = null;
- notifyAll();
- }
- }
-
- //public void flush_output() { super.flush_output(); }
-
// Stubs of required methods when extending AltosLink
public boolean can_cancel_reply() { return false; }
public boolean show_reply_timeout() { return true; }
import java.util.TimerTask;
import android.app.Activity;
+import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.widget.Toast;
import android.app.AlertDialog;
import android.location.Location;
+import android.hardware.usb.*;
import org.altusmetrum.altoslib_6.*;
static final String TAG = "AltosDroid";
static final boolean D = true;
+ // Actions sent to the telemetry server at startup time
+
+ public static final String ACTION_BLUETOOTH = "org.altusmetrum.AltosDroid.BLUETOOTH";
+ public static final String ACTION_USB = "org.altusmetrum.AltosDroid.USB";
+
// Message types received by our Handler
public static final int MSG_STATE = 1;
private Timer timer;
AltosState saved_state;
+ UsbDevice pending_usb_device;
+ boolean start_with_usb;
+
// Service
private boolean mIsBound = false;
private Messenger mService = null;
} catch (RemoteException e) {
// In this case the service has crashed before we could even do anything with it
}
+ if (pending_usb_device != null) {
+ try {
+ mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, pending_usb_device));
+ pending_usb_device = null;
+ } catch (RemoteException e) {
+ }
+ }
}
public void onServiceDisconnected(ComponentName className) {
super.onCreate(savedInstanceState);
if(D) Log.e(TAG, "+++ ON CREATE +++");
- // Get local Bluetooth adapter
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-
- // If the adapter is null, then Bluetooth is not supported
- if (mBluetoothAdapter == null) {
- Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
- finish();
- }
-
fm = getSupportFragmentManager();
// Set up the window layout
mAgeView = (TextView) findViewById(R.id.age_value);
}
+ private boolean ensureBluetooth() {
+ // Get local Bluetooth adapter
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ // If the adapter is null, then Bluetooth is not supported
+ if (mBluetoothAdapter == null) {
+ Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ if (!mBluetoothAdapter.isEnabled()) {
+ Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT);
+ }
+
+ return true;
+ }
+
+ private boolean check_usb() {
+ UsbDevice device = AltosUsb.find_device(this, AltosLib.product_basestation);
+
+ if (device != null) {
+ Intent i = new Intent(this, AltosDroid.class);
+ PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent("hello world", null, this, AltosDroid.class), 0);
+
+ if (AltosUsb.request_permission(this, device, pi)) {
+ connectUsb(device);
+ }
+ start_with_usb = true;
+ return true;
+ }
+
+ start_with_usb = false;
+
+ return false;
+ }
+
+ private void noticeIntent(Intent intent) {
+
+ /* Ok, this is pretty convenient.
+ *
+ * When a USB device is plugged in, and our 'hotplug'
+ * intent registration fires, we get an Intent with
+ * EXTRA_DEVICE set.
+ *
+ * When we start up and see a usb device and request
+ * permission to access it, that queues a
+ * PendingIntent, which has the EXTRA_DEVICE added in,
+ * along with the EXTRA_PERMISSION_GRANTED field as
+ * well.
+ *
+ * So, in both cases, we get the device name using the
+ * same call. We check to see if access was granted,
+ * in which case we ignore the device field and do our
+ * usual startup thing.
+ */
+
+ UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+
+ if (D) Log.e(TAG, "intent " + intent + " device " + device + " granted " + granted);
+
+ if (!granted)
+ device = null;
+
+ if (device != null) {
+ if (D) Log.d(TAG, "intent has usb device " + device.toString());
+ connectUsb(device);
+ } else {
+
+ /* 'granted' is only false if this intent came
+ * from the request_permission call and
+ * permission was denied. In which case, we
+ * don't want to loop forever...
+ */
+ if (granted) {
+ if (D) Log.d(TAG, "check for a USB device at startup");
+ if (check_usb())
+ return;
+ }
+ if (D) Log.d(TAG, "Starting by looking for bluetooth devices");
+ if (ensureBluetooth())
+ return;
+ finish();
+ }
+ }
+
@Override
public void onStart() {
super.onStart();
if(D) Log.e(TAG, "++ ON START ++");
+ noticeIntent(getIntent());
+
// Start Telemetry Service
- startService(new Intent(AltosDroid.this, TelemetryService.class));
+ String action = start_with_usb ? ACTION_USB : ACTION_BLUETOOTH;
- if (!mBluetoothAdapter.isEnabled()) {
- Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
- startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT);
- }
+ startService(new Intent(action, null, AltosDroid.this, TelemetryService.class));
doBindService();
if (mAltosVoice == null)
mAltosVoice = new AltosVoice(this);
+
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ if(D) Log.d(TAG, "onNewIntent");
+ noticeIntent(intent);
}
@Override
stop_timer();
}
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(D) Log.d(TAG, "onActivityResult " + resultCode);
switch (requestCode) {
case REQUEST_CONNECT_DEVICE:
}
}
+ private void connectUsb(UsbDevice device) {
+ if (mService == null)
+ pending_usb_device = device;
+ else {
+ // Attempt to connect to the device
+ try {
+ mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, device));
+ if (D) Log.d(TAG, "Sent OPEN_USB message");
+ } catch (RemoteException e) {
+ if (D) Log.e(TAG, "connect device message failed");
+ }
+ }
+ }
+
private void connectDevice(Intent data) {
// Attempt to connect to the device
try {
Intent serverIntent = null;
switch (item.getItemId()) {
case R.id.connect_scan:
- // Launch the DeviceListActivity to see devices and do scan
- serverIntent = new Intent(this, DeviceListActivity.class);
- startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
+ if (ensureBluetooth()) {
+ // Launch the DeviceListActivity to see devices and do scan
+ serverIntent = new Intent(this, DeviceListActivity.class);
+ startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
+ }
return true;
case R.id.disconnect:
- /* Disconnect the bluetooth device
+ /* Disconnect the device
*/
disconnectDevice();
return true;
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+
+import android.os.Handler;
+import android.util.Log;
+
+import org.altusmetrum.altoslib_6.*;
+
+public abstract class AltosDroidLink extends AltosLink {
+
+ // Debugging
+ private static final String TAG = "AltosDroidLink";
+ private static final boolean D = true;
+
+ Handler handler;
+
+ Thread input_thread = null;
+
+ public double frequency() {
+ return frequency;
+ }
+
+ public int telemetry_rate() {
+ return telemetry_rate;
+ }
+
+ public void save_frequency() {
+ AltosPreferences.set_frequency(0, frequency);
+ }
+
+ public void save_telemetry_rate() {
+ AltosPreferences.set_telemetry_rate(0, telemetry_rate);
+ }
+
+ Object closed_lock = new Object();
+ boolean closing = false;
+ boolean closed = false;
+
+ public boolean closed() {
+ synchronized(closed_lock) {
+ return closing;
+ }
+ }
+
+ void connected() throws InterruptedException {
+ input_thread = new Thread(this);
+ input_thread.start();
+
+ // Configure the newly connected device for telemetry
+ print("~\nE 0\n");
+ set_monitor(false);
+ if (D) Log.d(TAG, "ConnectThread: connected");
+
+ /* Let TelemetryService know we're connected
+ */
+ handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
+
+ /* Notify other waiting threads that we're connected now
+ */
+ notifyAll();
+ }
+
+ public void closing() {
+ synchronized(closed_lock) {
+ if (D) Log.d(TAG, "Marked closing true");
+ closing = true;
+ }
+ }
+
+ private boolean actually_closed() {
+ synchronized(closed_lock) {
+ return closed;
+ }
+ }
+
+ abstract void close_device();
+
+ public void close() {
+ if (D) Log.d(TAG, "close(): begin");
+
+ closing();
+
+ flush_output();
+
+ synchronized (closed_lock) {
+ if (D) Log.d(TAG, "Marked closed true");
+ closed = true;
+ }
+
+ close_device();
+
+ synchronized(this) {
+
+ if (input_thread != null) {
+ if (D) Log.d(TAG, "close(): stopping input_thread");
+ try {
+ if (D) Log.d(TAG, "close(): input_thread.interrupt().....");
+ input_thread.interrupt();
+ if (D) Log.d(TAG, "close(): input_thread.join().....");
+ input_thread.join();
+ } catch (Exception e) {}
+ input_thread = null;
+ }
+ notifyAll();
+ }
+ }
+
+ abstract int write(byte[] buffer, int len);
+
+ abstract int read(byte[] buffer, int len);
+
+ private static final int buffer_size = 64;
+
+ private byte[] in_buffer = new byte[buffer_size];
+ private byte[] out_buffer = new byte[buffer_size];
+ private int buffer_len = 0;
+ private int buffer_off = 0;
+ private int out_buffer_off = 0;
+
+ private byte[] debug_chars = new byte[buffer_size];
+ private int debug_off;
+
+ private void debug_input(byte b) {
+ if (b == '\n') {
+ Log.d(TAG, " " + new String(debug_chars, 0, debug_off));
+ debug_off = 0;
+ } else {
+ if (debug_off < buffer_size)
+ debug_chars[debug_off++] = b;
+ }
+ }
+
+ private void disconnected() {
+ if (closed()) {
+ if (D) Log.d(TAG, "disconnected after closed");
+ return;
+ }
+
+ if (D) Log.d(TAG, "Sending disconnected message");
+ handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
+ }
+
+ public int getchar() {
+
+ if (actually_closed())
+ return ERROR;
+
+ while (buffer_off == buffer_len) {
+ buffer_len = read(in_buffer, buffer_size);
+ if (buffer_len < 0) {
+ Log.d(TAG, "ERROR returned from getchar()");
+ disconnected();
+ return ERROR;
+ }
+ buffer_off = 0;
+ }
+ if (D)
+ debug_input(in_buffer[buffer_off]);
+ return in_buffer[buffer_off++];
+ }
+
+ public void flush_output() {
+ super.flush_output();
+
+ if (actually_closed()) {
+ out_buffer_off = 0;
+ return;
+ }
+
+ while (out_buffer_off != 0) {
+ int sent = write(out_buffer, out_buffer_off);
+
+ if (sent <= 0) {
+ Log.d(TAG, "flush_output() failed");
+ out_buffer_off = 0;
+ break;
+ }
+
+ if (sent < out_buffer_off)
+ System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent);
+
+ out_buffer_off -= sent;
+ }
+ }
+
+ public void putchar(byte c) {
+ out_buffer[out_buffer_off++] = c;
+ if (out_buffer_off == buffer_size)
+ flush_output();
+ }
+
+ public void print(String data) {
+ byte[] bytes = data.getBytes();
+ if (D) Log.d(TAG, "print(): begin");
+ for (byte b : bytes)
+ putchar(b);
+ if (D) Log.d(TAG, "print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");
+ }
+
+ public AltosDroidLink(Handler handler) {
+ this.handler = handler;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+import java.util.HashMap;
+
+import android.content.Context;
+import android.hardware.usb.*;
+import android.app.*;
+import android.os.Handler;
+import android.util.Log;
+
+import org.altusmetrum.altoslib_6.*;
+
+public class AltosUsb extends AltosDroidLink {
+
+ // Debugging
+ private static final String TAG = "AltosUsb";
+ private static final boolean D = true;
+
+ private Thread input_thread = null;
+
+ private Handler handler;
+
+ private UsbManager manager;
+ private UsbDevice device;
+ private UsbDeviceConnection connection;
+ private UsbInterface iface;
+ private UsbEndpoint in, out;
+
+ private InputStream input;
+ private OutputStream output;
+
+ // Constructor
+ public AltosUsb(Context context, UsbDevice device, Handler handler) {
+ super(handler);
+// set_debug(D);
+ this.handler = handler;
+
+ iface = null;
+ in = null;
+ out = null;
+
+ int niface = device.getInterfaceCount();
+
+ for (int i = 0; i < niface; i++) {
+
+ iface = device.getInterface(i);
+
+ in = null;
+ out = null;
+
+ int nendpoints = iface.getEndpointCount();
+
+ for (int e = 0; e < nendpoints; e++) {
+ UsbEndpoint endpoint = iface.getEndpoint(e);
+
+ if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
+ switch (endpoint.getDirection()) {
+ case UsbConstants.USB_DIR_OUT:
+ out = endpoint;
+ break;
+ case UsbConstants.USB_DIR_IN:
+ in = endpoint;
+ break;
+ }
+ }
+ }
+
+ if (in != null && out != null)
+ break;
+ }
+
+ if (in != null && out != null) {
+ Log.d(TAG, String.format("\tin %s out %s\n", in.toString(), out.toString()));
+
+ manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+ if (manager == null) {
+ Log.d(TAG, "USB_SERVICE failed");
+ return;
+ }
+
+ connection = manager.openDevice(device);
+
+ if (connection == null) {
+ Log.d(TAG, "openDevice failed");
+ return;
+ }
+
+ connection.claimInterface(iface, true);
+
+ input_thread = new Thread(this);
+ input_thread.start();
+
+ // Configure the newly connected device for telemetry
+ print("~\nE 0\n");
+ set_monitor(false);
+ }
+ }
+
+ static private boolean isAltusMetrum(UsbDevice device) {
+ if (device.getVendorId() != AltosLib.vendor_altusmetrum)
+ return false;
+ if (device.getProductId() < AltosLib.product_altusmetrum_min)
+ return false;
+ if (device.getProductId() > AltosLib.product_altusmetrum_max)
+ return false;
+ return true;
+ }
+
+ static boolean matchProduct(int want_product, UsbDevice device) {
+
+ if (!isAltusMetrum(device))
+ return false;
+
+ if (want_product == AltosLib.product_any)
+ return true;
+
+ int have_product = device.getProductId();
+
+ if (want_product == AltosLib.product_basestation)
+ return have_product == AltosLib.product_teledongle ||
+ have_product == AltosLib.product_teleterra ||
+ have_product == AltosLib.product_telebt ||
+ have_product == AltosLib.product_megadongle;
+
+ if (want_product == AltosLib.product_altimeter)
+ return have_product == AltosLib.product_telemetrum ||
+ have_product == AltosLib.product_telemega ||
+ have_product == AltosLib.product_easymega ||
+ have_product == AltosLib.product_telegps ||
+ have_product == AltosLib.product_easymini ||
+ have_product == AltosLib.product_telemini;
+
+ if (have_product == AltosLib.product_altusmetrum) /* old devices match any request */
+ return true;
+
+ if (want_product == have_product)
+ return true;
+
+ return false;
+ }
+
+ static public boolean request_permission(Context context, UsbDevice device, PendingIntent pi) {
+ UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+// if (manager.hasPermission(device))
+// return true;
+
+ Log.d(TAG, "request permission for USB device " + device.toString());
+
+ manager.requestPermission(device, pi);
+ return false;
+ }
+
+ static public UsbDevice find_device(Context context, int match_product) {
+ UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+ HashMap<String,UsbDevice> devices = manager.getDeviceList();
+
+ for (UsbDevice device : devices.values()) {
+ int vendor = device.getVendorId();
+ int product = device.getProductId();
+
+ if (matchProduct(match_product, device)) {
+ Log.d(TAG, "found USB device " + device.toString());
+ return device;
+ }
+ }
+
+ return null;
+ }
+
+ private void disconnected() {
+ if (closed()) {
+ if (D) Log.d(TAG, "disconnected after closed");
+ return;
+ }
+
+ if (D) Log.d(TAG, "Sending disconnected message");
+ handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
+ }
+
+ void close_device() {
+ UsbDeviceConnection tmp_connection;
+
+ synchronized(this) {
+ tmp_connection = connection;
+ connection = null;
+ }
+
+ if (tmp_connection != null) {
+ if (D) Log.d(TAG, "Closing USB device");
+ tmp_connection.close();
+ }
+ }
+
+ int read(byte[] buffer, int len) {
+ int ret = connection.bulkTransfer(in, buffer, len, -1);
+ if (D) Log.d(TAG, String.format("read(%d) = %d\n", len, ret));
+ return ret;
+ }
+
+ int write(byte[] buffer, int len) {
+ int ret = connection.bulkTransfer(out, buffer, len, -1);
+ if (D) Log.d(TAG, String.format("write(%d) = %d\n", len, ret));
+ return ret;
+ }
+
+ // Stubs of required methods when extending AltosLink
+ public boolean can_cancel_reply() { return false; }
+ public boolean show_reply_timeout() { return true; }
+ public void hide_reply_timeout() { }
+
+}
}
} catch (InterruptedException ee) {
} catch (IOException ie) {
+ Log.e(TAG, "IO exception in telemetry reader");
+ handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, link).sendToTarget();
} finally {
close();
}
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothAdapter;
+import android.hardware.usb.*;
import android.content.Intent;
import android.content.Context;
import android.os.Bundle;
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_CONNECT = 3;
- static final int MSG_CONNECTED = 4;
- static final int MSG_CONNECT_FAILED = 5;
- static final int MSG_DISCONNECTED = 6;
- static final int MSG_TELEMETRY = 7;
- static final int MSG_SETFREQUENCY = 8;
- static final int MSG_CRC_ERROR = 9;
- static final int MSG_SETBAUD = 10;
- static final int MSG_DISCONNECT = 11;
+ static final int MSG_OPEN_USB = 4;
+ static final int MSG_CONNECTED = 5;
+ static final int MSG_CONNECT_FAILED = 6;
+ static final int MSG_DISCONNECTED = 7;
+ static final int MSG_TELEMETRY = 8;
+ static final int MSG_SETFREQUENCY = 9;
+ static final int MSG_CRC_ERROR = 10;
+ static final int MSG_SETBAUD = 11;
+ static final int MSG_DISCONNECT = 12;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
// Name of the connected device
DeviceAddress address;
- private AltosBluetooth altos_bluetooth = null;
+ private AltosDroidLink altos_link = null;
private TelemetryReader telemetry_reader = null;
private TelemetryLogger telemetry_logger = null;
@Override
public void handleMessage(Message msg) {
TelemetryService s = service.get();
- AltosBluetooth bt = null;
+ AltosDroidLink bt = null;
if (s == null)
return;
switch (msg.what) {
AltosDroidPreferences.set_active_device(address);
s.start_altos_bluetooth(address, false);
break;
+ case MSG_OPEN_USB:
+ if (D) Log.d(TAG, "Open USB command received");
+ UsbDevice device = (UsbDevice) msg.obj;
+ s.start_usb(device);
+ break;
case MSG_DISCONNECT:
if (D) Log.d(TAG, "Disconnect command received");
s.address = null;
- s.stop_altos_bluetooth(true);
+ s.disconnect(true);
break;
case MSG_SETFREQUENCY:
if (D) Log.d(TAG, "MSG_SETFREQUENCY");
s.telemetry_state.frequency = (Double) msg.obj;
if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
try {
- s.altos_bluetooth.set_radio_frequency(s.telemetry_state.frequency);
- s.altos_bluetooth.save_frequency();
+ s.altos_link.set_radio_frequency(s.telemetry_state.frequency);
+ s.altos_link.save_frequency();
} catch (InterruptedException e) {
} catch (TimeoutException e) {
}
if (D) Log.d(TAG, "MSG_SETBAUD");
s.telemetry_state.telemetry_rate = (Integer) msg.obj;
if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
- s.altos_bluetooth.set_telemetry_rate(s.telemetry_state.telemetry_rate);
- s.altos_bluetooth.save_telemetry_rate();
+ s.altos_link.set_telemetry_rate(s.telemetry_state.telemetry_rate);
+ s.altos_link.save_telemetry_rate();
}
s.send_to_clients();
break;
*/
case MSG_CONNECTED:
Log.d(TAG, "MSG_CONNECTED");
- bt = (AltosBluetooth) msg.obj;
+ bt = (AltosDroidLink) msg.obj;
- if (bt != s.altos_bluetooth) {
+ if (bt != s.altos_link) {
if (D) Log.d(TAG, "Stale message");
break;
}
break;
case MSG_CONNECT_FAILED:
Log.d(TAG, "MSG_CONNECT_FAILED");
- bt = (AltosBluetooth) msg.obj;
+ bt = (AltosDroidLink) msg.obj;
- if (bt != s.altos_bluetooth) {
+ if (bt != s.altos_link) {
if (D) Log.d(TAG, "Stale message");
break;
}
if (D) Log.d(TAG, "Connection failed... retrying");
s.start_altos_bluetooth(s.address, true);
} else {
- s.stop_altos_bluetooth(true);
+ s.disconnect(true);
}
break;
case MSG_DISCONNECTED:
+
+ /* This can be sent by either AltosDroidLink or TelemetryReader */
Log.d(TAG, "MSG_DISCONNECTED");
- bt = (AltosBluetooth) msg.obj;
+ bt = (AltosDroidLink) msg.obj;
- if (bt != s.altos_bluetooth) {
+ if (bt != s.altos_link) {
if (D) Log.d(TAG, "Stale message");
break;
}
if (D) Log.d(TAG, "Connection lost... retrying");
s.start_altos_bluetooth(s.address, true);
} else {
- s.stop_altos_bluetooth(true);
+ s.disconnect(true);
}
break;
send_to_client(client, m);
}
- private void stop_altos_bluetooth(boolean notify) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): begin");
+ private void disconnect(boolean notify) {
+ if (D) Log.d(TAG, "disconnect(): begin");
+
telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
telemetry_state.address = null;
- if (altos_bluetooth != null)
- altos_bluetooth.closing();
+ if (altos_link != null)
+ altos_link.closing();
if (telemetry_reader != null) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): stopping TelemetryReader");
+ if (D) Log.d(TAG, "disconnect(): stopping TelemetryReader");
telemetry_reader.interrupt();
try {
telemetry_reader.join();
telemetry_reader = null;
}
if (telemetry_logger != null) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): stopping TelemetryLogger");
+ if (D) Log.d(TAG, "disconnect(): stopping TelemetryLogger");
telemetry_logger.stop();
telemetry_logger = null;
}
- if (altos_bluetooth != null) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): stopping AltosBluetooth");
- altos_bluetooth.close();
- altos_bluetooth = null;
+ if (altos_link != null) {
+ if (D) Log.d(TAG, "disconnect(): stopping AltosDroidLink");
+ altos_link.close();
+ altos_link = null;
}
telemetry_state.config = null;
if (notify) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): send message to clients");
+ if (D) Log.d(TAG, "disconnect(): send message to clients");
send_to_clients();
if (clients.isEmpty()) {
- if (D) Log.d(TAG, "stop_altos_bluetooth(): no clients, terminating");
+ if (D) Log.d(TAG, "disconnect(): no clients, terminating");
stopSelf();
}
}
}
+ private void start_usb(UsbDevice device) {
+ AltosUsb d = new AltosUsb(this, device, handler);
+
+ if (d != null) {
+ disconnect(false);
+ altos_link = d;
+ try {
+ connected();
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+
private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
// Get the BLuetoothDevice object
BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address);
- stop_altos_bluetooth(false);
+ disconnect(false);
if (pause) {
try {
Thread.sleep(4000);
}
this.address = address;
if (D) Log.d(TAG, String.format("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress()));
- altos_bluetooth = new AltosBluetooth(device, handler);
+ altos_link = new AltosBluetooth(device, handler);
telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
telemetry_state.address = address;
send_to_clients();
private void connected() throws InterruptedException {
if (D) Log.d(TAG, "connected top");
try {
- if (altos_bluetooth == null)
+ if (altos_link == null)
throw new InterruptedException("no bluetooth");
- telemetry_state.config = altos_bluetooth.config_data();
- altos_bluetooth.set_radio_frequency(telemetry_state.frequency);
- altos_bluetooth.set_telemetry_rate(telemetry_state.telemetry_rate);
+ telemetry_state.config = altos_link.config_data();
+ altos_link.set_radio_frequency(telemetry_state.frequency);
+ altos_link.set_telemetry_rate(telemetry_state.telemetry_rate);
} catch (TimeoutException e) {
// If this timed out, then we really want to retry it, but
// probably safer to just retry the connection from scratch.
start_altos_bluetooth(address, true);
} else {
handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();
- stop_altos_bluetooth(true);
+ disconnect(true);
}
return;
}
telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
telemetry_state.address = address;
- telemetry_reader = new TelemetryReader(altos_bluetooth, handler, telemetry_state.state);
+ telemetry_reader = new TelemetryReader(altos_link, handler, telemetry_state.state);
telemetry_reader.start();
if (D) Log.d(TAG, "connected TelemetryReader started");
- telemetry_logger = new TelemetryLogger(this, altos_bluetooth);
+ telemetry_logger = new TelemetryLogger(this, altos_link);
if (D) Log.d(TAG, "Notify UI of connection");
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
-
- DeviceAddress address = AltosDroidPreferences.active_device();
- if (address != null)
- start_altos_bluetooth(address, false);
}
@Override
// Move us into the foreground.
startForeground(NOTIFICATION, notification);
+ String action = intent.getAction();
+
+ if (action.equals(AltosDroid.ACTION_BLUETOOTH)) {
+ DeviceAddress address = AltosDroidPreferences.active_device();
+ if (address != null && !address.address.startsWith("USB"))
+ start_altos_bluetooth(address, false);
+ }
+
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
// Stop the bluetooth Comms threads
- stop_altos_bluetooth(true);
+ disconnect(true);
// Demote us from the foreground, and cancel the persistent notification.
stopForeground(true);