Hi All, I am writing an app that connects to a telnet server via wifi. I have a service that manages the socket connection. It all works fine, but when the phone sleeps it disconnects the wifi radio, which causes the socket connection to break (and throws a SocketException).
I feel like I should be able to set up a a broadcast receiver whose onResume() method is called when the wifi network connection is lost, and that would allow me to gracefully shut down the socket, and re-open it if the network is immediately re-connected. But I can't find anything like that in the doc or via searching.
Service code is here if you want it, thanks for the help, I really appreciate it!
/**
*
*/
package com.wingedvictorydesign.LightfactoryRemote;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.text.Editable;
import android.util.Log;
import android.widget.Toast;
import android.os.Debug;
/**
* @author Max
*
*/
public class TelnetService extends Service {
private final int DISCONNECTED = 0;
private final int CONNECTED = 1;
//place notifications in the notification bar
NotificationManager mNM;
protected InputStream in;
protected OutputStream out;
protected Socket socket;
//the socket timeout, to prevent blocking if the server connection is lost.
protected final int SO_TIMEOUT = 250;
//holds the incoming stream from socket until it is ready to be read.
BufferedReader inputBuffer;
final RemoteCallbackList<TelnetServiceCallback> mCallbacks
= new RemoteCallbackList<TelnetServiceCallback>();
@Override
public void onCreate() {
super.onCreate();
Log.d("LightfactoryRemote", "TelnetService onCreate()");
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
}//end onCreate()
@Override
public void onDestroy() {
super.onDestroy();
Log.d("LightfactoryRemote", "TelnetService onDestroy()");
// Cancel the persistent notification, if it hasn't been already.
mNM.cancel(R.string.telnet_service_connected);
}//end onDestroy()
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d("LightfactoryRemote", "TelnetService onBind()");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
super.onUnbind(intent);
Log.d("LightfactoryRemote", "TelnetService onUnBind()");
return true;
}
@Override
public void onStart( Intent intent, int startId ) {
super.onStart( intent, startId );
Log.d("TelnetService", "TelnetService onStart()");
}
private final TelnetServiceInterface.Stub mBinder = new TelnetServiceInterface.Stub() {
public void registerCallback(TelnetServiceCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
public void unregisterCallback(TelnetServiceCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
public String connectToTelnet(String Host, int Port) throws RemoteException {
//android.os.Debug.waitForDebugger();
String hostInfo = null;
try {
socket = new java.net.Socket();
socket.setSoTimeout(SO_TIMEOUT);
socket.connect(new InetSocketAddress(Host, Port), 10000); //setup the port with a timeout of 10sec.
out = socket.getOutputStream();
/*
* in is wrapped in a reader, then in a Buffered reader. This is supposedly better for performance,
* and allows us to read a line at a time using the readLine() method.
*/
inputBuffer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
catch(java.io.IOException e) {
Log.d("TelnetService.java", "Connection failed! " + e);
/* if the connection fails, return null for serverResponse, which will
* be handled appropriately on the client side.
*/
return hostInfo;
}
//now that the command has been sent, read the response.
hostInfo = readBuffer();
Log.d("TelnetService.java", hostInfo);
//notify the user that we are connected
showNotification(CONNECTED, Host, Port);
return hostInfo;
}//end connectToTelnet
/** Tests for a currently active connection. Three cases must be distinguished.
* 1. A connection attempt has not been made. Return false.
* 2. A connection attempt has been made, and socket is initialized, but no connection
* is active. isConnected() returns false.
* 3. A connection is active. isConnected() returns true.
*/
public boolean areYouThere() {
if (socket != null) {
boolean connectStatus = socket.isConnected();
return connectStatus;
}
else {
return false;
}
}//end areYouThere
public void disconnect() {
try {
if (inputBuffer != null) {
inputBuffer.close();
}
if (socket != null) {
socket.close();
}
}
catch (IOException e) {
}
// Cancel the persistent notification.
mNM.cancel(R.string.telnet_service_connected);
}//end disconnect()
/** send the string to the telnet server, and return the response from server
* If the connection is lost, an IOException results, so return null to be handled
* appropriately on the client-side.
* @throws RemoteException
*/
public String sendToTelnet(String toTelnet) throws RemoteException {
if (out == null) {
/*if out is still null, no connection has been made. Throw
* RemoteException to be handled on the client side.
*/
throw new RemoteException();
}
else {
byte arr[];
arr = (toTelnet + "\r" + "\n").getBytes();
try {
out.write(arr);
//now that the command has been sent, read the response.
String serverResponse = readBuffer();
return serverResponse;
}
catch (IOException e) {
/* if a connection was made, but then lost, we end up here.
* throw a Remoteexception for handling by the client.
*/
Log.d("TelnetService", "IO exception" + e);
disconnect();
throw new RemoteException();
}
}//end else
}//end sendToTelnet
};//end ConnectService.Stub class
public String readBuffer() {
StringBuilder serverResponse = new StringBuilder();
int character;
try {
//keep reading new lines into line until there are none left.
while (inputBuffer.ready()) {
/*as each character is read, append it to serverResponse,
* throwing away the carriage returns (which read as glyphs),
* and the ">" prompt symbols.
*/
character = inputBuffer.read();
if ((character != 13) && (character != 62)) {
serverResponse.append( (char)character);
}
}
}//end try
catch (SocketTimeoutException e) {
Log.d("TelnetService read()", "SocketTimeoutException");
}
catch (IOException e) {
Log.d("TelnetService read()", "read() IO exception" + e);
}
return serverResponse.toString();
}
/**
* Show a notification while this service is running.
*/
private void showNotification(int event, String Host, int Port) {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence notificationText = "Connected to " + Host + " : " + Port;
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.notbar_connected, notificationText,
System.currentTimeMillis());
//set the notification not to clear when the user hits "Clear All Notifications"
notification.flags |= Notification.FLAG_NO_CLEAR;
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LightfactoryRemote.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.telnet_service_connected),
notificationText, contentIntent);
// Send the notification.
// We use a string id because it is a unique number. We use it later to cancel.
mNM.notify(R.string.telnet_service_connected, notification);
}//end showNotification()
} //end TelnetConnection