factor upward code from BTEvent so that SMSService can use same

mechanism to send asynchronous results, then use to notify when a game
on other end has been deleted.  This flushed out a bug where a rowid
will get reused and a new game wind up with data from the previous
one -- but otherwise works.
This commit is contained in:
Eric House 2012-04-06 23:00:47 -07:00
parent d9ba0c4229
commit d6a3aafaec
12 changed files with 177 additions and 78 deletions

View file

@ -54,3 +54,4 @@ XWListActivity.java
XWListAdapter.java
XWListItem.java
XWListPreference.java
MultiService.java

View file

@ -59,7 +59,7 @@ public class BTInviteActivity extends InviteActivity
// BTService.BTEventListener interface
@Override
public void eventOccurred( BTService.BTEvent event, final Object ... args )
public void eventOccurred( MultiService.MultiEvent event, final Object ... args )
{
switch( event ) {
case SCAN_DONE:

View file

@ -46,30 +46,12 @@ import java.util.concurrent.TimeUnit;
import junit.framework.Assert;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommonPrefs;
import org.eehouse.android.xw4.jni.CommsAddrRec;
public class BTService extends Service {
public enum BTEvent { BAD_PROTO
, BT_ENABLED
, BT_DISABLED
, SCAN_DONE
, HOST_PONGED
, NEWGAME_SUCCESS
, NEWGAME_FAILURE
, MESSAGE_ACCEPTED
, MESSAGE_REFUSED
, MESSAGE_NOGAME
, MESSAGE_RESEND
, MESSAGE_FAILOUT
, MESSAGE_DROPPED
};
public interface BTEventListener {
public void eventOccurred( BTEvent event, Object ... args );
}
private static final long RESEND_TIMEOUT = 5; // seconds
private static final int MAX_SEND_FAIL = 3;
@ -96,8 +78,7 @@ public class BTService extends Service {
private static final String NTO_STR = "TOT";
private static final String NHE_STR = "HER";
private static BTEventListener s_eventListener = null;
private static Object s_syncObj = new Object();
private static MultiService s_srcMgr = new MultiService();
private enum BTCmd {
PING,
@ -167,10 +148,9 @@ public class BTService extends Service {
}
}
public static void setBTEventListener( BTEventListener li ) {
synchronized( s_syncObj ) {
s_eventListener = li;
}
public static MultiService getMultiEventSrc()
{
return s_srcMgr;
}
public static void radioChanged( Context context, boolean cameOn )
@ -306,7 +286,8 @@ public class BTService extends Service {
break;
case RADIO:
boolean cameOn = intent.getBooleanExtra( RADIO_STR, false );
BTEvent evt = cameOn? BTEvent.BT_ENABLED : BTEvent.BT_DISABLED;
MultiEvent evt = cameOn? MultiEvent.BT_ENABLED
: MultiEvent.BT_DISABLED;
sendResult( evt );
if ( !cameOn ) {
stopListener();
@ -368,7 +349,7 @@ public class BTService extends Service {
byte proto = inStream.readByte();
if ( proto != BT_PROTO ) {
socket.close();
sendResult( BTEvent.BAD_PROTO,
sendResult( MultiEvent.BAD_PROTO,
socket.getRemoteDevice().getName() );
} else {
byte msg = inStream.readByte();
@ -633,7 +614,7 @@ public class BTService extends Service {
switch( elem.m_cmd ) {
case PING:
sendPings( BTEvent.HOST_PONGED );
sendPings( MultiEvent.HOST_PONGED );
break;
case SCAN:
sendPings( null );
@ -656,7 +637,7 @@ public class BTService extends Service {
}
} // run
private void sendPings( BTEvent event )
private void sendPings( MultiEvent event )
{
Set<BluetoothDevice> pairedDevs = m_adapter.getBondedDevices();
DbgUtils.logf( "ping: got %d paired devices", pairedDevs.size() );
@ -725,8 +706,8 @@ public class BTService extends Service {
}
socket.close();
BTEvent evt = success ? BTEvent.NEWGAME_SUCCESS
: BTEvent.NEWGAME_FAILURE;
MultiEvent evt = success ? MultiEvent.NEWGAME_SUCCESS
: MultiEvent.NEWGAME_FAILURE;
sendResult( evt, elem.m_gameID );
}
} catch ( java.io.IOException ioe ) {
@ -740,13 +721,13 @@ public class BTService extends Service {
synchronized( m_deadGames ) {
success = m_deadGames.contains( elem.m_gameID );
}
BTEvent evt;
MultiEvent evt;
if ( success ) {
evt = BTEvent.MESSAGE_DROPPED;
evt = MultiEvent.MESSAGE_DROPPED;
DbgUtils.logf( "dropping message because game %X dead",
elem.m_gameID );
} else {
evt = BTEvent.MESSAGE_REFUSED;
evt = MultiEvent.MESSAGE_REFUSED;
}
if ( !success ) {
try {
@ -775,10 +756,10 @@ public class BTService extends Service {
switch ( reply ) {
case MESG_ACCPT:
evt = BTEvent.MESSAGE_ACCEPTED;
evt = MultiEvent.MESSAGE_ACCEPTED;
break;
case MESG_GAMEGONE:
evt = BTEvent.MESSAGE_NOGAME;
evt = MultiEvent.MESSAGE_NOGAME;
break;
}
}
@ -793,7 +774,7 @@ public class BTService extends Service {
sendResult( evt, elem.m_gameID, 0, elem.m_recipient );
if ( ! success ) {
int failCount = elem.incrFailCount();
sendResult( BTEvent.MESSAGE_RESEND, elem.m_recipient,
sendResult( MultiEvent.MESSAGE_RESEND, elem.m_recipient,
RESEND_TIMEOUT, failCount );
}
return success;
@ -811,7 +792,7 @@ public class BTService extends Service {
if ( success ) {
iter.remove();
} else if ( elem.failCountExceeded() ) {
sendResult( BTEvent.MESSAGE_FAILOUT, elem.m_recipient );
sendResult( MultiEvent.MESSAGE_FAILOUT, elem.m_recipient );
iter.remove();
}
}
@ -862,17 +843,12 @@ public class BTService extends Service {
private void sendNames()
{
sendResult( BTEvent.SCAN_DONE, (Object)(names()) );
sendResult( MultiEvent.SCAN_DONE, (Object)(names()) );
}
private void sendResult( BTEvent event, Object ... args )
private void sendResult( MultiEvent event, Object ... args )
{
Assert.assertNotNull( event );
synchronized( s_syncObj ) {
if ( null != s_eventListener ) {
s_eventListener.eventOccurred( event, args );
}
}
s_srcMgr.sendResult( event, args );
}
private void listLocalBTGames( boolean force )

View file

@ -821,22 +821,26 @@ public class BoardActivity extends XWActivity
//////////////////////////////////////////////////
@Override
@SuppressWarnings("fallthrough")
public void eventOccurred( BTService.BTEvent event, final Object ... args )
public void eventOccurred( MultiService.MultiEvent event, final Object ... args )
{
switch( event ) {
case MESSAGE_ACCEPTED:
case MESSAGE_REFUSED:
if ( null != m_jniThread ) {
boolean accepted = BTService.BTEvent.MESSAGE_ACCEPTED == event;
boolean accepted =
MultiService.MultiEvent.MESSAGE_ACCEPTED == event;
m_jniThread.handle( JNICmd.CMD_DRAW_BT_STATUS, accepted );
}
break;
case MESSAGE_NOGAME:
post( new Runnable() {
public void run() {
showDialog( DLG_DELETED );
}
} );
int gameID = (Integer)args[0];
if ( gameID == m_gi.gameID ) {
post( new Runnable() {
public void run() {
showDialog( DLG_DELETED );
}
} );
}
break;
case NEWGAME_FAILURE:
DbgUtils.logf( "failed to create game" );

View file

@ -267,7 +267,7 @@ public class DlgDelegate {
return true;
}
public void eventOccurred( BTService.BTEvent event, final Object ... args )
public void eventOccurred( MultiService.MultiEvent event, final Object ... args )
{
String msg = null;
boolean asToast = true;

View file

@ -283,6 +283,7 @@ public class GameUtils {
public static void deleteGame( Context context, long rowid,
boolean informNow )
{
DbgUtils.logf( "deleteGame(rowid=%d)", rowid );
// does this need to be synchronized?
GameLock lock = new GameLock( rowid, true );
if ( lock.tryLock() ) {
@ -839,11 +840,16 @@ public class GameUtils {
boolean informNow )
{
GameSummary summary = DBUtils.getSummary( context, lock );
if ( null != summary.relayID ) {
switch( summary.conType ) {
case COMMS_CONN_RELAY:
tellRelayDied( context, summary, informNow );
}
if ( 0 != summary.gameID ) {
break;
case COMMS_CONN_BT:
BTService.gameDied( context, summary.gameID );
break;
case COMMS_CONN_SMS:
SMSService.gameDied( context, summary.gameID, summary.smsPhone );
break;
}
}

View file

@ -405,7 +405,8 @@ public class GamesList extends XWListActivity
// BTService.BTEventListener interface
@Override
public void eventOccurred( BTService.BTEvent event, final Object ... args )
public void eventOccurred( MultiService.MultiEvent event,
final Object ... args )
{
switch( event ) {
case HOST_PONGED:

View file

@ -0,0 +1,65 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2009 - 2012 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.eehouse.android.xw4;
public class MultiService {
private BTEventListener m_li;
public enum MultiEvent { BAD_PROTO
, BT_ENABLED
, BT_DISABLED
, SCAN_DONE
, HOST_PONGED
, NEWGAME_SUCCESS
, NEWGAME_FAILURE
, MESSAGE_ACCEPTED
, MESSAGE_REFUSED
, MESSAGE_NOGAME
, MESSAGE_RESEND
, MESSAGE_FAILOUT
, MESSAGE_DROPPED
};
public interface BTEventListener {
public void eventOccurred( MultiEvent event, Object ... args );
}
// public interface MultiEventSrc {
// public void setBTEventListener( BTEventListener li );
// }
public void setListener( BTEventListener li )
{
synchronized( this ) {
m_li = li;
}
}
public void sendResult( MultiEvent event, Object ... args )
{
synchronized( this ) {
if ( null != m_li ) {
m_li.eventOccurred( event, args );
}
}
}
}

View file

@ -249,7 +249,8 @@ public class NewGameActivity extends XWActivity {
// BTService.BTEventListener interface
@Override
public void eventOccurred( BTService.BTEvent event, final Object ... args )
public void eventOccurred( MultiService.MultiEvent event,
final Object ... args )
{
switch( event ) {
case BT_ENABLED:

View file

@ -40,6 +40,7 @@ import java.util.Arrays;
import java.util.HashMap;
import junit.framework.Assert;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.XwJNI;
@ -53,6 +54,8 @@ public class SMSService extends Service {
private static final int HANDLE = 1;
private static final int INVITE = 2;
private static final int SEND = 3;
private static final int REMOVE = 4;
private static final int MESG_GAMEGONE = 5;
private static final String CMD_STR = "CMD";
private static final String BUFFER = "BUFFER";
@ -65,10 +68,11 @@ public class SMSService extends Service {
private static final String NPLAYERSH = "NPLAYERSH";
private static Boolean s_showToasts = null;
private static MultiService s_srcMgr = new MultiService();
// All messages are base64-encoded byte arrays. The first byte is
// always one of these. What follows depends.
private enum SMS_CMD { NONE, INVITE, DATA, };
private enum SMS_CMD { NONE, INVITE, DATA, DEATH, };
private int m_nReceived = 0;
private static int s_nSent = 0;
@ -116,6 +120,14 @@ public class SMSService extends Service {
return binmsg.length;
}
public static void gameDied( Context context, int gameID, String phone )
{
Intent intent = getIntentTo( context, REMOVE );
intent.putExtra( PHONE, phone );
intent.putExtra( GAMEID, gameID );
context.startService( intent );
}
public static String toPublicFmt( String msg )
{
String result;
@ -139,6 +151,11 @@ public class SMSService extends Service {
return result;
}
public static MultiService getMultiEventSrc()
{
return s_srcMgr;
}
private static Intent getIntentTo( Context context, int cmd )
{
if ( null == s_showToasts ) {
@ -192,6 +209,11 @@ public class SMSService extends Service {
gameID = intent.getIntExtra( GAMEID, -1 );
sendPacket( phone, gameID, bytes );
break;
case REMOVE:
gameID = intent.getIntExtra( GAMEID, -1 );
phone = intent.getStringExtra( PHONE );
sendDiedPacket( phone, gameID );
break;
}
result = Service.START_STICKY;
@ -226,6 +248,19 @@ public class SMSService extends Service {
}
}
private void sendDiedPacket( String phone, int gameID )
{
ByteArrayOutputStream bas = new ByteArrayOutputStream( 32 );
DataOutputStream das = new DataOutputStream( bas );
try {
das.writeInt( gameID );
das.flush();
send( SMS_CMD.DEATH, bas.toByteArray(), phone );
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
}
public int sendPacket( String phone, int gameID, byte[] bytes )
{
DbgUtils.logf( "non-static SMSService.sendPacket()" );
@ -321,6 +356,10 @@ public class SMSService extends Service {
dis.read( rest );
feedMessage( gameID, rest, addr );
break;
case DEATH:
gameID = dis.readInt();
s_srcMgr.sendResult( MultiEvent.MESSAGE_NOGAME, gameID );
break;
default:
Assert.fail();
break;
@ -438,17 +477,17 @@ public class SMSService extends Service {
private void feedMessage( int gameID, byte[] msg, CommsAddrRec addr )
{
if ( BoardActivity.feedMessage( gameID, msg, addr ) ) {
long rowid = DBUtils.getRowIDFor( this, gameID );
if ( DBUtils.ROWID_NOTFOUND == rowid ) {
sendDiedPacket( addr.sms_phone, gameID );
} else if ( BoardActivity.feedMessage( gameID, msg, addr ) ) {
// do nothing
} else {
long rowid = DBUtils.getRowIDFor( this, gameID );
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
SMSMsgSink sink = new SMSMsgSink( this );
if ( GameUtils.feedMessage( this, rowid, msg, addr, sink ) ) {
postNotification( gameID, R.string.new_smsmove_title,
getString(R.string.new_move_body)
);
}
SMSMsgSink sink = new SMSMsgSink( this );
if ( GameUtils.feedMessage( this, rowid, msg, addr, sink ) ) {
postNotification( gameID, R.string.new_smsmove_title,
getString(R.string.new_move_body)
);
}
}
}

View file

@ -33,7 +33,7 @@ import junit.framework.Assert;
import org.eehouse.android.xw4.jni.CommonPrefs;
public class XWActivity extends Activity
implements DlgDelegate.DlgClickNotify, BTService.BTEventListener {
implements DlgDelegate.DlgClickNotify, MultiService.BTEventListener {
private DlgDelegate m_delegate;
@ -57,7 +57,8 @@ public class XWActivity extends Activity
protected void onResume()
{
DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this );
BTService.setBTEventListener( this );
BTService.getMultiEventSrc().setListener( this );
SMSService.getMultiEventSrc().setListener( this );
super.onResume();
}
@ -65,7 +66,8 @@ public class XWActivity extends Activity
protected void onPause()
{
DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this );
BTService.setBTEventListener( null );
BTService.getMultiEventSrc().setListener( null );
SMSService.getMultiEventSrc().setListener( null );
super.onPause();
}
@ -180,7 +182,8 @@ public class XWActivity extends Activity
}
// BTService.BTEventListener interface
public void eventOccurred( BTService.BTEvent event, final Object ... args )
public void eventOccurred( MultiService.MultiEvent event,
final Object ... args )
{
m_delegate.eventOccurred( event, args );
}

View file

@ -30,7 +30,7 @@ import junit.framework.Assert;
import org.eehouse.android.xw4.jni.CommonPrefs;
public class XWListActivity extends ListActivity
implements DlgDelegate.DlgClickNotify, BTService.BTEventListener {
implements DlgDelegate.DlgClickNotify, MultiService.BTEventListener {
private DlgDelegate m_delegate;
@ -54,7 +54,8 @@ public class XWListActivity extends ListActivity
protected void onResume()
{
DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this );
BTService.setBTEventListener( this );
BTService.getMultiEventSrc().setListener( this );
SMSService.getMultiEventSrc().setListener( this );
super.onResume();
}
@ -62,7 +63,8 @@ public class XWListActivity extends ListActivity
protected void onPause()
{
DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this );
BTService.setBTEventListener( null );
BTService.getMultiEventSrc().setListener( null );
SMSService.getMultiEventSrc().setListener( null );
super.onPause();
}
@ -191,7 +193,8 @@ public class XWListActivity extends ListActivity
}
// BTService.BTEventListener interface
public void eventOccurred( BTService.BTEvent event, final Object ... args )
public void eventOccurred( MultiService.MultiEvent event,
final Object ... args )
{
m_delegate.eventOccurred( event, args );
}