From 6282d0ac998d3e0ca4c909a4774f974a6af103e2 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 8 Mar 2010 05:47:40 +0000 Subject: First pass at fixing auto-reconnect Queue up reconnections until we receive notifications from the system that we're connected again. Need to hook up a better user notification system for network connectivity problems. git-svn-id: https://connectbot.googlecode.com/svn/trunk/connectbot@486 df292f66-193f-0410-a5fc-6d59da041ff2 --- src/org/connectbot/ConsoleActivity.java | 10 +- src/org/connectbot/HostEditorActivity.java | 8 +- src/org/connectbot/HostListActivity.java | 4 +- src/org/connectbot/PortForwardListActivity.java | 12 +- src/org/connectbot/service/TerminalBridge.java | 13 +- src/org/connectbot/service/TerminalManager.java | 202 +++++++++++++++++------- src/org/connectbot/transport/AbsTransport.java | 5 + src/org/connectbot/transport/Local.java | 8 + src/org/connectbot/transport/SSH.java | 8 + src/org/connectbot/transport/Telnet.java | 8 + 10 files changed, 195 insertions(+), 83 deletions(-) (limited to 'src/org') diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java index b0c9e83..234ef43 100644 --- a/src/org/connectbot/ConsoleActivity.java +++ b/src/org/connectbot/ConsoleActivity.java @@ -149,7 +149,7 @@ public class ConsoleActivity extends Activity { final String requestedNickname = (requested != null) ? requested.getFragment() : null; int requestedIndex = 0; - TerminalBridge requestedBridge = bound.getBridgeByName(requestedNickname); + TerminalBridge requestedBridge = bound.getConnectedBridge(requestedNickname); // If we didn't find the requested connection, try opening it if (requestedNickname != null && requestedBridge == null) { @@ -176,8 +176,10 @@ public class ConsoleActivity extends Activity { public void onServiceDisconnected(ComponentName className) { // tell each bridge to forget about our prompt handler - for(TerminalBridge bridge : bound.bridges) - bridge.promptHelper.setHandler(null); + synchronized (bound.bridges) { + for(TerminalBridge bridge : bound.bridges) + bridge.promptHelper.setHandler(null); + } flip.removeAllViews(); updateEmptyVisible(); @@ -852,7 +854,7 @@ public class ConsoleActivity extends Activity { return; } - TerminalBridge requestedBridge = bound.getBridgeByName(requested.getFragment()); + TerminalBridge requestedBridge = bound.getConnectedBridge(requested.getFragment()); int requestedIndex = 0; synchronized (flip) { diff --git a/src/org/connectbot/HostEditorActivity.java b/src/org/connectbot/HostEditorActivity.java index e8202e2..95d04cc 100644 --- a/src/org/connectbot/HostEditorActivity.java +++ b/src/org/connectbot/HostEditorActivity.java @@ -240,13 +240,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr public void onServiceConnected(ComponentName className, IBinder service) { TerminalManager bound = ((TerminalManager.TerminalBinder) service).getService(); - for (TerminalBridge bridge: bound.bridges) { - if (bridge.host.equals(host)) { - hostBridge = bridge; - Log.d(TAG, "Found host bridge; charset updates will be made live"); - break; - } - } + hostBridge = bound.getConnectedBridge(host); } public void onServiceDisconnected(ComponentName name) { diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java index c477a58..94cc1ad 100644 --- a/src/org/connectbot/HostListActivity.java +++ b/src/org/connectbot/HostListActivity.java @@ -330,7 +330,7 @@ public class HostListActivity extends ListActivity { // edit, disconnect, delete MenuItem connect = menu.add(R.string.list_host_disconnect); - final TerminalBridge bridge = bound.findBridge(host); + final TerminalBridge bridge = bound.getConnectedBridge(host); connect.setEnabled((bridge != null)); connect.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { @@ -473,7 +473,7 @@ public class HostListActivity extends ListActivity { if (this.manager == null) return STATE_UNKNOWN; - if (manager.findBridge(host) != null) + if (manager.getConnectedBridge(host) != null) return STATE_CONNECTED; if (manager.disconnected.contains(host)) diff --git a/src/org/connectbot/PortForwardListActivity.java b/src/org/connectbot/PortForwardListActivity.java index 2cb5dbc..137030a 100644 --- a/src/org/connectbot/PortForwardListActivity.java +++ b/src/org/connectbot/PortForwardListActivity.java @@ -132,16 +132,8 @@ public class PortForwardListActivity extends ListActivity { public void onServiceConnected(ComponentName className, IBinder service) { TerminalManager bound = ((TerminalManager.TerminalBinder) service).getService(); - for (TerminalBridge bridge: bound.bridges) { - if (bridge.host.equals(host)) { - hostBridge = bridge; - updateHandler.sendEmptyMessage(-1); - Log.d(TAG, "Found host bridge; using that instead of database"); - break; - } - } - - + hostBridge = bound.getConnectedBridge(host); + updateHandler.sendEmptyMessage(-1); } public void onServiceDisconnected(ComponentName name) { diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java index c748046..9a2a946 100644 --- a/src/org/connectbot/service/TerminalBridge.java +++ b/src/org/connectbot/service/TerminalBridge.java @@ -455,8 +455,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener { if (disconnectListener != null) disconnectListener.onDisconnected(TerminalBridge.this); } else { + { + final String line = manager.res.getString(R.string.alert_disconnect_msg); + ((vt320) buffer).putString("\r\n" + line + "\r\n"); + } if (host.getStayConnected()) { - startConnection(); + manager.requestReconnect(this); return; } Thread disconnectPromptThread = new Thread(new Runnable() { @@ -1348,4 +1352,11 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener { return urls; } + + /** + * @return + */ + public boolean isUsingNetwork() { + return transport.usesNetwork(); + } } diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java index 083969d..1b7b91d 100644 --- a/src/org/connectbot/service/TerminalManager.java +++ b/src/org/connectbot/service/TerminalManager.java @@ -18,6 +18,7 @@ package org.connectbot.service; import java.io.IOException; +import java.lang.ref.WeakReference; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Arrays; @@ -48,10 +49,7 @@ import android.content.res.Resources; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.net.Uri; -import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -73,6 +71,11 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen public final static String TAG = "ConnectBot.TerminalManager"; public List bridges = new LinkedList(); + public Map> mHostBridgeMap = + new HashMap>(); + public Map> mNicknameBridgeMap = + new HashMap>(); + public TerminalBridge defaultBridge = null; public List disconnected = new LinkedList(); @@ -88,10 +91,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen protected SharedPreferences prefs; - private final IBinder binder = new TerminalBinder(); + final private IBinder binder = new TerminalBinder(); - private ConnectivityManager connectivityManager; - private WifiManager.WifiLock wifilock; + private ConnectivityReceiver connectivityManager; private MediaPlayer mediaPlayer; @@ -108,6 +110,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen private boolean resizeAllowed = true; + protected List> mPendingReconnect + = new LinkedList>(); + @Override public void onCreate() { Log.i(TAG, "Starting background service"); @@ -139,29 +144,22 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen } } - connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - - WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE); - wifilock = manager.createWifiLock(TAG); - vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); wantKeyVibration = prefs.getBoolean(PreferenceConstants.BUMPY_ARROWS, true); wantBellVibration = prefs.getBoolean(PreferenceConstants.BELL_VIBRATE, true); enableMediaPlayer(); + + final boolean lockingWifi = prefs.getBoolean(PreferenceConstants.WIFI_LOCK, true); + + connectivityManager = new ConnectivityReceiver(this, lockingWifi); } @Override public void onDestroy() { Log.i(TAG, "Destroying background service"); - if (bridges.size() > 0) { - TerminalBridge[] tmpBridges = bridges.toArray(new TerminalBridge[bridges.size()]); - - // disconnect and dispose of any existing bridges - for (int i = 0; i < tmpBridges.length; i++) - tmpBridges[i].dispatchDisconnect(true); - } + disconnectAll(true); if(hostdb != null) { hostdb.close(); @@ -180,35 +178,58 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen pubkeyTimer.cancel(); } - if (wifilock != null && wifilock.isHeld()) - wifilock.release(); + connectivityManager.cleanup(); ConnectionNotifier.getInstance().hideRunningNotification(this); disableMediaPlayer(); } + /** + * Disconnect all currently connected bridges. + */ + private void disconnectAll(final boolean immediate) { + TerminalBridge[] tmpBridges = null; + + synchronized (bridges) { + if (bridges.size() > 0) { + tmpBridges = bridges.toArray(new TerminalBridge[bridges.size()]); + } + } + + if (tmpBridges != null) { + // disconnect and dispose of any existing bridges + for (int i = 0; i < tmpBridges.length; i++) + tmpBridges[i].dispatchDisconnect(immediate); + } + } + /** * Open a new SSH session using the given parameters. */ private TerminalBridge openConnection(HostBean host) throws IllegalArgumentException, IOException { // throw exception if terminal already open - if (findBridge(host) != null) { + if (getConnectedBridge(host) != null) { throw new IllegalArgumentException("Connection already open for that nickname"); } TerminalBridge bridge = new TerminalBridge(this, host); bridge.setOnDisconnectedListener(this); bridge.startConnection(); - bridges.add(bridge); - // Add a reference to the WifiLock - NetworkInfo info = connectivityManager.getActiveNetworkInfo(); - if (isLockingWifi() && - info != null && - info.getType() == ConnectivityManager.TYPE_WIFI) { - Log.d(TAG, "Acquiring WifiLock"); - wifilock.acquire(); + synchronized (bridges) { + bridges.add(bridge); + WeakReference wr = new WeakReference(bridge); + mHostBridgeMap.put(bridge.host, wr); + mNicknameBridgeMap.put(bridge.host.getNickname(), wr); + } + + synchronized (disconnected) { + disconnected.remove(bridge.host); + } + + if (bridge.isUsingNetwork()) { + connectivityManager.incRef(); } // also update database with new connected time @@ -238,10 +259,6 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen return prefs.getString(PreferenceConstants.KEYMODE, PreferenceConstants.KEYMODE_RIGHT); // "Use right-side keys" } - public boolean isLockingWifi() { - return prefs.getBoolean(PreferenceConstants.WIFI_LOCK, true); - } - /** * Open a new connection by reading parameters from the given URI. Follows * format specified by an individual transport. @@ -264,30 +281,57 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen } /** - * Find the {@link TerminalBridge} with the given nickname. + * Find a connected {@link TerminalBridge} with the given HostBean. + * + * @param host the HostBean to search for + * @return TerminalBridge that uses the HostBean */ - public TerminalBridge findBridge(HostBean host) { - // find the first active bridge with given nickname - for(TerminalBridge bridge : bridges) { - if (bridge.host.equals(host)) - return bridge; + public TerminalBridge getConnectedBridge(HostBean host) { + WeakReference wr = mHostBridgeMap.get(host); + if (wr != null) { + return wr.get(); + } else { + return null; + } + } + + /** + * Find a connected {@link TerminalBridge} using its nickname. + * + * @param nickname + * @return TerminalBridge that matches nickname + */ + public TerminalBridge getConnectedBridge(final String nickname) { + if (nickname == null) { + return null; + } + WeakReference wr = mNicknameBridgeMap.get(nickname); + if (wr != null) { + return wr.get(); + } else { + return null; } - return null; } /** * Called by child bridge when somehow it's been disconnected. */ public void onDisconnected(TerminalBridge bridge) { - // remove this bridge from our list - bridges.remove(bridge); + synchronized (bridges) { + // remove this bridge from our list + bridges.remove(bridge); - if (bridges.size() == 0 && wifilock.isHeld()) { - Log.d(TAG, "WifiLock was held, releasing"); - wifilock.release(); + mHostBridgeMap.remove(bridge.host); + mNicknameBridgeMap.remove(bridge.host.getNickname()); + + if (bridge.isUsingNetwork()) { + connectivityManager.decRef(); + } } - disconnected.add(bridge.host); + synchronized (disconnected) { + disconnected.add(bridge.host); + } // pass notification back up to gui if (disconnectHandler != null) @@ -557,6 +601,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen } else if (PreferenceConstants.BUMPY_ARROWS.equals(key)) { wantKeyVibration = sharedPreferences.getBoolean( PreferenceConstants.BUMPY_ARROWS, true); + } else if (PreferenceConstants.WIFI_LOCK.equals(key)) { + final boolean lockingWifi = prefs.getBoolean(PreferenceConstants.WIFI_LOCK, true); + connectivityManager.setWantWifiLock(lockingWifi); } } @@ -579,25 +626,62 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen } /** - * @param requestedNickname - * @return TerminalBridge that matches nickname. + * Called when connectivity to the network is lost and it doesn't appear + * we'll be getting a different connection any time soon. */ - public TerminalBridge getBridgeByName(final String requestedNickname) { - if (requestedNickname == null) { - return null; - } + public void onConnectivityLost() { + final Thread t = new Thread() { + @Override + public void run() { + disconnectAll(false); + } + }; + t.start(); + } - for (final TerminalBridge bridge : bridges) { - final String nick = bridge.host.getNickname(); - if (nick == null) { - continue; + /** + * Called when connectivity to the network is restored. + */ + public void onConnectivityRestored() { + final Thread t = new Thread() { + @Override + public void run() { + reconnectPending(); } + }; + t.start(); + } - if (nick.equals(requestedNickname)) { - return bridge; + /** + * Insert request into reconnect queue to be executed either immediately + * or later when connectivity is restored depending on whether we're + * currently connected. + * + * @param bridge the TerminalBridge to reconnect when possible + */ + public void requestReconnect(TerminalBridge bridge) { + synchronized (mPendingReconnect) { + mPendingReconnect.add(new WeakReference(bridge)); + if (connectivityManager.isConnected()) { + reconnectPending(); } } + } - return null; + /** + * Reconnect all bridges that were pending a reconnect when connectivity + * was lost. + */ + private void reconnectPending() { + synchronized (mPendingReconnect) { + for (WeakReference ref : mPendingReconnect) { + TerminalBridge bridge = ref.get(); + if (bridge == null) { + continue; + } + bridge.startConnection(); + } + mPendingReconnect.clear(); + } } } diff --git a/src/org/connectbot/transport/AbsTransport.java b/src/org/connectbot/transport/AbsTransport.java index fbd4655..18397ea 100644 --- a/src/org/connectbot/transport/AbsTransport.java +++ b/src/org/connectbot/transport/AbsTransport.java @@ -246,4 +246,9 @@ public abstract class AbsTransport { public static String getFormatHint(Context context) { return "???"; } + + /** + * @return + */ + public abstract boolean usesNetwork(); } diff --git a/src/org/connectbot/transport/Local.java b/src/org/connectbot/transport/Local.java index 46903b2..5ace1b0 100644 --- a/src/org/connectbot/transport/Local.java +++ b/src/org/connectbot/transport/Local.java @@ -208,4 +208,12 @@ public class Local extends AbsTransport { public static String getFormatHint(Context context) { return context.getString(R.string.hostpref_nickname_title); } + + /* (non-Javadoc) + * @see org.connectbot.transport.AbsTransport#usesNetwork() + */ + @Override + public boolean usesNetwork() { + return false; + } } diff --git a/src/org/connectbot/transport/SSH.java b/src/org/connectbot/transport/SSH.java index 11e0659..7dfaf0c 100644 --- a/src/org/connectbot/transport/SSH.java +++ b/src/org/connectbot/transport/SSH.java @@ -944,4 +944,12 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC agentLockPassphrase = lockPassphrase; return true; } + + /* (non-Javadoc) + * @see org.connectbot.transport.AbsTransport#usesNetwork() + */ + @Override + public boolean usesNetwork() { + return true; + } } diff --git a/src/org/connectbot/transport/Telnet.java b/src/org/connectbot/transport/Telnet.java index b7388ad..5fde2f6 100644 --- a/src/org/connectbot/transport/Telnet.java +++ b/src/org/connectbot/transport/Telnet.java @@ -319,4 +319,12 @@ public class Telnet extends AbsTransport { context.getString(R.string.format_hostname), context.getString(R.string.format_port)); } + + /* (non-Javadoc) + * @see org.connectbot.transport.AbsTransport#usesNetwork() + */ + @Override + public boolean usesNetwork() { + return true; + } } -- cgit v1.2.3