mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-09 05:24:44 +01:00
fix bluetooth bootstrapping
Separate processing of sockets from accepting them so that when an ACL CONN notification is received and we open a socket (but don't yet have a Service running because the ACL thing is most likely for some other app) we can set it aside to be processed once we do have a service. Use the same block-until-non-null thing as in RelayService to keep that thread free of NPEs.
This commit is contained in:
parent
48c06acef5
commit
68bb8a1268
1 changed files with 139 additions and 80 deletions
|
@ -599,14 +599,13 @@ public class BTService extends XWService {
|
||||||
private static BTListenerThread[] s_listener = {null};
|
private static BTListenerThread[] s_listener = {null};
|
||||||
private BluetoothServerSocket m_serverSocket;
|
private BluetoothServerSocket m_serverSocket;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private BTService mService;
|
private BTService[] mServiceHolder = { null };
|
||||||
private BTServiceHelper mHelper;
|
|
||||||
private volatile Thread mTimerThread;
|
private volatile Thread mTimerThread;
|
||||||
|
|
||||||
private BTListenerThread( Context context, BTService service )
|
private BTListenerThread( Context context, BTService service )
|
||||||
{
|
{
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mService = service;
|
mServiceHolder[0] = service;
|
||||||
|
|
||||||
// Started without a BTService instance? Run for only a little
|
// Started without a BTService instance? Run for only a little
|
||||||
// while
|
// while
|
||||||
|
@ -615,8 +614,7 @@ public class BTService extends XWService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void startYourself( Context context, BTService service,
|
static void startYourself( Context context, BTService service )
|
||||||
BTServiceHelper helper )
|
|
||||||
{
|
{
|
||||||
Log.d( TAG, "startYourself(%s, %s)", context, service );
|
Log.d( TAG, "startYourself(%s, %s)", context, service );
|
||||||
DbgUtils.assertOnUIThread();
|
DbgUtils.assertOnUIThread();
|
||||||
|
@ -625,7 +623,7 @@ public class BTService extends XWService {
|
||||||
s_listener[0] = new BTListenerThread( context, service );
|
s_listener[0] = new BTListenerThread( context, service );
|
||||||
s_listener[0].start();
|
s_listener[0].start();
|
||||||
} else if ( null != service ) {
|
} else if ( null != service ) {
|
||||||
s_listener[0].setService( context, service, helper );
|
s_listener[0].setService( context, service );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -633,7 +631,7 @@ public class BTService extends XWService {
|
||||||
static void startYourself( Context context )
|
static void startYourself( Context context )
|
||||||
{
|
{
|
||||||
DbgUtils.assertOnUIThread();
|
DbgUtils.assertOnUIThread();
|
||||||
startYourself( context, null, null );
|
startYourself( context, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stopYourself( BTListenerThread self )
|
static void stopYourself( BTListenerThread self )
|
||||||
|
@ -655,16 +653,20 @@ public class BTService extends XWService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setService( Context context, BTService service, BTServiceHelper helper )
|
void setService( Context context, BTService service )
|
||||||
{
|
{
|
||||||
if ( null == mService ) {
|
synchronized ( mServiceHolder ) {
|
||||||
Log.d( TAG, "setService(): we didn't have one before. Do something!!!" );
|
if ( null == mServiceHolder[0] ) {
|
||||||
mService = service;
|
Log.d( TAG, "setService(): setting. notifying all!!!" );
|
||||||
mHelper = helper;
|
|
||||||
Assert.assertNotNull( context );
|
Assert.assertNotNull( context );
|
||||||
mContext = context; // Use Service instead of Receiver (possibly)
|
mContext = context; // Use Service instead of Receiver (possibly)
|
||||||
|
mServiceHolder[0] = service;
|
||||||
|
if ( service != null ) {
|
||||||
|
mServiceHolder.notifyAll();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Assert.assertTrue( service == mService );
|
Assert.assertTrue( service == mServiceHolder[0] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,13 +674,18 @@ public class BTService extends XWService {
|
||||||
{
|
{
|
||||||
stopKillTimer();
|
stopKillTimer();
|
||||||
|
|
||||||
if (! inForeground() ) {
|
|
||||||
startService( mContext,
|
startService( mContext,
|
||||||
getIntentTo( mContext,
|
getIntentTo( mContext,
|
||||||
BTAction.START_BACKGROUND ) );
|
BTAction.START_BACKGROUND ) );
|
||||||
} else {
|
}
|
||||||
// Will be a race condition maybe?
|
|
||||||
Assert.assertFalse( BuildConfig.DEBUG );
|
private BTService blockForService() throws InterruptedException
|
||||||
|
{
|
||||||
|
synchronized( mServiceHolder ) {
|
||||||
|
while ( mServiceHolder[0] == null ) {
|
||||||
|
mServiceHolder.wait();
|
||||||
|
}
|
||||||
|
return mServiceHolder[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +701,7 @@ public class BTService extends XWService {
|
||||||
Thread.sleep( 10 * 1000 );
|
Thread.sleep( 10 * 1000 );
|
||||||
stopYourself( BTListenerThread.this );
|
stopYourself( BTListenerThread.this );
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
Log.e( TAG, "timer thread interrupted; exiting" );
|
Log.e( TAG, "kill timer thread exiting; we're live!" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
@ -733,40 +740,16 @@ public class BTService extends XWService {
|
||||||
BTCmd cmd = BTCmd.values()[inStream.readByte()];
|
BTCmd cmd = BTCmd.values()[inStream.readByte()];
|
||||||
Log.d( TAG, "BTListenerThread() got %s", cmd );
|
Log.d( TAG, "BTListenerThread() got %s", cmd );
|
||||||
if ( protoOK( proto, cmd ) ) {
|
if ( protoOK( proto, cmd ) ) {
|
||||||
if ( null == mService ) {
|
if ( null == mServiceHolder[0] ) {
|
||||||
Log.d( TAG, "dropping packet; starting service" );
|
|
||||||
startTheService();
|
startTheService();
|
||||||
} else {
|
|
||||||
switch( cmd ) {
|
|
||||||
case PING:
|
|
||||||
receivePing( socket );
|
|
||||||
break;
|
|
||||||
case INVITE:
|
|
||||||
receiveInvitation( proto, inStream, socket );
|
|
||||||
break;
|
|
||||||
case MESG_SEND:
|
|
||||||
receiveMessage( cmd, inStream, socket );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MESG_GAMEGONE:
|
|
||||||
receiveMessage( cmd, inStream, socket );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Log.e( TAG, "unexpected msg %s", cmd.toString());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mService.updateStatusIn( true );
|
|
||||||
noteLastUsed( mContext );
|
|
||||||
}
|
}
|
||||||
|
processWhenReady( socket, proto, inStream, cmd );
|
||||||
} else {
|
} else {
|
||||||
DataOutputStream os =
|
DataOutputStream os =
|
||||||
new DataOutputStream( socket.getOutputStream() );
|
new DataOutputStream( socket.getOutputStream() );
|
||||||
os.writeByte( BTCmd.BAD_PROTO.ordinal() );
|
os.writeByte( BTCmd.BAD_PROTO.ordinal() );
|
||||||
os.flush();
|
os.flush();
|
||||||
socket.close();
|
socket.close();
|
||||||
|
|
||||||
mService.sendBadProto( socket );
|
|
||||||
}
|
}
|
||||||
} catch ( IOException ioe ) {
|
} catch ( IOException ioe ) {
|
||||||
Log.w( TAG, "trying again..." );
|
Log.w( TAG, "trying again..." );
|
||||||
|
@ -787,6 +770,75 @@ public class BTService extends XWService {
|
||||||
}
|
}
|
||||||
} // run()
|
} // run()
|
||||||
|
|
||||||
|
private static class InPacket {
|
||||||
|
BluetoothSocket mSocket;
|
||||||
|
byte mProto;
|
||||||
|
DataInputStream mInStream;
|
||||||
|
BTCmd mCmd;
|
||||||
|
|
||||||
|
InPacket( BluetoothSocket socket, byte proto,
|
||||||
|
DataInputStream inStream, BTCmd cmd )
|
||||||
|
{
|
||||||
|
mSocket = socket;
|
||||||
|
mProto = proto;
|
||||||
|
mInStream = inStream;
|
||||||
|
mCmd = cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkedBlockingQueue<InPacket> mInQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
private Thread mIgnored;
|
||||||
|
private void processWhenReady( BluetoothSocket socket, byte proto,
|
||||||
|
DataInputStream inStream, BTCmd cmd )
|
||||||
|
{
|
||||||
|
if ( mIgnored == null ) {
|
||||||
|
mIgnored = new Thread( new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
for ( ; ; ) {
|
||||||
|
try {
|
||||||
|
InPacket elem = mInQueue.take();
|
||||||
|
BTService service = blockForService();
|
||||||
|
BTCmd cmd = elem.mCmd; // fired
|
||||||
|
switch( cmd ) {
|
||||||
|
case PING:
|
||||||
|
receivePing( service, elem.mSocket );
|
||||||
|
break;
|
||||||
|
case INVITE:
|
||||||
|
receiveInvitation( service, elem.mProto,
|
||||||
|
elem.mInStream, elem.mSocket );
|
||||||
|
break;
|
||||||
|
case MESG_SEND:
|
||||||
|
receiveMessage( service, cmd, elem.mInStream,
|
||||||
|
elem.mSocket );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MESG_GAMEGONE:
|
||||||
|
receiveMessage( service, cmd, elem.mInStream,
|
||||||
|
elem.mSocket );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log.e( TAG, "unexpected msg %s", cmd.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
service.updateStatusIn( true );
|
||||||
|
noteLastUsed( service );
|
||||||
|
} catch (IOException | InterruptedException ex) {
|
||||||
|
DbgUtils.printStack( TAG, ex );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
mIgnored.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
mInQueue.add( new InPacket( socket, proto, inStream, cmd ) );
|
||||||
|
}
|
||||||
|
|
||||||
public void stopListening()
|
public void stopListening()
|
||||||
{
|
{
|
||||||
if ( null != m_serverSocket ) {
|
if ( null != m_serverSocket ) {
|
||||||
|
@ -806,7 +858,7 @@ public class BTService extends XWService {
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receivePing( BluetoothSocket socket ) throws IOException
|
private void receivePing( BTService service, BluetoothSocket socket ) throws IOException
|
||||||
{
|
{
|
||||||
DataInputStream inStream = new DataInputStream( socket.getInputStream() );
|
DataInputStream inStream = new DataInputStream( socket.getInputStream() );
|
||||||
int gameID = inStream.readInt();
|
int gameID = inStream.readInt();
|
||||||
|
@ -818,11 +870,11 @@ public class BTService extends XWService {
|
||||||
os.flush();
|
os.flush();
|
||||||
|
|
||||||
socket.close();
|
socket.close();
|
||||||
mService.updateStatusOut( true );
|
service.updateStatusOut( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receiveInvitation( byte proto, DataInputStream is,
|
private void receiveInvitation( BTService service, byte proto,
|
||||||
BluetoothSocket socket )
|
DataInputStream is, BluetoothSocket socket )
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
BTCmd result;
|
BTCmd result;
|
||||||
|
@ -838,7 +890,8 @@ public class BTService extends XWService {
|
||||||
}
|
}
|
||||||
|
|
||||||
BluetoothDevice host = socket.getRemoteDevice();
|
BluetoothDevice host = socket.getRemoteDevice();
|
||||||
result = mService.makeOrNotify( nli, host.getName(), host.getAddress() );
|
result = service
|
||||||
|
.makeOrNotify( nli, host.getName(), host.getAddress() );
|
||||||
|
|
||||||
DataOutputStream os = new DataOutputStream( socket.getOutputStream() );
|
DataOutputStream os = new DataOutputStream( socket.getOutputStream() );
|
||||||
os.writeByte( result.ordinal() );
|
os.writeByte( result.ordinal() );
|
||||||
|
@ -847,7 +900,8 @@ public class BTService extends XWService {
|
||||||
socket.close();
|
socket.close();
|
||||||
} // receiveInvitation
|
} // receiveInvitation
|
||||||
|
|
||||||
private void receiveMessage( BTCmd cmd, DataInputStream dis, BluetoothSocket socket )
|
private void receiveMessage( BTService service, BTCmd cmd,
|
||||||
|
DataInputStream dis, BluetoothSocket socket )
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
BTCmd result = null;
|
BTCmd result = null;
|
||||||
|
@ -861,15 +915,15 @@ public class BTService extends XWService {
|
||||||
CommsAddrRec addr = new CommsAddrRec( host.getName(),
|
CommsAddrRec addr = new CommsAddrRec( host.getName(),
|
||||||
host.getAddress() );
|
host.getAddress() );
|
||||||
XWServiceHelper.ReceiveResult rslt
|
XWServiceHelper.ReceiveResult rslt
|
||||||
= mHelper.receiveMessage( mContext,
|
= service.mHelper.receiveMessage( mContext, gameID,
|
||||||
gameID, mService.m_btMsgSink,
|
service.m_btMsgSink,
|
||||||
buffer, addr );
|
buffer, addr );
|
||||||
|
|
||||||
result = rslt == XWServiceHelper.ReceiveResult.GAME_GONE ?
|
result = rslt == XWServiceHelper.ReceiveResult.GAME_GONE ?
|
||||||
BTCmd.MESG_GAMEGONE : BTCmd.MESG_ACCPT;
|
BTCmd.MESG_GAMEGONE : BTCmd.MESG_ACCPT;
|
||||||
break;
|
break;
|
||||||
case MESG_GAMEGONE:
|
case MESG_GAMEGONE:
|
||||||
mHelper.postEvent( MultiEvent.MESSAGE_NOGAME, gameID );
|
service.mHelper.postEvent( MultiEvent.MESSAGE_NOGAME, gameID );
|
||||||
result = BTCmd.MESG_ACCPT;
|
result = BTCmd.MESG_ACCPT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1043,11 +1097,12 @@ public class BTService extends XWService {
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
} catch ( IOException ioe ) {
|
} catch ( IOException ioe ) {
|
||||||
logIOE( ioe );
|
Log.e( TAG, "sendPing() failure; %s", ioe.getMessage() );
|
||||||
|
DbgUtils.printStack( TAG, ioe );
|
||||||
}
|
}
|
||||||
updateStatusOut( sendWorking );
|
updateStatusOut( sendWorking );
|
||||||
updateStatusIn( receiveWorking );
|
updateStatusIn( receiveWorking );
|
||||||
Log.d( TAG, "sendPing(%s) => %b", dev, gotReply );
|
Log.d( TAG, "sendPing(%s) => %b", dev.getName(), gotReply );
|
||||||
return gotReply;
|
return gotReply;
|
||||||
} // sendPing
|
} // sendPing
|
||||||
|
|
||||||
|
@ -1273,7 +1328,7 @@ public class BTService extends XWService {
|
||||||
private void startListener()
|
private void startListener()
|
||||||
{
|
{
|
||||||
Assert.assertNotNull( mHelper );
|
Assert.assertNotNull( mHelper );
|
||||||
BTListenerThread.startYourself( this, this, mHelper );
|
BTListenerThread.startYourself( this, this );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startSender()
|
private void startSender()
|
||||||
|
@ -1314,29 +1369,34 @@ public class BTService extends XWService {
|
||||||
private DataOutputStream connect( BluetoothSocket socket, BTCmd cmd )
|
private DataOutputStream connect( BluetoothSocket socket, BTCmd cmd )
|
||||||
{
|
{
|
||||||
String name = socket.getRemoteDevice().getName();
|
String name = socket.getRemoteDevice().getName();
|
||||||
|
Log.w( TAG, "connect(%s) starting", name );
|
||||||
// DbgUtils.logf( "connecting to %s to send cmd %s", name, cmd.toString() );
|
// DbgUtils.logf( "connecting to %s to send cmd %s", name, cmd.toString() );
|
||||||
// Docs say always call cancelDiscovery before trying to connect
|
// Docs say always call cancelDiscovery before trying to connect
|
||||||
m_adapter.cancelDiscovery();
|
m_adapter.cancelDiscovery();
|
||||||
|
|
||||||
DataOutputStream dos;
|
DataOutputStream dos = null;
|
||||||
try {
|
|
||||||
for ( int ii = 0; ii < 3; ++ii ) {
|
// Try for 8 seconds. Some devices take a long time to get ACL conn
|
||||||
|
// ACTION
|
||||||
|
for ( long end = 10000 + System.currentTimeMillis(); ; ) {
|
||||||
try {
|
try {
|
||||||
socket.connect();
|
socket.connect();
|
||||||
break;
|
Log.i( TAG, "connect(%s) succeeded", name );
|
||||||
} catch (IOException ioe) {
|
|
||||||
Thread.sleep( 2000 );
|
|
||||||
Log.d( TAG, "ioe on connect(); trying again" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dos = new DataOutputStream( socket.getOutputStream() );
|
dos = new DataOutputStream( socket.getOutputStream() );
|
||||||
dos.writeByte( BT_PROTO );
|
dos.writeByte( BT_PROTO );
|
||||||
dos.writeByte( cmd.ordinal() );
|
dos.writeByte( cmd.ordinal() );
|
||||||
Log.i( TAG, "connect() to %s successful", name );
|
break; // success!!!
|
||||||
} catch ( IOException | InterruptedException ioe ) {
|
} catch (IOException ioe) {
|
||||||
dos = null;
|
if ( System.currentTimeMillis() > end ) {
|
||||||
Log.w( TAG, "BTService.connect() to %s failed", name );
|
break;
|
||||||
// logIOE( ioe );
|
}
|
||||||
|
Log.d( TAG, "connect(%s) trying again", name );
|
||||||
|
try {
|
||||||
|
Thread.sleep( 1000 );
|
||||||
|
} catch ( InterruptedException ex ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return dos;
|
return dos;
|
||||||
}
|
}
|
||||||
|
@ -1374,7 +1434,6 @@ public class BTService extends XWService {
|
||||||
mHandler.postAtTime( new Runnable() {
|
mHandler.postAtTime( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.d( TAG, "timeout timer fired" );
|
|
||||||
setTimeoutTimer();
|
setTimeoutTimer();
|
||||||
}
|
}
|
||||||
}, this, dieTimeMillis );
|
}, this, dieTimeMillis );
|
||||||
|
|
Loading…
Reference in a new issue