mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-27 09:58:45 +01:00
Replace most of NBSReceiver with new NBSService. Route outgoing (from
comms) and incoming (from NBSReceiver) messages through it. Since some messages are too big, add header/protocol for breaking them up and reassemble. With this commit complete robot-vs-robot games are possible between two very new Samsung phones on T-mobile. Older phones and other networks not tested.
This commit is contained in:
parent
8e3d8c20c1
commit
599d01bfc7
11 changed files with 520 additions and 217 deletions
|
@ -36,7 +36,7 @@
|
|||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" />
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="8" />
|
||||
|
||||
<application android:icon="@drawable/icon48x48"
|
||||
android:label="@string/app_name"
|
||||
|
@ -170,5 +170,7 @@
|
|||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service android:name="NBSService"/>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -52,3 +52,4 @@ XWListPreference.java
|
|||
GameNamer.java
|
||||
LookupActivity.java
|
||||
NBSInviteActivity.java
|
||||
NBSService.java
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
<!-- <uses-permission android:name="android.permission.RECEIVE_SMS" /> -->
|
||||
<!-- <uses-permission android:name="android.permission.SEND_SMS" /> -->
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="7" />
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="8" />
|
||||
|
||||
<application android:icon="@drawable/icon48x48"
|
||||
android:label="@string/app_name"
|
||||
|
|
|
@ -10,4 +10,4 @@
|
|||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=android-7
|
||||
target=android-8
|
||||
|
|
|
@ -1855,7 +1855,7 @@
|
|||
Bluetooth play.</string>
|
||||
|
||||
<string name="new_btmove_title">New move via Bluetooth</string>
|
||||
<string name="new_btmove_body">One or more moves has arrived</string>
|
||||
<string name="new_move_body">One or more moves has arrived</string>
|
||||
|
||||
<string name="bt_devs_missing">Missing connections? Invite BT
|
||||
devs? (I won\'t ask again until you reopen this game.)</string>
|
||||
|
|
|
@ -511,7 +511,7 @@ public class BTService extends Service {
|
|||
buffer, addr,
|
||||
m_btMsgSink ) ) {
|
||||
postNotification( gameID, R.string.new_btmove_title,
|
||||
R.string.new_btmove_body );
|
||||
R.string.new_move_body );
|
||||
// do nothing
|
||||
} else {
|
||||
DbgUtils.logf( "nobody took msg for gameID %X",
|
||||
|
|
|
@ -1736,9 +1736,9 @@ public class BoardActivity extends XWActivity
|
|||
m_gi.nPlayers, 1 );
|
||||
break;
|
||||
case COMMS_CONN_SMS:
|
||||
NBSReceiver.inviteRemote( this, dev, m_gi.gameID,
|
||||
gameName, m_gi.dictLang,
|
||||
m_gi.nPlayers, 1 );
|
||||
NBSService.inviteRemote( this, dev, m_gi.gameID,
|
||||
gameName, m_gi.dictLang,
|
||||
m_gi.nPlayers, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -235,6 +235,7 @@ public class CommsTransport implements TransportProcs,
|
|||
// // Let other know I'm here
|
||||
// m_jniThread.handle( JNIThread.JNICmd.CMD_RESEND );
|
||||
// break;
|
||||
// case COMMS_CONN_SMS:
|
||||
// default:
|
||||
// DbgUtils.logf( "tickle: unexpected type %s",
|
||||
// addr.conType.toString() );
|
||||
|
@ -379,33 +380,10 @@ public class CommsTransport implements TransportProcs,
|
|||
}
|
||||
break;
|
||||
case COMMS_CONN_SMS:
|
||||
Assert.fail();
|
||||
// DbgUtils.logf( "sending via sms to %s:%d",
|
||||
// m_addr.sms_phone, m_addr.sms_port );
|
||||
// try {
|
||||
// Intent intent = new Intent( m_context, StatusReceiver.class);
|
||||
// PendingIntent pi
|
||||
// = PendingIntent.getBroadcast( m_context, 0,
|
||||
// intent, 0 );
|
||||
// if ( 0 == m_addr.sms_port ) {
|
||||
// SmsManager.getDefault().sendTextMessage( m_addr.sms_phone,
|
||||
// null, "Hello world",
|
||||
// pi, pi );
|
||||
// DbgUtils.logf( "called sendTextMessage" );
|
||||
// } else {
|
||||
// SmsManager.getDefault().
|
||||
// sendDataMessage( m_addr.sms_phone, (String)null,
|
||||
// (short)m_addr.sms_port,
|
||||
// buf, pi, pi );
|
||||
// DbgUtils.logf( "called sendDataMessage" );
|
||||
// }
|
||||
// nSent = buf.length;
|
||||
// } catch ( java.lang.IllegalArgumentException iae ) {
|
||||
// DbgUtils.logf( iae.toString() );
|
||||
// }
|
||||
nSent = NBSService.sendPacket( m_context, addr.sms_phone,
|
||||
gameID, buf );
|
||||
break;
|
||||
case COMMS_CONN_BT:
|
||||
String hostName = addr.bt_hostName;
|
||||
nSent = BTService.enqueueFor( m_context, buf, addr.bt_hostName,
|
||||
addr.bt_btAddr, gameID );
|
||||
break;
|
||||
|
|
|
@ -20,39 +20,20 @@
|
|||
|
||||
package org.eehouse.android.xw4;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.SmsManager;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.util.Base64;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
|
||||
public class NBSReceiver extends BroadcastReceiver {
|
||||
|
||||
// All messages are base64-encoded byte arrays. The first byte is
|
||||
// always one of these. What follows depends.
|
||||
private enum NBS_CMS { NBS_CMD_NONE,
|
||||
NBS_CMD_INVITE,
|
||||
NBS_CMD_DATA,
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onReceive( Context context, Intent intent )
|
||||
{
|
||||
DbgUtils.logf( "NBSReceiver::onReceive(intent=%s)!!!!",
|
||||
intent.toString() );
|
||||
DbgUtils.logf( "NBSReceiver::onReceive()" );
|
||||
|
||||
Bundle bundle = intent.getExtras();
|
||||
if ( null != bundle ) {
|
||||
|
@ -61,167 +42,12 @@ public class NBSReceiver extends BroadcastReceiver {
|
|||
|
||||
for ( int ii = 0; ii < nbses.length; ++ii ) {
|
||||
nbses[ii] = SmsMessage.createFromPdu((byte[])pdus[ii]);
|
||||
receiveBuffer( context, nbses[ii].getUserData(),
|
||||
nbses[ii].getOriginatingAddress() );
|
||||
String phone = nbses[ii].getOriginatingAddress();
|
||||
DbgUtils.logf( "NBSReceiver: post format: %s", phone );
|
||||
NBSService.handleFrom( context, nbses[ii].getUserData(),
|
||||
phone );
|
||||
}
|
||||
}
|
||||
DbgUtils.logf( "onReceive done" );
|
||||
}
|
||||
|
||||
static void tryNBSMessage( Context context, String phoneNo )
|
||||
{
|
||||
byte[] data = { 'a', 'b', 'c' };
|
||||
|
||||
SmsManager mgr = SmsManager.getDefault();
|
||||
|
||||
try {
|
||||
/* online comment says providing PendingIntents prevents
|
||||
random crashes */
|
||||
PendingIntent sent = PendingIntent.getBroadcast( context,
|
||||
0, new Intent(), 0 );
|
||||
PendingIntent dlvrd = PendingIntent.getBroadcast( context, 0,
|
||||
new Intent(), 0 );
|
||||
|
||||
mgr.sendDataMessage( phoneNo, null, (short)50009,
|
||||
data, sent, dlvrd );
|
||||
// PendingIntent sentIntent,
|
||||
// PendingIntent deliveryIntent );
|
||||
DbgUtils.logf( "sendDataMessage finished" );
|
||||
} catch ( IllegalArgumentException iae ) {
|
||||
DbgUtils.logf( "%s", iae.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
public static void inviteRemote( Context context, String phone,
|
||||
int gameID, String gameName,
|
||||
int lang, int nPlayersT,
|
||||
int nPlayersH )
|
||||
{
|
||||
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
|
||||
DataOutputStream das = new DataOutputStream( bas );
|
||||
try {
|
||||
das.writeInt( gameID );
|
||||
das.writeUTF( gameName );
|
||||
das.writeInt( lang );
|
||||
das.writeByte( nPlayersT );
|
||||
das.writeByte( nPlayersH );
|
||||
das.flush();
|
||||
|
||||
send( context, NBS_CMS.NBS_CMD_INVITE, bas.toByteArray(), phone );
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.logf( "ioe: %s", ioe.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
private static void send( Context context, NBS_CMS cmd,
|
||||
byte[] data, String phone )
|
||||
{
|
||||
int hash = Arrays.hashCode( data );
|
||||
DbgUtils.logf( "NBSReceiver: outgoing hash on %d bytes: %X",
|
||||
data.length, hash );
|
||||
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
|
||||
DataOutputStream das = new DataOutputStream( bas );
|
||||
try {
|
||||
das.writeByte( cmd.ordinal() );
|
||||
das.writeInt( hash );
|
||||
das.write( data, 0, data.length );
|
||||
das.flush();
|
||||
|
||||
byte[] as64 = Base64.encode( bas.toByteArray(), Base64.NO_WRAP );
|
||||
sendBuffer( context, as64, phone );
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.logf( "ioe: %s", ioe.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
private void receive( Context context, NBS_CMS cmd,
|
||||
byte[] data, String phone )
|
||||
{
|
||||
switch( cmd ) {
|
||||
case NBS_CMD_INVITE:
|
||||
DataInputStream dis =
|
||||
new DataInputStream( new ByteArrayInputStream(data) );
|
||||
try {
|
||||
int gameID = dis.readInt();
|
||||
String gameName = dis.readUTF();
|
||||
int lang = dis.readInt();
|
||||
int nPlayersT = dis.readByte();
|
||||
int nPlayersH = dis.readByte();
|
||||
|
||||
CommsAddrRec addr = new CommsAddrRec( phone );
|
||||
long rowid = GameUtils.makeNewNBSGame( context, gameID, addr,
|
||||
lang, nPlayersT, nPlayersH );
|
||||
|
||||
if ( null != gameName && 0 < gameName.length() ) {
|
||||
DBUtils.setName( context, rowid, gameName );
|
||||
}
|
||||
String body = Utils.format( context, R.string.new_nbs_bodyf, phone );
|
||||
postNotification( context, gameID, R.string.new_nbs_title, body );
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.logf( "ioe: %s", ioe.toString() );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Assert.fail();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void receiveBuffer( Context context, byte[] as64, String senderPhone )
|
||||
{
|
||||
byte[] data = Base64.decode( as64, Base64.NO_WRAP );
|
||||
DataInputStream dis =
|
||||
new DataInputStream( new ByteArrayInputStream(data) );
|
||||
try {
|
||||
NBS_CMS cmd = NBS_CMS.values()[dis.readByte()];
|
||||
int hashRead = dis.readInt();
|
||||
DbgUtils.logf( "NBSReceiver: incoming hash: %X", hashRead );
|
||||
byte[] rest = new byte[dis.available()];
|
||||
dis.read( rest );
|
||||
int hashComputed = Arrays.hashCode( rest );
|
||||
if ( hashComputed == hashRead ) {
|
||||
DbgUtils.logf( "NBSReceiver: incoming hashes on %d bytes match: %X",
|
||||
rest.length, hashRead );
|
||||
receive( context, cmd, rest, senderPhone );
|
||||
} else {
|
||||
DbgUtils.logf( "NBSReceiver: incoming hashes on %d bytes "
|
||||
+ "DON'T match: read: %X; figured: %X",
|
||||
rest.length, hashRead, hashComputed );
|
||||
}
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.logf( "ioe: %s", ioe.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendBuffer( Context context, byte[] data, String phone )
|
||||
{
|
||||
if ( XWApp.onEmulator() ) {
|
||||
DbgUtils.logf( "sendBuffer(phone=%s): FAKING IT", phone );
|
||||
} else {
|
||||
try {
|
||||
PendingIntent sent =
|
||||
PendingIntent.getBroadcast( context, 0, new Intent(), 0 );
|
||||
PendingIntent dlvrd =
|
||||
PendingIntent.getBroadcast( context, 0, new Intent(), 0 );
|
||||
|
||||
SmsManager mgr = SmsManager.getDefault();
|
||||
mgr.sendDataMessage( phone, null, XWApp.getNBSPort(),
|
||||
data, sent, dlvrd );
|
||||
// PendingIntent sentIntent,
|
||||
// PendingIntent deliveryIntent );
|
||||
DbgUtils.logf( "send to %s finished", phone );
|
||||
} catch ( IllegalArgumentException iae ) {
|
||||
DbgUtils.logf( "%s", iae.toString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void postNotification( Context context, int gameID, int title,
|
||||
String body )
|
||||
{
|
||||
Intent intent = new Intent( context, DispatchNotify.class );
|
||||
intent.putExtra( DispatchNotify.GAMEID_EXTRA, gameID );
|
||||
Utils.postNotification( context, intent, R.string.new_nbsmove_title,
|
||||
body );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,492 @@
|
|||
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
|
||||
/*
|
||||
* Copyright 2010 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;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.telephony.SmsManager;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.util.Base64;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.lang.System;
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
|
||||
public class NBSService extends Service {
|
||||
|
||||
private static final int MAX_LEN_TEXT = 100;
|
||||
private static final int HANDLE = 1;
|
||||
private static final int INVITE = 2;
|
||||
private static final int SEND = 3;
|
||||
|
||||
private static final String CMD_STR = "CMD";
|
||||
private static final String BUFFER = "BUFFER";
|
||||
private static final String PHONE = "PHONE";
|
||||
private static final String GAMEID = "GAMEID";
|
||||
private static final String GAMENAME = "GAMENAME";
|
||||
private static final String LANG = "LANG";
|
||||
private static final String NPLAYERST = "NPLAYERST";
|
||||
private static final String NPLAYERSH = "NPLAYERSH";
|
||||
|
||||
// All messages are base64-encoded byte arrays. The first byte is
|
||||
// always one of these. What follows depends.
|
||||
private enum NBS_CMD { NONE, INVITE, DATA, };
|
||||
|
||||
private int m_nReceived = 0;
|
||||
private static int s_nSent = 0;
|
||||
private static HashMap<String, HashMap <Integer, MsgStore>> s_partialMsgs
|
||||
= new HashMap<String, HashMap <Integer, MsgStore>>();
|
||||
|
||||
public static void handleFrom( Context context, byte[] buffer, String phone )
|
||||
{
|
||||
Intent intent = getIntentTo( context, HANDLE );
|
||||
intent.putExtra( BUFFER, buffer );
|
||||
intent.putExtra( PHONE, phone );
|
||||
context.startService( intent );
|
||||
}
|
||||
|
||||
public static void inviteRemote( Context context, String phone,
|
||||
int gameID, String gameName,
|
||||
int lang, int nPlayersT,
|
||||
int nPlayersH )
|
||||
{
|
||||
DbgUtils.logf( "inviteRemote(%s)", phone );
|
||||
Intent intent = getIntentTo( context, INVITE );
|
||||
intent.putExtra( PHONE, phone );
|
||||
intent.putExtra( GAMEID, gameID );
|
||||
intent.putExtra( GAMENAME, gameName );
|
||||
intent.putExtra( LANG, lang );
|
||||
intent.putExtra( NPLAYERST, nPlayersT );
|
||||
intent.putExtra( NPLAYERSH, nPlayersH );
|
||||
context.startService( intent );
|
||||
}
|
||||
|
||||
public static int sendPacket( Context context, String phone,
|
||||
int gameID, byte[] buffer )
|
||||
{
|
||||
DbgUtils.logf( "NBSService.sendPacket()" );
|
||||
Intent intent = getIntentTo( context, SEND );
|
||||
intent.putExtra( PHONE, phone );
|
||||
intent.putExtra( GAMEID, gameID );
|
||||
intent.putExtra( BUFFER, buffer );
|
||||
context.startService( intent );
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
private static Intent getIntentTo( Context context, int cmd )
|
||||
{
|
||||
Intent intent = new Intent( context, NBSService.class );
|
||||
intent.putExtra( CMD_STR, cmd );
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
if ( XWApp.NBSSUPPORTED ) {
|
||||
} else {
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand( Intent intent, int flags, int startId )
|
||||
{
|
||||
int result;
|
||||
if ( XWApp.NBSSUPPORTED ) {
|
||||
int cmd = intent.getIntExtra( CMD_STR, -1 );
|
||||
switch( cmd ) {
|
||||
case HANDLE:
|
||||
DbgUtils.showf( this, "got %dth nbs", ++m_nReceived );
|
||||
byte[] buffer = intent.getByteArrayExtra( BUFFER );
|
||||
String phone = intent.getStringExtra( PHONE );
|
||||
receiveBuffer( buffer, phone );
|
||||
break;
|
||||
case INVITE:
|
||||
phone = intent.getStringExtra( PHONE );
|
||||
DbgUtils.logf( "INVITE(%s)", phone );
|
||||
int gameID = intent.getIntExtra( GAMEID, -1 );
|
||||
String gameName = intent.getStringExtra( GAMENAME );
|
||||
int lang = intent.getIntExtra( LANG, -1 );
|
||||
int nPlayersT = intent.getIntExtra( NPLAYERST, -1 );
|
||||
int nPlayersH = intent.getIntExtra( NPLAYERSH, -1 );
|
||||
inviteRemote( phone, gameID, gameName, lang, nPlayersT,
|
||||
nPlayersH);
|
||||
break;
|
||||
case SEND:
|
||||
phone = intent.getStringExtra( PHONE );
|
||||
buffer = intent.getByteArrayExtra( BUFFER );
|
||||
gameID = intent.getIntExtra( GAMEID, -1 );
|
||||
sendPacket( phone, gameID, buffer );
|
||||
break;
|
||||
}
|
||||
|
||||
result = Service.START_STICKY;
|
||||
} else {
|
||||
result = Service.START_STICKY_COMPATIBILITY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind( Intent intent )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private void inviteRemote( String phone, int gameID, String gameName,
|
||||
int lang, int nPlayersT, int nPlayersH )
|
||||
{
|
||||
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
|
||||
DataOutputStream das = new DataOutputStream( bas );
|
||||
try {
|
||||
das.writeInt( gameID );
|
||||
das.writeUTF( gameName );
|
||||
das.writeInt( lang );
|
||||
das.writeByte( nPlayersT );
|
||||
das.writeByte( nPlayersH );
|
||||
das.flush();
|
||||
|
||||
send( NBS_CMD.INVITE, bas.toByteArray(), phone );
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.logf( "ioe: %s", ioe.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
public int sendPacket( String phone, int gameID, byte[] buf )
|
||||
{
|
||||
DbgUtils.logf( "non-static NBSService.sendPacket()" );
|
||||
int nSent = -1;
|
||||
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
|
||||
DataOutputStream das = new DataOutputStream( bas );
|
||||
try {
|
||||
das.writeInt( gameID );
|
||||
das.write( buf, 0, buf.length );
|
||||
das.flush();
|
||||
if ( send( NBS_CMD.DATA, bas.toByteArray(), phone ) ) {
|
||||
nSent = buf.length;
|
||||
}
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.logf( "ioe: %s", ioe.toString() );
|
||||
}
|
||||
return nSent;
|
||||
}
|
||||
|
||||
private boolean send( NBS_CMD cmd, byte[] data, String phone )
|
||||
throws java.io.IOException
|
||||
{
|
||||
DbgUtils.logf( "non-static NBSService.sendPacket()" );
|
||||
int hash = Arrays.hashCode( data );
|
||||
DbgUtils.logf( "NBSService: outgoing hash on %d bytes: %X",
|
||||
data.length, hash );
|
||||
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
|
||||
DataOutputStream das = new DataOutputStream( bas );
|
||||
das.writeByte( 0 ); // protocol
|
||||
das.writeByte( cmd.ordinal() );
|
||||
das.writeInt( hash );
|
||||
das.write( data, 0, data.length );
|
||||
das.flush();
|
||||
|
||||
String as64 = Base64.encodeToString( bas.toByteArray(), Base64.NO_WRAP );
|
||||
byte[][] msgs = breakAndEncode( as64 );
|
||||
return sendBuffers( msgs, phone );
|
||||
}
|
||||
|
||||
private byte[][] breakAndEncode( String msg )
|
||||
throws java.io.IOException
|
||||
{
|
||||
// TODO: as optimization, truncate header when only one packet
|
||||
// required
|
||||
Assert.assertFalse( msg.contains(":") );
|
||||
int count = (msg.length() + (MAX_LEN_TEXT-1)) / MAX_LEN_TEXT;
|
||||
byte[][] result = new byte[count][];
|
||||
int msgID = ++s_nSent % 0x000000FF;
|
||||
DbgUtils.logf( "preparing %d packets for msgid %x", count, msgID );
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for ( int ii = 0; ii < count; ++ii ) {
|
||||
int len = msg.length() - end;
|
||||
if ( len > MAX_LEN_TEXT ) {
|
||||
len = MAX_LEN_TEXT;
|
||||
}
|
||||
end += len;
|
||||
String out = String.format( "0:%X:%X:%X:%s", msgID, ii, count,
|
||||
msg.substring( start, end ) );
|
||||
DbgUtils.logf( "fragment[%d]: %s", ii, out );
|
||||
result[ii] = out.getBytes();
|
||||
start = end;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void receive( NBS_CMD cmd, byte[] data, String phone )
|
||||
{
|
||||
CommsAddrRec addr = new CommsAddrRec( phone );
|
||||
DataInputStream dis =
|
||||
new DataInputStream( new ByteArrayInputStream(data) );
|
||||
try {
|
||||
switch( cmd ) {
|
||||
case INVITE:
|
||||
int gameID = dis.readInt();
|
||||
String gameName = dis.readUTF();
|
||||
int lang = dis.readInt();
|
||||
int nPlayersT = dis.readByte();
|
||||
int nPlayersH = dis.readByte();
|
||||
|
||||
long rowid = GameUtils.makeNewNBSGame( this, gameID, addr,
|
||||
lang, nPlayersT, nPlayersH );
|
||||
|
||||
if ( null != gameName && 0 < gameName.length() ) {
|
||||
DBUtils.setName( this, rowid, gameName );
|
||||
}
|
||||
String body = Utils.format( this, R.string.new_nbs_bodyf, phone );
|
||||
postNotification( gameID, R.string.new_nbs_title, body );
|
||||
break;
|
||||
case DATA:
|
||||
gameID = dis.readInt();
|
||||
byte[] rest = new byte[dis.available()];
|
||||
dis.read( rest );
|
||||
feedMessage( gameID, rest, addr );
|
||||
break;
|
||||
default:
|
||||
Assert.fail();
|
||||
break;
|
||||
}
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.logf( "ioe: %s", ioe.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
private void receiveBuffer( byte[] as64, String senderPhone )
|
||||
{
|
||||
String asString = new String( as64 );
|
||||
DbgUtils.logf( "receiveBuffer(%s)", asString );
|
||||
String[] parts = asString.split( ":" );
|
||||
DbgUtils.logf( "receiveBuffer: got %d parts", parts.length );
|
||||
for ( String part : parts ) {
|
||||
DbgUtils.logf( "part: %s", part );
|
||||
}
|
||||
Assert.assertTrue( 5 == parts.length );
|
||||
if ( 5 == parts.length ) {
|
||||
byte proto = Byte.valueOf( parts[0], 10 );
|
||||
int id = Integer.valueOf( parts[1], 16 );
|
||||
int index = Integer.valueOf( parts[2], 16 );
|
||||
int count = Integer.valueOf( parts[3], 16 );
|
||||
tryAssemble( senderPhone, id, index, count, parts[4] );
|
||||
}
|
||||
}
|
||||
|
||||
private void tryAssemble( String senderPhone, int id, int index,
|
||||
int count, String msg )
|
||||
{
|
||||
if ( index == 0 && count == 1 ) {
|
||||
disAssemble( senderPhone, msg.getBytes() );
|
||||
} else {
|
||||
synchronized( s_partialMsgs ) {
|
||||
HashMap <Integer, MsgStore> perPhone =
|
||||
s_partialMsgs.get( senderPhone );
|
||||
if ( null == perPhone ) {
|
||||
perPhone = new HashMap <Integer, MsgStore>();
|
||||
s_partialMsgs.put( senderPhone, perPhone );
|
||||
}
|
||||
MsgStore store = perPhone.get( id );
|
||||
if ( null == store ) {
|
||||
store = new MsgStore( id, count );
|
||||
perPhone.put( id, store );
|
||||
}
|
||||
store.add( index, msg );
|
||||
|
||||
if ( store.isComplete() ) {
|
||||
byte[] fullMsg = store.message();
|
||||
perPhone.remove( id );
|
||||
disAssemble( senderPhone, fullMsg );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void disAssemble( String senderPhone, byte[] fullMsg )
|
||||
{
|
||||
DbgUtils.logf( "disAssemble()" );
|
||||
byte[] data = Base64.decode( fullMsg, Base64.NO_WRAP );
|
||||
DataInputStream dis =
|
||||
new DataInputStream( new ByteArrayInputStream(data) );
|
||||
try {
|
||||
byte proto = dis.readByte();
|
||||
if ( 0 != proto ) {
|
||||
DbgUtils.logf( "NBSService.disAssemble: bad proto %d; dropping",
|
||||
proto );
|
||||
} else {
|
||||
NBS_CMD cmd = NBS_CMD.values()[dis.readByte()];
|
||||
int hashRead = dis.readInt();
|
||||
DbgUtils.logf( "NBSService: incoming hash: %X", hashRead );
|
||||
byte[] rest = new byte[dis.available()];
|
||||
dis.read( rest );
|
||||
int hashComputed = Arrays.hashCode( rest );
|
||||
if ( hashComputed == hashRead ) {
|
||||
DbgUtils.logf( "NBSService: incoming hashes on %d " +
|
||||
"bytes match: %X", rest.length, hashRead );
|
||||
receive( cmd, rest, senderPhone );
|
||||
} else {
|
||||
DbgUtils.logf( "NBSService: incoming hashes on %d bytes "
|
||||
+ "DON'T match: read: %X; figured: %X",
|
||||
rest.length, hashRead, hashComputed );
|
||||
}
|
||||
}
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
DbgUtils.logf( "ioe: %s", ioe.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sendBuffers( byte[][] fragments, String phone )
|
||||
{
|
||||
DbgUtils.logf( "NBSService.sendBuffers()" );
|
||||
boolean success = false;
|
||||
if ( XWApp.onEmulator() ) {
|
||||
DbgUtils.logf( "sendBuffer(phone=%s): FAKING IT", phone );
|
||||
} else {
|
||||
try {
|
||||
PendingIntent sent =
|
||||
PendingIntent.getBroadcast( this, 0, new Intent(), 0 );
|
||||
PendingIntent dlvrd =
|
||||
PendingIntent.getBroadcast( this, 0, new Intent(), 0 );
|
||||
|
||||
SmsManager mgr = SmsManager.getDefault();
|
||||
short port = XWApp.getNBSPort();
|
||||
for ( byte[] fragment : fragments ) {
|
||||
String tmp = new String(fragment);
|
||||
DbgUtils.logf( "sending len %d packet: %s", tmp.length(), tmp );
|
||||
mgr.sendDataMessage( phone, null, port, fragment, sent, dlvrd );
|
||||
DbgUtils.logf( "sendDataMessage of %d bytes to %s on %d "
|
||||
+ "finished", fragment.length, phone, port );
|
||||
}
|
||||
DbgUtils.showf( this, "send %dth msg", ++s_nSent );
|
||||
success = true;
|
||||
} catch ( IllegalArgumentException iae ) {
|
||||
DbgUtils.logf( "%s", iae.toString() );
|
||||
} catch ( Exception ee ) {
|
||||
DbgUtils.logf( "sendDataMessage message failed: %s",
|
||||
ee.toString() );
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private void feedMessage( int gameID, byte[] msg, CommsAddrRec addr )
|
||||
{
|
||||
if ( BoardActivity.feedMessage( gameID, msg, addr ) ) {
|
||||
// do nothing
|
||||
} else {
|
||||
long rowid = DBUtils.getRowIDFor( this, gameID );
|
||||
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
|
||||
NBSMsgSink sink = new NBSMsgSink( this );
|
||||
if ( GameUtils.feedMessage( this, rowid, msg, addr, sink ) ) {
|
||||
postNotification( gameID, R.string.new_nbsmove_title,
|
||||
getString(R.string.new_move_body)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void postNotification( int gameID, int title, String body )
|
||||
{
|
||||
Intent intent = new Intent( this, DispatchNotify.class );
|
||||
intent.putExtra( DispatchNotify.GAMEID_EXTRA, gameID );
|
||||
Utils.postNotification( this, intent, R.string.new_nbsmove_title,
|
||||
body );
|
||||
}
|
||||
|
||||
private class NBSMsgSink extends MultiMsgSink {
|
||||
private Context m_context;
|
||||
public NBSMsgSink( Context context ) {
|
||||
super();
|
||||
m_context = context;
|
||||
}
|
||||
|
||||
/***** TransportProcs interface *****/
|
||||
public int transportSend( byte[] buf, final CommsAddrRec addr, int gameID )
|
||||
{
|
||||
DbgUtils.logf( "NBSMsgSink.transportSend()" );
|
||||
return sendPacket( addr.sms_phone, gameID, buf );
|
||||
}
|
||||
|
||||
public boolean relayNoConnProc( byte[] buf, String relayID )
|
||||
{
|
||||
Assert.fail();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class MsgStore {
|
||||
String[] m_msgs;
|
||||
int m_msgID;
|
||||
int m_haveCount;
|
||||
int m_fullLength;
|
||||
|
||||
public MsgStore( int id, int count )
|
||||
{
|
||||
m_msgID = id;
|
||||
m_msgs = new String[count];
|
||||
m_fullLength = 0;
|
||||
}
|
||||
|
||||
public void add( int index, String msg )
|
||||
{
|
||||
if ( null == m_msgs[index] ) {
|
||||
++m_haveCount;
|
||||
m_fullLength += msg.length();
|
||||
}
|
||||
m_msgs[index] = msg;
|
||||
}
|
||||
|
||||
public boolean isComplete()
|
||||
{
|
||||
boolean complete = m_msgs.length == m_haveCount;
|
||||
DbgUtils.logf( "isComplete(msg %d)=>%b", m_msgID, complete );
|
||||
return complete;
|
||||
}
|
||||
|
||||
public byte[] message()
|
||||
{
|
||||
byte[] result = new byte[m_fullLength];
|
||||
int offset = 0;
|
||||
for ( int ii = 0; ii < m_msgs.length; ++ii ) {
|
||||
byte[] src = m_msgs[ii].getBytes();
|
||||
System.arraycopy( src, 0, result, offset, src.length );
|
||||
offset += src.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -193,9 +193,9 @@ public class NewGameActivity extends XWActivity {
|
|||
m_gameID = GameUtils.newGameID();
|
||||
m_gameName = Utils.format( this, R.string.dft_nbs_namef,
|
||||
m_gameID & 0xFFFF );
|
||||
NBSReceiver.inviteRemote( NewGameActivity.this, phones[0],
|
||||
m_gameID, m_gameName,
|
||||
m_lang, 2, 1 );
|
||||
NBSService.inviteRemote( NewGameActivity.this, phones[0],
|
||||
m_gameID, m_gameName,
|
||||
m_lang, 2, 1 );
|
||||
long rowid =
|
||||
GameUtils.makeNewNBSGame( NewGameActivity.this,
|
||||
m_gameID, null, m_lang,
|
||||
|
@ -355,6 +355,10 @@ public class NewGameActivity extends XWActivity {
|
|||
m_newRowID = GameUtils.makeNewNBSGame( NewGameActivity.this,
|
||||
gameID, null, m_lang,
|
||||
2, 1 ); // initial defaults
|
||||
String name = Utils.format( this, R.string.dft_nbs_namef,
|
||||
gameID & 0xFFFF );
|
||||
DBUtils.setName( this, m_newRowID, name );
|
||||
|
||||
Intent intent = new Intent( this, GameConfig.class );
|
||||
intent.setAction( Intent.ACTION_EDIT );
|
||||
intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_newRowID );
|
||||
|
|
Loading…
Reference in a new issue