get another DatagramSocket when ours goes bad

Fix longstanding bug triggered by something as simple as putting device
into and out of airplane mode. Once the class-variable socket was
created it was never replaced. Now respond to exceptions that mean it's
useless and set it to null so existing logic will recreate it.
This commit is contained in:
Eric House 2019-03-20 13:09:01 -07:00
parent 4354cbdc4f
commit 36cc181a5e

View file

@ -521,33 +521,40 @@ public class RelayService extends XWJIService
} }
} }
private synchronized void connectSocketOnce() throws InterruptedException private DatagramSocket connectSocketOnce() throws InterruptedException
{ {
if ( null == s_UDPSocket ) { DatagramSocket result = null;
final RelayService service = this; synchronized ( RelayService.class ) {
int port = XWPrefs.getDefaultRelayPort( service ); if ( null != s_UDPSocket && ! s_UDPSocket.isConnected() ) {
String host = XWPrefs.getDefaultRelayHost( service ); closeUDPSocket( s_UDPSocket );
try {
s_UDPSocket = new DatagramSocket();
s_UDPSocket.setSoTimeout(30 * 1000); // timeout so we can log
InetAddress addr = InetAddress.getByName( host );
s_UDPSocket.connect( addr, port ); // remember this address
Log.d( TAG, "connectSocket(%s:%d): s_UDPSocket now %H",
host, port, s_UDPSocket );
} catch( SocketException se ) {
Log.ex( TAG, se );
Assert.fail();
} catch( java.net.UnknownHostException uhe ) {
Log.ex( TAG, uhe );
Log.e( TAG, "connectSocketOnce(): %s", uhe.getMessage() );
// Assert.assertFalse( BuildConfig.DEBUG );
} }
} else if( ! s_UDPSocket.isConnected() ) {
Log.e( TAG, "connectSocketOnce(): udp socket not connected" ); if ( null == s_UDPSocket ) {
// Assert.assertTrue( s_UDPSocket.isConnected() ); final RelayService service = this;
int port = XWPrefs.getDefaultRelayPort( service );
String host = XWPrefs.getDefaultRelayHost( service );
try {
DatagramSocket udpSocket = new DatagramSocket();
udpSocket.setSoTimeout(30 * 1000); // timeout so we can log
InetAddress addr = InetAddress.getByName( host );
udpSocket.connect( addr, port ); // remember this address
Log.d( TAG, "connectSocket(%s:%d): udpSocket now %H",
host, port, udpSocket );
s_UDPSocket = udpSocket;
} catch( SocketException se ) {
Log.ex( TAG, se );
Assert.fail();
} catch( java.net.UnknownHostException uhe ) {
Log.ex( TAG, uhe );
Log.e( TAG, "connectSocketOnce(): %s", uhe.getMessage() );
// Assert.assertFalse( BuildConfig.DEBUG );
}
}
result = s_UDPSocket;
} }
return result;
} }
private boolean serviceQueue() private boolean serviceQueue()
@ -695,40 +702,40 @@ public class RelayService extends XWJIService
{ {
int sentLen = 0; int sentLen = 0;
if ( packets.size() > 0 ) { DatagramSocket udpSocket = s_UDPSocket;
if ( null != udpSocket && packets.size() > 0 ) {
// Log.d( TAG, "sendViaUDP(): sending %d at once", packets.size() ); // Log.d( TAG, "sendViaUDP(): sending %d at once", packets.size() );
final RelayService service = this; final RelayService service = this;
service.noteSent( packets, true ); service.noteSent( packets, true );
for ( PacketData packet : packets ) { for ( PacketData packet : packets ) {
boolean getOut = true; boolean breakNow = true;
byte[] data = packet.assemble(); byte[] data = packet.assemble();
try { try {
DatagramPacket udpPacket = new DatagramPacket( data, data.length ); DatagramPacket udpPacket = new DatagramPacket( data, data.length );
s_UDPSocket.send( udpPacket ); udpSocket.send( udpPacket );
sentLen += udpPacket.getLength(); sentLen += udpPacket.getLength();
// Why's this commented out?
// packet.setSentMS( nowMS ); // packet.setSentMS( nowMS );
getOut = false; breakNow = false;
} catch ( java.net.SocketException se ) { } catch ( IOException ex ) {
Log.ex( TAG, se ); Log.e( TAG, "fail sending to %s", udpSocket );
Log.ex( TAG, ex );
Log.i( TAG, "Restarting threads to force new socket" ); Log.i( TAG, "Restarting threads to force new socket" );
ConnStatusHandler.updateStatusOut( service, null, ConnStatusHandler.updateStatusOut( service, null,
CommsConnType.COMMS_CONN_RELAY, CommsConnType.COMMS_CONN_RELAY,
true ); true );
closeUDPSocket( udpSocket );
service.m_handler.post( new Runnable() { service.m_handler.post( new Runnable() {
public void run() { public void run() {
service.stopUDPReadThread(); service.stopUDPReadThread();
} }
} ); } );
break;
} catch ( java.io.IOException ioe ) {
Log.e( TAG, "sendViaUDP(): failure \"%s\" sending on %s",
s_UDPSocket, ioe.getMessage() );
} catch ( NullPointerException npe ) { } catch ( NullPointerException npe ) {
Log.w( TAG, "network problem; dropping packet" ); Log.w( TAG, "network problem; dropping packet" );
} }
if ( getOut ) { if ( breakNow ) {
break; break;
} }
} }
@ -736,8 +743,8 @@ public class RelayService extends XWJIService
ConnStatusHandler.updateStatus( service, null, ConnStatusHandler.updateStatus( service, null,
CommsConnType.COMMS_CONN_RELAY, CommsConnType.COMMS_CONN_RELAY,
sentLen > 0 ); sentLen > 0 );
Log.d( TAG, "sendViaUDP(): sent %d bytes (%d packets)", Log.d( TAG, "%s.sendViaUDP(): sent %d bytes (%d packets)",
sentLen, packets.size() ); this, sentLen, packets.size() );
} }
return sentLen; return sentLen;
@ -1253,6 +1260,8 @@ public class RelayService extends XWJIService
private static class UDPReadThread extends Thread { private static class UDPReadThread extends Thread {
private RelayService[] mServiceHolder = {null}; private RelayService[] mServiceHolder = {null};
UDPReadThread() { super("UDPReadThread"); }
void setService( RelayService service ) void setService( RelayService service )
{ {
Assert.assertNotNull( service ); Assert.assertNotNull( service );
@ -1294,19 +1303,24 @@ public class RelayService extends XWJIService
@Override @Override
public void run() { public void run() {
Log.i( TAG, "%s.run() starting", this );
Context context = XWApp.getContext(); Context context = XWApp.getContext();
try { try {
if ( null == s_UDPSocket ) { DatagramSocket udpSocket = s_UDPSocket;
getService().connectSocketOnce(); // block until this is done if ( null == udpSocket ) {
udpSocket = getService().connectSocketOnce(); // block until this is done
} }
Log.i( TAG, "%s.run() starting", this );
byte[] buf = new byte[1024]; byte[] buf = new byte[1024];
DatagramPacket packet =
new DatagramPacket( buf, buf.length );
for ( ; ; ) { for ( ; ; ) {
DatagramPacket packet = if ( interrupted() ) {
new DatagramPacket( buf, buf.length ); Log.d( TAG, "%s.run() interrupted; outta here", this );
break;
}
try { try {
s_UDPSocket.receive( packet ); udpSocket.receive( packet );
postGotPacket( context, packet ); postGotPacket( context, packet );
// final RelayService service = getService(); // final RelayService service = getService();
// service.resetExitTimer(); // service.resetExitTimer();
@ -1315,8 +1329,8 @@ public class RelayService extends XWJIService
// poll timing out, typically // poll timing out, typically
// Log.d( TAG, "iioe from receive(): %s", iioe.getMessage() ); // Log.d( TAG, "iioe from receive(): %s", iioe.getMessage() );
} catch( IOException ioe ) { } catch( IOException ioe ) {
Log.d( TAG, "ioe from receive(): %s/%s", ioe.getMessage() ); Log.d( TAG, "ioe from receive(): %s", ioe.getMessage() );
Assert.assertFalse( BuildConfig.DEBUG ); closeUDPSocket( udpSocket );
break; break;
} }
} }
@ -1340,6 +1354,16 @@ public class RelayService extends XWJIService
} }
} }
private static void closeUDPSocket( DatagramSocket udpSocket )
{
synchronized ( RelayService.class ) {
if ( udpSocket == s_UDPSocket ) {
s_UDPSocket.close();
s_UDPSocket = null;
}
}
}
private static class AsyncSender extends Thread { private static class AsyncSender extends Thread {
private Context m_context; private Context m_context;
private HashMap<String,ArrayList<byte[]>> m_msgHash; private HashMap<String,ArrayList<byte[]>> m_msgHash;