add threadKiller to close sockets when remote isn't responding --

which can sometimes be for minutes with the Moment at least.  Make
name->hwaddr Map static since it might as well outlive a Service
instance (though right now I'm clearing it all the time.)
This commit is contained in:
Eric House 2012-02-05 21:47:26 -08:00
parent 99e001b8a2
commit 4deb1d3a77

View file

@ -122,8 +122,11 @@ public class BTService extends Service {
private BluetoothAdapter m_adapter; private BluetoothAdapter m_adapter;
private LinkedBlockingQueue<BTQueueElem> m_queue; private LinkedBlockingQueue<BTQueueElem> m_queue;
private HashMap<String,String> m_names; private static HashMap<String,String> s_names =
new HashMap<String, String>();
private BTMsgSink m_btMsgSink; private BTMsgSink m_btMsgSink;
private Thread m_listener;
private Thread m_sender;
public static boolean BTEnabled() public static boolean BTEnabled()
{ {
@ -150,7 +153,8 @@ public class BTService extends Service {
context.startService( intent ); context.startService( intent );
} }
public static void rescan( Context context ){ public static void rescan( Context context )
{
Intent intent = new Intent( context, BTService.class ); Intent intent = new Intent( context, BTService.class );
intent.putExtra( CMD_STR, SCAN ); intent.putExtra( CMD_STR, SCAN );
context.startService( intent ); context.startService( intent );
@ -200,11 +204,8 @@ public class BTService extends Service {
DbgUtils.logf( "BTService.onCreate()" ); DbgUtils.logf( "BTService.onCreate()" );
m_adapter = BluetoothAdapter.getDefaultAdapter(); m_adapter = BluetoothAdapter.getDefaultAdapter();
if ( null != m_adapter && m_adapter.isEnabled() ) { if ( null != m_adapter && m_adapter.isEnabled() ) {
m_names = new HashMap<String, String>(); startListener();
m_queue = new LinkedBlockingQueue<BTQueueElem>(); startSender();
m_btMsgSink = new BTMsgSink();
new BTListenerThread().start();
new BTSenderThread().start();
} else { } else {
DbgUtils.logf( "not starting threads: BT not available" ); DbgUtils.logf( "not starting threads: BT not available" );
stopSelf(); stopSelf();
@ -251,6 +252,11 @@ public class BTService extends Service {
boolean cameOn = intent.getBooleanExtra( RADIO_STR, false ); boolean cameOn = intent.getBooleanExtra( RADIO_STR, false );
BTEvent evt = cameOn? BTEvent.BT_ENABLED : BTEvent.BT_DISABLED; BTEvent evt = cameOn? BTEvent.BT_ENABLED : BTEvent.BT_DISABLED;
sendResult( evt ); sendResult( evt );
if ( !cameOn ) {
stopListener();
stopSender();
stopSelf();
}
break; break;
default: default:
Assert.fail(); Assert.fail();
@ -270,7 +276,7 @@ public class BTService extends Service {
@Override @Override
public void run() { public void run() {
BluetoothServerSocket serverSocket; BluetoothServerSocket serverSocket;
for ( ; ; ) { while ( m_adapter.isEnabled() ) {
try { try {
serverSocket = m_adapter. serverSocket = m_adapter.
listenUsingRfcommWithServiceRecord( XWApp.getAppName(), listenUsingRfcommWithServiceRecord( XWApp.getAppName(),
@ -346,10 +352,12 @@ public class BTService extends Service {
os.writeByte( BTCmd.PING.ordinal() ); os.writeByte( BTCmd.PING.ordinal() );
DbgUtils.logf( "sendPings: wrote" ); DbgUtils.logf( "sendPings: wrote" );
os.flush(); os.flush();
Thread killer = killSocketIn( socket, 10 );
DataInputStream is = DataInputStream is =
new DataInputStream( socket.getInputStream() ); new DataInputStream( socket.getInputStream() );
boolean success = BTCmd.PONG == BTCmd.values()[is.readByte()]; boolean success = BTCmd.PONG == BTCmd.values()[is.readByte()];
killer.interrupt();
socket.close(); socket.close();
if ( success ) { if ( success ) {
@ -397,7 +405,7 @@ public class BTService extends Service {
sendResult( evt, elem.m_gameID ); sendResult( evt, elem.m_gameID );
} }
} catch ( java.io.IOException ioe ) { } catch ( java.io.IOException ioe ) {
DbgUtils.logf( "sendInvites: ioe: %s", ioe.toString() ); DbgUtils.logf( "sendInvite: ioe: %s", ioe.toString() );
} }
} }
@ -420,15 +428,17 @@ public class BTService extends Service {
outStream.write( elem.m_msg, 0, elem.m_msg.length ); outStream.write( elem.m_msg, 0, elem.m_msg.length );
outStream.flush(); outStream.flush();
Thread killer = killSocketIn( socket, 10 );
DataInputStream inStream = DataInputStream inStream =
new DataInputStream( socket.getInputStream() ); new DataInputStream( socket.getInputStream() );
success = success =
BTCmd.MESG_ACCPT == BTCmd.values()[inStream.readByte()]; BTCmd.MESG_ACCPT == BTCmd.values()[inStream.readByte()];
killer.interrupt();
socket.close(); socket.close();
} }
} catch ( java.io.IOException ioe ) { } catch ( java.io.IOException ioe ) {
DbgUtils.logf( "sendInvites: ioe: %s", ioe.toString() ); DbgUtils.logf( "sendMsg: ioe: %s", ioe.toString() );
success = false; success = false;
} }
@ -444,16 +454,16 @@ public class BTService extends Service {
private void addAddr( BluetoothDevice dev ) private void addAddr( BluetoothDevice dev )
{ {
synchronized( m_names ) { synchronized( s_names ) {
m_names.put( dev.getName(), dev.getAddress() ); s_names.put( dev.getName(), dev.getAddress() );
} }
} }
private String addrFor( String name ) private String addrFor( String name )
{ {
String addr; String addr;
synchronized( m_names ) { synchronized( s_names ) {
addr = m_names.get( name ); addr = s_names.get( name );
} }
DbgUtils.logf( "addrFor(%s)=>%s", name, addr ); DbgUtils.logf( "addrFor(%s)=>%s", name, addr );
Assert.assertNotNull( addr ); Assert.assertNotNull( addr );
@ -464,8 +474,8 @@ public class BTService extends Service {
private String[] names() private String[] names()
{ {
Set<String> names = null; Set<String> names = null;
synchronized( m_names ) { synchronized( s_names ) {
names = m_names.keySet(); names = s_names.keySet();
} }
String[] result = new String[names.size()]; String[] result = new String[names.size()];
@ -496,8 +506,8 @@ public class BTService extends Service {
sendPings( BTEvent.HOST_PONGED ); sendPings( BTEvent.HOST_PONGED );
break; break;
case SCAN: case SCAN:
synchronized ( m_names ) { synchronized ( s_names ) {
m_names.clear(); s_names.clear();
} }
sendPings( null ); sendPings( null );
sendResult( BTEvent.SCAN_DONE, (Object)(names()) ); sendResult( BTEvent.SCAN_DONE, (Object)(names()) );
@ -558,9 +568,6 @@ public class BTService extends Service {
result = BTCmd.INVITE_DUPID; result = BTCmd.INVITE_DUPID;
} }
// Post notification that, when selected, will create a game
// -- or ask if user wants to create one.
DataOutputStream os = new DataOutputStream( socket.getOutputStream() ); DataOutputStream os = new DataOutputStream( socket.getOutputStream() );
os.writeByte( result.ordinal() ); os.writeByte( result.ordinal() );
os.flush(); os.flush();
@ -622,12 +629,76 @@ public class BTService extends Service {
} }
} }
private void startListener()
{
m_btMsgSink = new BTMsgSink();
m_listener = new BTListenerThread();
m_listener.start();
}
private void startSender()
{
m_queue = new LinkedBlockingQueue<BTQueueElem>();
m_sender = new BTSenderThread();
m_sender.start();
}
private void stopListener()
{
DbgUtils.logf( "stopListener..." );
m_listener.interrupt();
try {
m_listener.join( 100 );
} catch ( InterruptedException ie ) {
DbgUtils.logf( "stopListener: join=>%s", ie.toString() );
}
m_listener = null;
DbgUtils.logf( "stopListener done" );
}
private void stopSender()
{
DbgUtils.logf( "stopSender..." );
m_sender.interrupt();
try {
m_sender.join( 100 );
} catch ( InterruptedException ie ) {
DbgUtils.logf( "stopSender: join=>%s", ie.toString() );
}
m_sender = null;
DbgUtils.logf( "stopSender done" );
}
private Thread killSocketIn( final BluetoothSocket socket, int seconds )
{
final int millis = seconds * 1000;
Thread thread = new Thread( new Runnable() {
public void run() {
try {
Thread.sleep( millis );
} catch ( InterruptedException ie ) {
DbgUtils.logf( "killSocketIn: killed by owner" );
return;
}
try {
socket.close();
} catch( java.io.IOException ioe ) {
DbgUtils.logf( "killSocketIn: %s", ioe.toString() );
}
}
} );
thread.start();
return thread;
}
private class BTMsgSink extends MultiMsgSink { private class BTMsgSink extends MultiMsgSink {
/***** TransportProcs interface *****/ /***** TransportProcs interface *****/
public int transportSend( byte[] buf, final CommsAddrRec addr, int gameID ) public int transportSend( byte[] buf, final CommsAddrRec addr, int gameID )
{ {
Assert.assertNotNull( addr );
Assert.assertNotNull( m_queue );
m_queue.add( new BTQueueElem( BTCmd.MESG_SEND, buf, m_queue.add( new BTQueueElem( BTCmd.MESG_SEND, buf,
addr.bt_hostName, addr.bt_btAddr, addr.bt_hostName, addr.bt_btAddr,
gameID ) ); gameID ) );