diff options
7 files changed, 330 insertions, 234 deletions
| diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java index 3740f55d..973250a5 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java @@ -62,6 +62,65 @@ public class AltosBluetooth extends AltosLink {  	} +	private void connected() { +		try { +			synchronized(this) { +				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).sendToTarget(); + +					/* Notify other waiting threads that we're connected now +					 */ +					notifyAll(); +				} +			} +		} catch (IOException io) { +			connect_failed(); +		} +	} + +	private void connect_failed() { +		synchronized (this) { +			if (socket != null) { +				try { +					socket.close(); +				} catch (IOException e2) { +					if (D) Log.e(TAG, "ConnectThread: Failed to close() socket after failed connection"); +				} +				socket = null; +			} +			input = null; +			output = null; +			handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED).sendToTarget(); +			if (D) Log.e(TAG, "ConnectThread: Failed to establish connection"); +		} +	} + +	private Object closing_lock = new Object(); +	private boolean closing = false; + +	private void disconnected() { +		synchronized(closing_lock) { +			if (D) Log.e(TAG, String.format("Connection lost during I/O. Closing  %b", closing)); +			if (!closing) { +				if (D) Log.d(TAG, "Sending disconnected message"); +				handler.obtainMessage(TelemetryService.MSG_DISCONNECTED).sendToTarget(); +			} +		} +	} +  	private class ConnectThread extends Thread {  		private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); @@ -83,54 +142,44 @@ public class AltosBluetooth extends AltosLink {  			// Always cancel discovery because it will slow down a connection  			adapter.cancelDiscovery(); -			synchronized (AltosBluetooth.this) { -				// Make a connection to the BluetoothSocket -				try { -					// This is a blocking call and will only return on a -					// successful connection or an exception -					socket.connect(); +			BluetoothSocket	local_socket; -					input = socket.getInputStream(); -					output = socket.getOutputStream(); -				} catch (IOException e) { -					// Close the socket -					try { -						socket.close(); -					} catch (IOException e2) { -						if (D) Log.e(TAG, "ConnectThread: Failed to close() socket after failed connection"); -					} -					input = null; -					output = null; -					AltosBluetooth.this.notifyAll(); -					handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED).sendToTarget(); -					if (D) Log.e(TAG, "ConnectThread: Failed to establish connection"); -					return; +			try { +				synchronized (AltosBluetooth.this) { +					local_socket = socket;  				} -				input_thread = new Thread(AltosBluetooth.this); -				input_thread.start(); - -				// Configure the newly connected device for telemetry -				print("~\nE 0\n"); -				set_monitor(false); +				if (local_socket != null) { +					// Make a connection to the BluetoothSocket +					// This is a blocking call and will only return on a +					// successful connection or an exception +					local_socket.connect(); +				} -				// Let TelemetryService know we're connected -				handler.obtainMessage(TelemetryService.MSG_CONNECTED).sendToTarget(); +				connected(); -				// Notify other waiting threads, now that we're connected -				AltosBluetooth.this.notifyAll(); +			} catch (IOException e) { +				connect_failed(); +			} -				// Reset the ConnectThread because we're done +			synchronized (AltosBluetooth.this) { +				/* Reset the ConnectThread because we're done +				 */  				connect_thread = null; - -				if (D) Log.d(TAG, "ConnectThread: Connect completed");  			} +			if (D) Log.d(TAG, "ConnectThread: Connect completed");  		}  		public void cancel() {  			try { -				if (socket != null) -					socket.close(); +				BluetoothSocket	local_socket; +				synchronized(AltosBluetooth.this) { +					local_socket = socket; +					socket = null; +				} +				if (local_socket != null) +					local_socket.close(); +  			} catch (IOException e) {  				if (D) Log.e(TAG, "ConnectThread: close() of connect socket failed", e);  			} @@ -154,17 +203,13 @@ public class AltosBluetooth extends AltosLink {  	}  	private synchronized void wait_connected() throws InterruptedException, IOException { -		if (input == null) { +		if (input == null && socket != null) {  			if (D) Log.d(TAG, "wait_connected...");  			wait();  			if (D) Log.d(TAG, "wait_connected done"); -			if (input == null) throw new IOException();  		} -	} - -	private void connection_lost() { -		if (D) Log.e(TAG, "Connection lost during I/O"); -		handler.obtainMessage(TelemetryService.MSG_DISCONNECTED).sendToTarget(); +		if (socket == null) +			throw new IOException();  	}  	public void print(String data) { @@ -175,9 +220,9 @@ public class AltosBluetooth extends AltosLink {  			output.write(bytes);  			if (D) Log.d(TAG, "print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");  		} catch (IOException e) { -			connection_lost(); +			disconnected();  		} catch (InterruptedException e) { -			connection_lost(); +			disconnected();  		}  	} @@ -189,9 +234,9 @@ public class AltosBluetooth extends AltosLink {  			output.write(bytes);  			if (D) Log.d(TAG, "print(): Wrote byte: '" + c + "'");  		} catch (IOException e) { -			connection_lost(); +			disconnected();  		} catch (InterruptedException e) { -			connection_lost(); +			disconnected();  		}  	} @@ -208,32 +253,44 @@ public class AltosBluetooth extends AltosLink {  				buffer_len = input.read(buffer);  				buffer_off = 0;  			} catch (IOException e) { -				connection_lost(); +				if (D) Log.d(TAG, "getchar IOException"); +				disconnected();  				return AltosLink.ERROR;  			} catch (java.lang.InterruptedException e) { -				connection_lost(); +				if (D) Log.d(TAG, "getchar Interrupted"); +				disconnected();  				return AltosLink.ERROR;  			}  		}  		return buffer[buffer_off++];  	} +	public void closing() { +		synchronized(closing_lock) { +			if (D) Log.d(TAG, "Marked closing true"); +			closing = true; +		} +	} + +  	public void close() {  		if (D) Log.d(TAG, "close(): begin"); + +		closing(); +  		synchronized(this) {  			if (D) Log.d(TAG, "close(): synched"); -			if (connect_thread != null) { -				if (D) Log.d(TAG, "close(): stopping connect_thread"); -				connect_thread.cancel(); -				connect_thread = null; -			} -			if (D) Log.d(TAG, "close(): Closing socket"); -			try { -				socket.close(); -			} catch (IOException e) { -				if (D) Log.e(TAG, "close(): unable to close() socket"); +			if (socket != null) { +				if (D) Log.d(TAG, "close(): Closing socket"); +				try { +					socket.close(); +				} catch (IOException e) { +					if (D) Log.e(TAG, "close(): unable to close() socket"); +				} +				socket = null;  			} +			connect_thread = null;  			if (input_thread != null) {  				if (D) Log.d(TAG, "close(): stopping input_thread");  				try { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index 53963f25..10e735c8 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -207,9 +207,12 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			}  			break;  		case TelemetryState.CONNECT_CONNECTING: -			mTitle.setText(R.string.title_connecting); +			if (telemetry_state.address != null) +				mTitle.setText(String.format("Connecting to %s...", telemetry_state.address.name)); +			else +				mTitle.setText("Connecting to something...");  			break; -		case TelemetryState.CONNECT_READY: +		case TelemetryState.CONNECT_DISCONNECTED:  		case TelemetryState.CONNECT_NONE:  			mTitle.setText(R.string.title_not_connected);  			break; @@ -244,8 +247,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		update_ui(telemetry_state.state, telemetry_state.location);  		if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED)  			start_timer(); -		else -			stop_timer();  	}  	boolean same_string(String a, String b) { @@ -267,8 +268,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  	void update_ui(AltosState state, Location location) { -		Log.d(TAG, "update_ui"); -  		int prev_state = AltosLib.ao_flight_invalid;  		AltosGreatCircle from_receiver = null; @@ -277,7 +276,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			prev_state = saved_state.state;  		if (state != null) { -			Log.d(TAG, String.format("prev state %d new state  %d\n", prev_state, state.state));  			if (state.state == AltosLib.ao_flight_stateless) {  				boolean	prev_locked = false;  				boolean locked = false; @@ -297,7 +295,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			} else {  				if (prev_state != state.state) {  					String currentTab = mTabHost.getCurrentTabTag(); -					Log.d(TAG, "switch state");  					switch (state.state) {  					case AltosLib.ao_flight_boost:  						if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent"); @@ -328,22 +325,18 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  			}  			if (saved_state == null || !same_string(saved_state.callsign, state.callsign)) { -				Log.d(TAG, "update callsign");  				mCallsignView.setText(state.callsign);  			}  			if (saved_state == null || state.serial != saved_state.serial) { -				Log.d(TAG, "update serial");  				mSerialView.setText(String.format("%d", state.serial));  			}  			if (saved_state == null || state.flight != saved_state.flight) { -				Log.d(TAG, "update flight");  				if (state.flight == AltosLib.MISSING)  					mFlightView.setText("");  				else  					mFlightView.setText(String.format("%d", state.flight));  			}  			if (saved_state == null || state.state != saved_state.state) { -				Log.d(TAG, "update state");  				if (state.state == AltosLib.ao_flight_stateless) {  					mStateLayout.setVisibility(View.GONE);  				} else { @@ -352,7 +345,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  				}  			}  			if (saved_state == null || state.rssi != saved_state.rssi) { -				Log.d(TAG, "update rssi");  				mRSSIView.setText(String.format("%d", state.rssi));  			}  		} @@ -495,13 +487,13 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  	}  	@Override -	public synchronized void onResume() { +	public void onResume() {  		super.onResume();  		if(D) Log.e(TAG, "+ ON RESUME +");  	}  	@Override -	public synchronized void onPause() { +	public void onPause() {  		super.onPause();  		if(D) Log.e(TAG, "- ON PAUSE -");  	} @@ -548,21 +540,19 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {  		}  	} -	private void connectDevice(String address) { +	private void connectDevice(Intent data) {  		// Attempt to connect to the device  		try { -			if (D) Log.d(TAG, "Connecting to " + address); -			mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, address)); +			String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); +			String name = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_NAME); + +			if (D) Log.d(TAG, "Connecting to " + address + name); +			DeviceAddress	a = new DeviceAddress(address, name); +			mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, a));  		} catch (RemoteException e) {  		}  	} -	private void connectDevice(Intent data) { -		// Get the device MAC address -		String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); -		connectDevice(address); -	} -  	@Override  	public boolean onCreateOptionsMenu(Menu menu) {  		MenuInflater inflater = getMenuInflater(); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java index 7ab70147..372500c1 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java @@ -22,9 +22,10 @@ import org.altusmetrum.altoslib_6.*;  public class AltosDroidPreferences extends AltosPreferences {  	/* Active device preference name */ -	final static String activeDevicePreference = "ACTIVE-DEVICE"; +	final static String activeDeviceAddressPreference = "ACTIVE-DEVICE-ADDRESS"; +	final static String activeDeviceNamePreference = "ACTIVE-DEVICE-NAME"; -	static String active_device_address; +	static DeviceAddress	active_device_address;  	public static void init(Context context) {  		if (backend != null) @@ -32,18 +33,23 @@ public class AltosDroidPreferences extends AltosPreferences {  		AltosPreferences.init(new AltosDroidPreferencesBackend(context)); -		active_device_address = backend.getString(activeDevicePreference, null); +		String address = backend.getString(activeDeviceAddressPreference, null); +		String name = backend.getString(activeDeviceNamePreference, null); + +		if (address != null && name != null) +			active_device_address = new DeviceAddress (address, name);  	} -	public static void set_active_device(String address) { +	public static void set_active_device(DeviceAddress address) {  		synchronized(backend) {  			active_device_address = address; -			backend.putString(activeDevicePreference, active_device_address); +			backend.putString(activeDeviceAddressPreference, active_device_address.address); +			backend.putString(activeDeviceNamePreference, active_device_address.name);  			flush_preferences();  		}  	} -	public static String active_device() { +	public static DeviceAddress active_device() {  		synchronized(backend) {  			return active_device_address;  		} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java index cbb20045..f61e7ef2 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java @@ -71,13 +71,9 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen  		last_state = state;  		last_from_receiver = from_receiver;  		last_receiver = receiver; -		if (is_current) { -			if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: visible, performing update", tab_name())); - +		if (is_current)  			show(state, from_receiver, receiver); -		} else { -			if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: not visible, skipping update", tab_name())); +		else  			return; -		}  	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java index 71692122..fd6abe0f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java @@ -50,7 +50,8 @@ public class DeviceListActivity extends Activity {  	private static final boolean D = true;  	// Return Intent extra -	public static String EXTRA_DEVICE_ADDRESS = "device_address"; +	public static final String EXTRA_DEVICE_ADDRESS = "device_address"; +	public static final String EXTRA_DEVICE_NAME = "device_name";  	// Member fields  	private BluetoothAdapter mBtAdapter; @@ -164,9 +165,20 @@ public class DeviceListActivity extends Activity {  			String info = ((TextView) v).getText().toString();  			String address = info.substring(info.length() - 17); +			int newline = info.indexOf('\n'); + +			String name = null; +			if (newline > 0) +				name = info.substring(0, newline); +			else +				name = info; + +			if (D) Log.d(TAG, String.format("******* selected item '%s'", info)); +  			// Create the result Intent and include the MAC address  			Intent intent = new Intent();  			intent.putExtra(EXTRA_DEVICE_ADDRESS, address); +			intent.putExtra(EXTRA_DEVICE_NAME, name);  			// Set result and finish this Activity  			setResult(Activity.RESULT_OK, intent); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index d4ac66aa..5e34d610 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java @@ -69,24 +69,21 @@ public class TelemetryService extends Service implements LocationListener {  	private int NOTIFICATION = R.string.telemetry_service_label;  	//private NotificationManager mNM; -	// Timer - we wake up every now and then to decide if the service should stop -	private Timer timer = new Timer(); - -	ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients. -	final Handler   mHandler   = new IncomingHandler(this); -	final Messenger mMessenger = new Messenger(mHandler); // Target we publish for clients to send messages to IncomingHandler. +	ArrayList<Messenger> clients = new ArrayList<Messenger>(); // Keeps track of all current registered clients. +	final Handler   handler   = new IncomingHandler(this); +	final Messenger messenger = new Messenger(handler); // Target we publish for clients to send messages to IncomingHandler.  	// Name of the connected device -	String address; -	private AltosBluetooth  mAltosBluetooth  = null; -	private TelemetryReader mTelemetryReader = null; -	private TelemetryLogger mTelemetryLogger = null; -	// Local Bluetooth adapter -	private BluetoothAdapter mBluetoothAdapter = null; +	DeviceAddress address; +	private AltosBluetooth  altos_bluetooth  = null; +	private TelemetryReader telemetry_reader = null; +	private TelemetryLogger telemetry_logger = null; -	private TelemetryState	telemetry_state; +	// Local Bluetooth adapter +	private BluetoothAdapter bluetooth_adapter = null;  	// Last data seen; send to UI when it starts +	private TelemetryState	telemetry_state;  	// Handler of incoming messages from clients.  	static class IncomingHandler extends Handler { @@ -99,27 +96,46 @@ public class TelemetryService extends Service implements LocationListener {  			if (s == null)  				return;  			switch (msg.what) { + +				/* Messages from application */  			case MSG_REGISTER_CLIENT: -				s.mClients.add(msg.replyTo); -				try { -					// Now we try to send the freshly connected UI any relavant information about what -					// we're talking to -					msg.replyTo.send(s.message()); -				} catch (RemoteException e) { -					s.mClients.remove(msg.replyTo); -				} -				if (D) Log.d(TAG, "Client bound to service"); +				s.add_client(msg.replyTo);  				break;  			case MSG_UNREGISTER_CLIENT: -				s.mClients.remove(msg.replyTo); -				if (D) Log.d(TAG, "Client unbound from service"); +				s.remove_client(msg.replyTo);  				break;  			case MSG_CONNECT:  				if (D) Log.d(TAG, "Connect command received"); -				String address = (String) msg.obj; +				DeviceAddress address = (DeviceAddress) msg.obj;  				AltosDroidPreferences.set_active_device(address); -				s.startAltosBluetooth(address); +				s.start_altos_bluetooth(address); +				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(); +					} catch (InterruptedException e) { +					} catch (TimeoutException e) { +					} +				} +				s.send_to_clients(); +				break; +			case MSG_SETBAUD: +				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.send_to_clients();  				break; + +				/* +				 *Messages from AltosBluetooth +				 */  			case MSG_CONNECTED:  				if (D) Log.d(TAG, "Connected to device");  				try { @@ -128,51 +144,40 @@ public class TelemetryService extends Service implements LocationListener {  				}  				break;  			case MSG_CONNECT_FAILED: -				if (D) Log.d(TAG, "Connection failed... retrying"); -				if (s.address != null) -					s.startAltosBluetooth(s.address); +				if (s.address != null && !s.clients.isEmpty()) { +					if (D) Log.d(TAG, "Connection failed... retrying"); +					s.start_altos_bluetooth(s.address); +				} else { +					s.stop_altos_bluetooth(true); +				}  				break;  			case MSG_DISCONNECTED:  				Log.d(TAG, "MSG_DISCONNECTED"); -				s.stopAltosBluetooth(); +				if (s.address != null && !s.clients.isEmpty()) { +					if (D) Log.d(TAG, "Connection lost... retrying"); +					s.start_altos_bluetooth(s.address); +				} else { +					s.stop_altos_bluetooth(true); +				}  				break; + +				/* +				 * Messages from TelemetryReader +				 */  			case MSG_TELEMETRY: -				// forward telemetry messages  				s.telemetry_state.state = (AltosState) msg.obj;  				if (s.telemetry_state.state != null) {  					if (D) Log.d(TAG, "Save state");  					AltosPreferences.set_state(0, s.telemetry_state.state, null);  				}  				if (D) Log.d(TAG, "MSG_TELEMETRY"); -				s.sendMessageToClients(); +				s.send_to_clients();  				break;  			case MSG_CRC_ERROR:  				// forward crc error messages  				s.telemetry_state.crc_errors = (Integer) msg.obj;  				if (D) Log.d(TAG, "MSG_CRC_ERROR"); -				s.sendMessageToClients(); -				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.mAltosBluetooth.set_radio_frequency(s.telemetry_state.frequency); -						s.mAltosBluetooth.save_frequency(); -					} catch (InterruptedException e) { -					} catch (TimeoutException e) { -					} -				} -				s.sendMessageToClients(); -				break; -			case MSG_SETBAUD: -				if (D) Log.d(TAG, "MSG_SETBAUD"); -				s.telemetry_state.telemetry_rate = (Integer) msg.obj; -				if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) { -					s.mAltosBluetooth.set_telemetry_rate(s.telemetry_state.telemetry_rate); -					s.mAltosBluetooth.save_telemetry_rate(); -				} -				s.sendMessageToClients(); +				s.send_to_clients();  				break;  			default:  				super.handleMessage(msg); @@ -180,6 +185,8 @@ public class TelemetryService extends Service implements LocationListener {  		}  	} +	/* Construct the message to deliver to clients +	 */  	private Message message() {  		if (telemetry_state == null)  			Log.d(TAG, "telemetry_state null!"); @@ -188,117 +195,149 @@ public class TelemetryService extends Service implements LocationListener {  		return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);  	} -	private void sendMessageToClients() { -		Message m = message(); -		if (D) Log.d(TAG, String.format("Send message to %d clients", mClients.size())); -		for (int i=mClients.size()-1; i>=0; i--) { -			try { -				if (D) Log.d(TAG, String.format("Send message to client %d", i)); -				mClients.get(i).send(m); -			} catch (RemoteException e) { -				mClients.remove(i); -			} +	/* A new friend has connected +	 */ +	private void add_client(Messenger client) { + +		clients.add(client); +		if (D) Log.d(TAG, "Client bound to service"); + +		/* On connect, send the current state to the new client +		 */ +		send_to_client(client, message()); + +		/* If we've got an address from a previous session, then +		 * go ahead and try to reconnect to the device +		 */ +		if (address != null && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) { +			if (D) Log.d(TAG, "Reconnecting now..."); +			start_altos_bluetooth(address);  		}  	} -	private void stopAltosBluetooth() { -		if (D) Log.d(TAG, "stopAltosBluetooth(): begin"); -		telemetry_state.connect = TelemetryState.CONNECT_READY; -		if (mTelemetryReader != null) { -			if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryReader"); -			mTelemetryReader.interrupt(); +	/* A client has disconnected, clean up +	 */ +	private void remove_client(Messenger client) { +		clients.remove(client); +		if (D) Log.d(TAG, "Client unbound from service"); + +		/* When the list of clients is empty, stop the service if +		 * we have no current telemetry source +		 */ + +		 if (clients.isEmpty() && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) { +			 if (!D) Log.d(TAG, "No clients, no connection. Stopping\n"); +			 stopSelf(); +		 } +	} + +	private void send_to_client(Messenger client, Message m) { +		try { +			if (D) Log.d(TAG, String.format("Send message to client %s", client.toString())); +			client.send(m); +		} catch (RemoteException e) { +			if (D) Log.e(TAG, String.format("Client %s disappeared", client.toString())); +			remove_client(client); +		} +	} + +	private void send_to_clients() { +		Message m = message(); +		if (D) Log.d(TAG, String.format("Send message to %d clients", clients.size())); +		for (Messenger client : clients) +			send_to_client(client, m); +	} + +	private void stop_altos_bluetooth(boolean notify) { +		if (D) Log.d(TAG, "stop_altos_bluetooth(): begin"); +		telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED; +		telemetry_state.address = null; + +		if (altos_bluetooth != null) +			altos_bluetooth.closing(); + +		if (telemetry_reader != null) { +			if (D) Log.d(TAG, "stop_altos_bluetooth(): stopping TelemetryReader"); +			telemetry_reader.interrupt();  			try { -				mTelemetryReader.join(); +				telemetry_reader.join();  			} catch (InterruptedException e) {  			} -			mTelemetryReader = null; +			telemetry_reader = null;  		} -		if (mTelemetryLogger != null) { -			if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryLogger"); -			mTelemetryLogger.stop(); -			mTelemetryLogger = null; +		if (telemetry_logger != null) { +			if (D) Log.d(TAG, "stop_altos_bluetooth(): stopping TelemetryLogger"); +			telemetry_logger.stop(); +			telemetry_logger = null;  		} -		if (mAltosBluetooth != null) { -			if (D) Log.d(TAG, "stopAltosBluetooth(): stopping AltosBluetooth"); -			mAltosBluetooth.close(); -			mAltosBluetooth = null; +		if (altos_bluetooth != null) { +			if (D) Log.d(TAG, "stop_altos_bluetooth(): stopping AltosBluetooth"); +			altos_bluetooth.close(); +			altos_bluetooth = null;  		}  		telemetry_state.config = null; -		if (D) Log.d(TAG, "stopAltosBluetooth(): send message to clients"); -		sendMessageToClients(); +		if (notify) { +			if (D) Log.d(TAG, "stop_altos_bluetooth(): send message to clients"); +			send_to_clients(); +			if (clients.isEmpty()) { +				if (D) Log.d(TAG, "stop_altos_bluetooth(): no clients, terminating"); +				stopSelf(); +			} +		}  	} -	private void startAltosBluetooth(String address) { +	private void start_altos_bluetooth(DeviceAddress address) {  		// Get the BLuetoothDevice object -		BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); +		BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address); +		stop_altos_bluetooth(false);  		this.address = address; -		if (mAltosBluetooth == null) { -			if (D) Log.d(TAG, String.format("startAltosBluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress())); -			mAltosBluetooth = new AltosBluetooth(device, mHandler); -			telemetry_state.connect = TelemetryState.CONNECT_CONNECTING; -			sendMessageToClients(); -		} else { -			// This is a bit of a hack - if it appears we're still connected, we treat this as a restart. -			// So, to give a suitable delay to teardown/bringup, we just schedule a resend of a message -			// to ourselves in a few seconds time that will ultimately call this method again. -			// ... then we tear down the existing connection. -			// We do it this way around so that we don't lose a reference to the device when this method -			// is called on reception of MSG_CONNECT_FAILED in the handler above. -			mHandler.sendMessageDelayed(Message.obtain(null, MSG_CONNECT, address), 3000); -			stopAltosBluetooth(); -		} +		if (D) Log.d(TAG, String.format("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress())); +		altos_bluetooth = 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 (mAltosBluetooth == null) +			if (altos_bluetooth == null)  				throw new InterruptedException("no bluetooth"); -			telemetry_state.config = mAltosBluetooth.config_data(); -			mAltosBluetooth.set_radio_frequency(telemetry_state.frequency); -			mAltosBluetooth.set_telemetry_rate(telemetry_state.telemetry_rate); +			telemetry_state.config = altos_bluetooth.config_data(); +			altos_bluetooth.set_radio_frequency(telemetry_state.frequency); +			altos_bluetooth.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. -			mHandler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget(); +			handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();  			return;  		}  		if (D) Log.d(TAG, "connected bluetooth configured");  		telemetry_state.connect = TelemetryState.CONNECT_CONNECTED; +		telemetry_state.address = address; -		mTelemetryReader = new TelemetryReader(mAltosBluetooth, mHandler, telemetry_state.state); -		mTelemetryReader.start(); +		telemetry_reader = new TelemetryReader(altos_bluetooth, handler, telemetry_state.state); +		telemetry_reader.start();  		if (D) Log.d(TAG, "connected TelemetryReader started"); -		mTelemetryLogger = new TelemetryLogger(this, mAltosBluetooth); +		telemetry_logger = new TelemetryLogger(this, altos_bluetooth);  		if (D) Log.d(TAG, "Notify UI of connection"); -		sendMessageToClients(); -	} - -	private void onTimerTick() { -		if (D) Log.d(TAG, "Timer wakeup"); -		try { -			if (mClients.size() <= 0 && telemetry_state.connect != TelemetryState.CONNECT_CONNECTED) { -				stopSelf(); -			} -		} catch (Throwable t) { -			Log.e(TAG, "Timer failed: ", t); -		} +		send_to_clients();  	}  	@Override  	public void onCreate() {  		// Get local Bluetooth adapter -		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); +		bluetooth_adapter = BluetoothAdapter.getDefaultAdapter();  		// If the adapter is null, then Bluetooth is not supported -		if (mBluetoothAdapter == null) { +		if (bluetooth_adapter == null) {  			Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();  		} @@ -310,7 +349,8 @@ public class TelemetryService extends Service implements LocationListener {  		// Create a reference to the NotificationManager so that we can update our notifcation text later  		//mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); -		telemetry_state.connect = TelemetryState.CONNECT_READY; +		telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED; +		telemetry_state.address = null;  		AltosSavedState saved_state = AltosPreferences.state(0); @@ -319,17 +359,14 @@ public class TelemetryService extends Service implements LocationListener {  			telemetry_state.state = saved_state.state;  		} -		// Start our timer - first event in 10 seconds, then every 10 seconds after that. -		timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 10000L, 10000L); -  		// Listen for GPS and Network position updates  		LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);  		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this); -		String address = AltosDroidPreferences.active_device(); +		DeviceAddress address = AltosDroidPreferences.active_device();  		if (address != null) -			startAltosBluetooth(address); +			start_altos_bluetooth(address);  	}  	@Override @@ -366,28 +403,25 @@ public class TelemetryService extends Service implements LocationListener {  		((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);  		// Stop the bluetooth Comms threads -		stopAltosBluetooth(); +		stop_altos_bluetooth(true);  		// Demote us from the foreground, and cancel the persistent notification.  		stopForeground(true); -		// Stop our timer -		if (timer != null) {timer.cancel();} -  		// Tell the user we stopped.  		Toast.makeText(this, R.string.telemetry_service_stopped, Toast.LENGTH_SHORT).show();  	}  	@Override  	public IBinder onBind(Intent intent) { -		return mMessenger.getBinder(); +		return messenger.getBinder();  	}  	public void onLocationChanged(Location location) {  		telemetry_state.location = location;  		if (D) Log.d(TAG, "location changed"); -		sendMessageToClients(); +		send_to_clients();  	}  	public void onStatusChanged(String provider, int status, Bundle extras) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java index 862847d2..ca066fc2 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java @@ -21,12 +21,13 @@ import org.altusmetrum.altoslib_6.*;  import android.location.Location;  public class TelemetryState { -	public static final int CONNECT_NONE       = 0; -	public static final int CONNECT_READY      = 1; -	public static final int CONNECT_CONNECTING = 2; -	public static final int CONNECT_CONNECTED  = 3; +	public static final int CONNECT_NONE         = 0; +	public static final int CONNECT_DISCONNECTED = 1; +	public static final int CONNECT_CONNECTING   = 2; +	public static final int CONNECT_CONNECTED    = 3;  	int		connect; +	DeviceAddress	address;  	AltosConfigData	config;  	AltosState	state;  	Location	location; | 
