Merge branch 'android_branch' into android_gcm

This commit is contained in:
Eric House 2012-11-05 06:14:21 -08:00
commit 03108f311a
30 changed files with 403 additions and 115 deletions

View file

@ -22,7 +22,7 @@
to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eehouse.android.xw4"
android:versionCode="45"
android:versionCode="46"
android:versionName="@string/app_version"
>

View file

@ -5,36 +5,17 @@
</style>
</head>
<body>
<b>Crosswords 4.4 beta 53 release</b>
<b>Crosswords 4.4 beta 54 release</b>
<ul>
<li>Add a second way devices can play: via SMS. If you and your
friends have unlimited texting plans (ONLY IF!!) this is the way to
go: connecting is easier, moves get transmitted more quickly, and
less battery is required. <a
href="http://xwords.sf.net/and_faq.php">See the FAQ</a></li>
<li>Don't try to access directory OS says is for downloads when it
doesn't actually exist</li>
<li>In the main games-list screen, replace green highlighting of the
player whose turn it is with green transitioning to red over three
days. That way you can see how long it's been that person's turn.
Nothing happens -- yet -- when the three days is up. Maybe later
that'll force a resignation.</li>
<li>Download wordlists without using the browser when possible</li>
<li>Warn when you join a network game with a different wordlist from
the host's, and give a chance to download and switch.</li>
<li>Don't allow networked games to have different wordlist for each
player. That feature is for local games only now.</li>
<li>There's now a preference for where downloaded wordlists are
stored instead of a question asked each time.</li>
<li>Recast game-over feature as "resigning".</li>
</ul>
<p>(The full changelog
is <a href="http://xwords.sf.net/and_changes.php">here</a>.)</p>
<p>Please remember that this is beta software. Please let me know (at
eehouse@eehouse.org) what's broken and what features you'd most like
to see.</p>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_version">4.4 beta 53</string>
<string name="app_version">4.4 beta 54</string>
</resources>

View file

@ -93,6 +93,7 @@
<string name="key_na_browse">key_na_browse</string>
<string name="key_na_values">key_na_values</string>
<string name="key_enable_debug">key_enable_debug</string>
<string name="key_download_path">key_download_path</string>
<!-- Nor is my email address -->
<string name="email_author_email">xwords@eehouse.org</string>

View file

@ -2123,5 +2123,5 @@
<string name="default_loc">Store wordlists internally</string>
<string name="default_loc_summary">(Not in external/sdcard memory)</string>
<string name="sms_searching_toast">Searching SMS messages...</string>
<string name="download_path_title">Downloads Directory</string>
</resources>

View file

@ -299,6 +299,10 @@
android:summary="Menuitems etc."
android:defaultValue="false"
/>
<org.eehouse.android.xw4.XWEditTextPreference
android:key="@string/key_download_path"
android:title="@string/download_path_title"
/>
<CheckBoxPreference android:key="@string/key_show_sms"
android:title="Show SMS sends, receives"
android:defaultValue="false"

View file

@ -33,6 +33,7 @@ import java.util.Comparator;
import junit.framework.Assert;
import org.eehouse.android.xw4.DictUtils.DictAndLoc;
import org.eehouse.android.xw4.DictUtils.DictLoc;
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.jni.DictInfo;
@ -244,7 +245,7 @@ public class DictLangCache {
// May be called from background thread
public static void inval( final Context context, String name,
DictUtils.DictLoc loc, boolean added )
DictLoc loc, boolean added )
{
DictAndLoc dal = new DictAndLoc( name, loc );
DBUtils.dictsRemoveInfo( context, dal );
@ -372,7 +373,7 @@ public class DictLangCache {
{
DictInfo result = DBUtils.dictsGetInfo( context, name );
if ( null == result ) {
DictUtils.DictLoc loc = DictUtils.getDictLoc( context, name );
DictLoc loc = DictUtils.getDictLoc( context, name );
result = getInfo( context, new DictAndLoc( name, loc ) );
}
return result;
@ -397,7 +398,7 @@ public class DictLangCache {
if ( XwJNI.dict_getInfo( pairs.m_bytes[0], dal.name,
pairs.m_paths[0],
JNIUtilsImpl.get( context ),
DictUtils.DictLoc.DOWNLOAD == dal.loc,
DictLoc.DOWNLOAD == dal.loc,
info ) ) {
info.name = dal.name;

View file

@ -149,7 +149,8 @@ public class DictUtils {
}
tryDir( context, getSDDir( context ), false, DictLoc.EXTERNAL, al );
tryDir( context, getDownloadDir(), true, DictLoc.DOWNLOAD, al );
tryDir( context, getDownloadDir( context ), true,
DictLoc.DOWNLOAD, al );
s_dictListCache =
al.toArray( new DictUtils.DictAndLoc[al.size()] );
@ -248,7 +249,7 @@ public class DictUtils {
File path = null;
switch( loc ) {
case DOWNLOAD:
path = getDownloadsPathFor( name );
path = getDownloadsPathFor( context, name );
break;
case EXTERNAL:
path = getSDPathFor( context, name );
@ -305,7 +306,7 @@ public class DictUtils {
FileInputStream fis = null;
if ( null == fis ) {
if ( loc == DictLoc.UNKNOWN || loc == DictLoc.DOWNLOAD ) {
File path = getDownloadsPathFor( name );
File path = getDownloadsPathFor( context, name );
if ( null != path && path.exists() ) {
DbgUtils.logf( "loading %s from Download", name );
fis = new FileInputStream( path );
@ -365,7 +366,7 @@ public class DictUtils {
File path;
switch ( to ) {
case DOWNLOAD:
path = getDownloadsPathFor( name );
path = getDownloadsPathFor( context, name );
break;
case EXTERNAL:
path = getSDPathFor( context, name );
@ -560,25 +561,36 @@ public class DictUtils {
return result;
}
private static File getDownloadDir()
public static boolean haveDownloadDir( Context context )
{
return null != getDownloadDir( context );
}
private static File getDownloadDir( Context context )
{
File result = null;
if ( haveWriteableSD() ) {
File storage = Environment.getExternalStorageDirectory();
if ( null != storage ) {
result = new File( storage.getPath(), "download/" );
if ( !result.exists() ) {
result = null;
File file = null;
String myPath = XWPrefs.getMyDownloadDir( context );
if ( null != myPath && 0 < myPath.length() ) {
file = new File( myPath );
} else {
file = Environment.getExternalStorageDirectory();
if ( null != file ) {
file = new File( file, "download/" );
}
}
if ( null != file && file.exists() && file.isDirectory() ) {
result = file;
}
}
return result;
}
private static File getDownloadsPathFor( String name )
private static File getDownloadsPathFor( Context context, String name )
{
File result = null;
File dir = getDownloadDir();
File dir = getDownloadDir( context );
if ( dir != null ) {
result = new File( dir, name );
}

View file

@ -263,15 +263,6 @@ public class DictsActivity extends ExpandableListActivity
message = Utils.format( this, R.string.move_dictf,
m_adapter.getSelChildView().getText() );
String[] items = new String[3];
for ( int ii = 0; ii < 3; ++ii ) {
DictLoc loc = itemToRealLoc(ii);
if ( loc.equals( m_moveFromLoc ) ) {
m_moveFromItem = ii;
}
items[ii] = m_locNames[loc.ordinal()];
}
OnClickListener newSelLstnr =
new OnClickListener() {
public void onClick( DialogInterface dlgi, int item ) {
@ -307,7 +298,8 @@ public class DictsActivity extends ExpandableListActivity
dialog = new AlertDialog.Builder( this )
.setTitle( message )
.setSingleChoiceItems( items, m_moveFromItem, newSelLstnr )
.setSingleChoiceItems( makeDictDirItems(), m_moveFromItem,
newSelLstnr )
.setPositiveButton( R.string.button_move, lstnr )
.setNegativeButton( R.string.button_cancel, null )
.create();
@ -716,6 +708,26 @@ public class DictsActivity extends ExpandableListActivity
XWPrefs.setClosedLangs( this, asArray );
}
private String[] makeDictDirItems()
{
boolean showDownload = DictUtils.haveDownloadDir( this );
int nItems = showDownload ? 3 : 2;
int nextI = 0;
String[] items = new String[nItems];
for ( int ii = 0; ii < 3; ++ii ) {
DictLoc loc = itemToRealLoc(ii);
if ( !showDownload && DictLoc.DOWNLOAD == loc ) {
continue;
}
if ( loc.equals( m_moveFromLoc ) ) {
m_moveFromItem = nextI;
}
items[nextI++] = m_locNames[loc.ordinal()];
}
return items;
}
private static Intent mkDownloadIntent( Context context, String dict_url )
{
Uri uri = Uri.parse( dict_url );

View file

@ -208,12 +208,20 @@ public class DlgDelegate {
public void showConfirmThen( String msg, int posButton, int callbackID )
{
m_msg = msg;
m_posButton = posButton;
Assert.assertTrue( 0 != callbackID );
Assert.assertTrue( 0 == m_cbckID );
m_cbckID = callbackID;
m_activity.showDialog( CONFIRM_THEN );
// FIX ME!! Need to store data per message rather than have
// assertions failing or messages dropped.
if ( 0 != m_cbckID ) {
DbgUtils.logf( "showConfirmThen: busy with another message; "
+ "dropping \"%s\" in favor of \"%s\"",
msg, m_msg );
} else {
m_msg = msg;
m_posButton = posButton;
Assert.assertTrue( 0 != callbackID );
m_cbckID = callbackID;
m_activity.showDialog( CONFIRM_THEN );
}
}
public void showEmailOrSMSThen( final int callbackID )

View file

@ -730,7 +730,6 @@ public class GameUtils {
}
lock.unlock();
}
DbgUtils.logf( "feedMessages=>%b", draw );
return draw;
} // feedMessages

View file

@ -29,6 +29,7 @@ import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.Button;
import java.io.File;
public class PrefsActivity extends PreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
@ -40,6 +41,7 @@ public class PrefsActivity extends PreferenceActivity
private String m_keyLogging;
private String m_smsToasting;
private String m_smsEnable;
private String m_downloadPath;
@Override
protected Dialog onCreateDialog( int id )
@ -120,6 +122,7 @@ public class PrefsActivity extends PreferenceActivity
m_keyLogging = getString( R.string.key_logging_on );
m_smsToasting = getString( R.string.key_show_sms );
m_smsEnable = getString( R.string.key_enable_sms );
m_downloadPath = getString( R.string.key_download_path );
Button button = (Button)findViewById( R.id.revert_colors );
button.setOnClickListener( new View.OnClickListener() {
@ -164,6 +167,23 @@ public class PrefsActivity extends PreferenceActivity
} else {
XWPrefs.setHaveCheckedSMS( this, false );
}
} else if ( key.equals( m_downloadPath ) ) {
String value = sp.getString( key, null );
if ( null != value ) {
File dir = new File( value );
String msg = null;
if ( !dir.exists() ) {
msg = String.format( "%s does not exist", value );
} else if ( !dir.isDirectory() ) {
msg = String.format( "%s is not a directory", value );
} else if ( !dir.canWrite() ) {
msg = String.format( "Cannot write to %s", value );
}
if ( null != msg ) {
Utils.showToast( this, msg );
}
}
DictUtils.invalDictList();
}
}

View file

@ -232,7 +232,6 @@ public class SMSService extends Service {
switch( cmd ) {
case CHECK_MSGDB:
if ( ! XWPrefs.getHaveCheckedSMS( this ) ) {
Utils.showToast( this, R.string.sms_searching_toast );
XWPrefs.setHaveCheckedSMS( this, true );
new Thread( new Runnable() {
public void run() {

View file

@ -216,6 +216,11 @@ public class XWPrefs {
: DictUtils.DictLoc.EXTERNAL;
return result;
}
public static String getMyDownloadDir( Context context )
{
return getPrefsString( context, R.string.key_download_path );
}
public static boolean getDefaultLocInternal( Context context )
{

View file

@ -1330,6 +1330,16 @@ got_connect_cmd( CommsCtxt* comms, XWStreamCtxt* stream,
sizeof(comms->r.connName) );
#endif
#ifdef XWFEATURE_DEVID
if ( !reconnected ) {
XP_UCHAR devID[MAX_DEVID_LEN + 1];
stringFromStreamHere( stream, devID, sizeof(devID) );
if ( devID[0] != '\0' ) {
util_deviceRegistered( comms->util, devID );
}
}
#endif
(*comms->procs.rconnd)( comms->procs.closure,
comms->addr.u.ip_relay.invite, reconnected,
comms->r.myHostID, XP_FALSE, nSought - nHere );

View file

@ -55,12 +55,6 @@ typedef enum {
, COMMS_RELAYSTATE_ALLCONNECTED
} CommsRelayState;
typedef enum {
ID_TYPE_NONE
,ID_TYPE_LINUX
,ID_TYPE_ANDROID_GCM
} DevIDType;
#ifdef XWFEATURE_BLUETOOTH
# define XW_BT_UUID "7be0d084-ff89-4d6d-9c78-594773a6f963"

View file

@ -154,6 +154,7 @@ typedef struct UtilVtable {
XP_U32 (*m_util_getCurSeconds)( XW_UtilCtxt* uc );
#ifdef XWFEATURE_DEVID
const XP_UCHAR* (*m_util_getDevID)( XW_UtilCtxt* uc, DevIDType* typ );
void (*m_util_deviceRegistered)( XW_UtilCtxt* uc, const XP_UCHAR* idRelay );
#endif
DictionaryCtxt* (*m_util_makeEmptyDict)( XW_UtilCtxt* uc );
@ -285,6 +286,8 @@ struct XW_UtilCtxt {
#ifdef XWFEATURE_DEVID
# define util_getDevID( uc, t ) \
(uc)->vtable->m_util_getDevID((uc),(t))
# define util_deviceRegistered( uc, id ) \
(uc)->vtable->m_util_deviceRegistered( (uc), (id) )
#endif
#define util_makeEmptyDict( uc ) \

View file

@ -505,6 +505,7 @@ typedef enum {
,CMD_SEED
#ifdef XWFEATURE_DEVID
,CMD_DEVID
,CMD_RDEVID
#endif
,CMD_GAMESEED
,CMD_GAMEFILE
@ -601,6 +602,7 @@ static CmdInfoRec CmdInfoRecs[] = {
,{ CMD_SEED, true, "seed", "random seed" }
#ifdef XWFEATURE_DEVID
,{ CMD_DEVID, true, "devid", "device ID (for testing GCM stuff)" }
,{ CMD_RDEVID, true, "rdevid", "relay's converted device ID (for testing GCM stuff)" }
#endif
,{ CMD_GAMESEED, true, "game-seed", "game seed (for relay play)" }
,{ CMD_GAMEFILE, true, "file", "file to save to/read from" }
@ -1683,6 +1685,9 @@ main( int argc, char** argv )
case CMD_DEVID:
mainParams.devID = optarg;
break;
case CMD_RDEVID:
mainParams.rDevID = optarg;
break;
#endif
case CMD_GAMESEED:
mainParams.gameSeed = atoi(optarg);

View file

@ -348,9 +348,25 @@ linux_util_getUserString( XW_UtilCtxt* XP_UNUSED(uc), XP_U16 code )
static const XP_UCHAR*
linux_util_getDevID( XW_UtilCtxt* uc, DevIDType* typ )
{
XP_UCHAR* result;
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
*typ = ID_TYPE_LINUX;
return cGlobals->params->devID;
if ( !!cGlobals->params->rDevID ) {
*typ = ID_TYPE_RELAY;
result = cGlobals->params->rDevID;
} else {
*typ = ID_TYPE_LINUX;
result = cGlobals->params->devID;
}
return result;
}
static void
linux_util_deviceRegistered( XW_UtilCtxt* XP_UNUSED(uc),
const XP_UCHAR* idRelay )
{
/* Script discon_ok2.sh is grepping for this in logs, so don't change
it! */
XP_LOGF( "%s: new id: %s", __func__, idRelay );
}
#endif
@ -365,6 +381,7 @@ linux_util_vt_init( MPFORMAL XW_UtilCtxt* util )
util->vtable->m_util_getUserString = linux_util_getUserString;
#ifdef XWFEATURE_DEVID
util->vtable->m_util_getDevID = linux_util_getDevID;
util->vtable->m_util_deviceRegistered = linux_util_deviceRegistered;
#endif
}

View file

@ -61,6 +61,7 @@ typedef struct LaunchParams {
char* bonusFile;
#ifdef XWFEATURE_DEVID
char* devID;
char* rDevID;
#endif
VTableMgr* vtMgr;
XP_U16 nLocalPlayers;

View file

@ -190,6 +190,7 @@ build_cmds() {
PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS"
# PARAMS="$PARAMS --savefail-pct 10"
[ -n "$SEED" ] && PARAMS="$PARAMS --seed $RANDOM"
# PARAMS="$PARAMS --devid LINUX_TEST_$(printf %.5d ${COUNTER})"
PARAMS="$PARAMS $PUBLIC"
ARGS[$COUNTER]=$PARAMS
ROOMS[$COUNTER]=$ROOM
@ -380,6 +381,19 @@ increment_drop() {
fi
}
set_relay_devid() {
KEY=$1
CMD=${ARGS[$KEY]}
if [ "$CMD" != "${CMD/--devid //}" ]; then
RELAY_ID=$(grep 'deviceRegistered: new id: ' ${LOGS[$KEY]} | tail -n 1)
if [ -n "$RELAY_ID" ]; then
RELAY_ID=$(echo $RELAY_ID | sed 's,^.*new id: ,,')
# turn --devid <whatever> into --rdevid $RELAY_ID
ARGS[$KEY]=$(echo $CMD | sed 's,^\(.*\)--devid[ ]\+[^ ]\+\(.*\)$,\1--rdevid $RELAY_ID\2,')
fi
fi
}
run_cmds() {
ENDTIME=$(($(date +%s) + TIMEOUT))
while :; do
@ -409,6 +423,7 @@ run_cmds() {
PIDS[$KEY]=0
ROOM_PIDS[$ROOM]=0
[ "$DROP_N" -ge 0 ] && increment_drop $KEY
# set_relay_devid $KEY
check_game $KEY
fi
done

View file

@ -27,6 +27,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@ -641,9 +642,10 @@ CookieRef::handleEvents()
case XWA_SEND_CONNRSP:
{
HostID hid;
if ( increasePlayerCounts( &evt, false, &hid ) ) {
DBMgr::DevIDRelay devID;
if ( increasePlayerCounts( &evt, false, &hid, &devID ) ) {
setAllConnectedTimer();
sendResponse( &evt, true );
sendResponse( &evt, true, &devID );
setAckTimer( hid );
}
}
@ -666,8 +668,8 @@ CookieRef::handleEvents()
/* break; */
case XWA_SEND_RERSP:
increasePlayerCounts( &evt, true, NULL );
sendResponse( &evt, false );
increasePlayerCounts( &evt, true, NULL, NULL );
sendResponse( &evt, false, NULL );
sendAnyStored( &evt );
postCheckAllHere();
break;
@ -855,8 +857,10 @@ CookieRef::send_stored_messages( HostID dest, int socket )
} /* send_stored_messages */
bool
CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp )
CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp,
DBMgr::DevIDRelay* devIDp )
{
DBMgr::DevIDRelay devID = DBMgr::DEVID_NONE;
int nPlayersH = evt->u.con.nPlayersH;
int socket = evt->u.con.socket;
int seed = evt->u.con.seed;
@ -883,10 +887,25 @@ CookieRef::increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp )
assert( m_nPlayersHere <= m_nPlayersSought );
}
if ( !!devIDp ) {
DevIDType devIDType = evt->u.con.devID->m_devIDType;
// does client support devID
if ( ID_TYPE_NONE != devIDType ) {
// have we not already converted it?
if ( ID_TYPE_RELAY == devIDType ) {
devID = (DBMgr::DevIDRelay)strtoul( evt->u.con.devID->m_devIDString.c_str(),
NULL, 16 );
} else {
devID = DBMgr::Get()->RegisterDevice( evt->u.con.devID );
}
}
*devIDp = devID;
}
evt->u.con.srcID =
DBMgr::Get()->AddDevice( ConnName(), evt->u.con.srcID,
evt->u.con.clientVersion, nPlayersH, seed,
evt->u.con.addr, evt->u.con.devID, reconn );
evt->u.con.addr, devID, reconn );
HostID hostid = evt->u.con.srcID;
if ( NULL != hidp ) {
@ -1016,7 +1035,8 @@ CookieRef::cancelAllConnectedTimer()
}
void
CookieRef::sendResponse( const CRefEvent* evt, bool initial )
CookieRef::sendResponse( const CRefEvent* evt, bool initial,
const DBMgr::DevIDRelay* devID )
{
int socket = evt->u.con.socket;
@ -1028,6 +1048,7 @@ CookieRef::sendResponse( const CRefEvent* evt, bool initial )
+ sizeof(unsigned char) /* total here */
+ sizeof(unsigned char) /* total expected */
+ 1 + MAX_CONNNAME_LEN
+ 1 + 1 + MAX_DEVID_LEN
];
unsigned char* bufp = buf;
@ -1043,12 +1064,33 @@ CookieRef::sendResponse( const CRefEvent* evt, bool initial )
const char* connName = ConnName();
assert( !!connName && connName[0] );
int len = strlen( connName );
size_t len = strlen( connName );
assert( len < MAX_CONNNAME_LEN );
*bufp++ = (char)len;
memcpy( bufp, connName, len );
bufp += len;
if ( initial ) {
// we always write at least empty string
char idbuf[MAX_DEVID_LEN + 1] = {0};
// If client supports devid, and we have one (response case), write it as
// 8-byte hex string plus a length byte -- but only if we didn't already
// receive it.
if ( !!devID && ID_TYPE_RELAY < evt->u.con.devID->m_devIDType ) {
len = snprintf( idbuf, sizeof(idbuf), "%.8X", *devID );
assert( len < sizeof(idbuf) );
}
len = strlen( idbuf );
assert( len <= MAX_DEVID_LEN );
*bufp++ = (char)len;
if ( 0 < len ) {
memcpy( bufp, idbuf, len );
bufp += len;
}
}
send_with_length( socket, buf, bufp - buf, true );
logf( XW_LOGVERBOSE0, "sent %s", cmdToStr( XWRELAY_Cmd(buf[0]) ) );
} /* sendResponse */

View file

@ -30,6 +30,8 @@
#include <pthread.h>
#include "xwrelay_priv.h"
#include "xwrelay.h"
#include "devid.h"
#include "dbmgr.h"
#include "states.h"
typedef vector<unsigned char> MsgBuffer;
@ -63,13 +65,6 @@ public:
class CookieRef* m_this;
};
class DevID {
public:
DevID() { m_devIDType = 0; }
string m_devIDString;
unsigned char m_devIDType;
};
class CookieRef {
public:
set<int> GetSockets();
@ -224,10 +219,12 @@ class CookieRef {
void handleEvents();
void sendResponse( const CRefEvent* evt, bool initial );
void sendResponse( const CRefEvent* evt, bool initial,
const DBMgr::DevIDRelay* devID );
void sendAnyStored( const CRefEvent* evt );
void initPlayerCounts( const CRefEvent* evt );
bool increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp );
bool increasePlayerCounts( CRefEvent* evt, bool reconn, HostID* hidp,
DBMgr::DevIDRelay* devID );
void updateAck( HostID hostID, bool keep );
void dropPending( int seed );

View file

@ -34,6 +34,7 @@
#define GAMES_TABLE "games"
#define MSGS_TABLE "msgs"
#define DEVICES_TABLE "devices"
#define ARRAYSUM "sum_array(nPerDevice)"
@ -243,10 +244,59 @@ DBMgr::AllDevsAckd( const char* const connName )
return full;
}
// Return DevIDRelay for device, adding it to devices table IFF it's not
// already there.
DBMgr::DevIDRelay
DBMgr::RegisterDevice( const DevID* host )
{
DBMgr::DevIDRelay devID;
assert( host->m_devIDType != ID_TYPE_RELAY );
int ii;
bool success;
// if it's already present, just return
devID = getDevID( host );
if ( DEVID_NONE == devID ) {
// loop until we're successful inserting the unique key. Ship with this
// coming from random, but test with increasing values initially to make
// sure duplicates are detected.
for ( success = false, ii = 0; !success; ++ii ) {
assert( 10 > ii ); // better to check that we're looping BECAUSE
// of uniqueness problem.
devID = (DBMgr::DevIDRelay)random();
if ( DEVID_NONE == devID ) {
continue;
}
const char* command = "INSERT INTO " DEVICES_TABLE
" (id, devType, devid)"
" VALUES( $1, $2, $3 )";
int nParams = 3;
char* paramValues[nParams];
char buf[512];
formatParams( paramValues, nParams,
"%d"DELIM"%d"DELIM"%s",
buf, sizeof(buf), devID, host->m_devIDType,
host->m_devIDString.c_str() );
PGresult* result = PQexecParams( getThreadConn(), command,
nParams, NULL,
paramValues,
NULL, NULL, 0 );
success = PGRES_COMMAND_OK == PQresultStatus(result);
if ( !success ) {
logf( XW_LOGERROR, "PQexec=>%s;%s", PQresStatus(PQresultStatus(result)),
PQresultErrorMessage(result) );
}
PQclear( result );
}
}
return devID;
}
HostID
DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion,
int nToAdd, unsigned short seed, const in_addr& addr,
const DevID* devID, bool ackd )
DevIDRelay devID, bool ackd )
{
HostID newID = curID;
@ -262,11 +312,9 @@ DBMgr::AddDevice( const char* connName, HostID curID, int clientVersion,
assert( newID <= 4 );
char devIDBuf[512] = {0};
if ( !!devID ) {
if ( DEVID_NONE != devID ) {
snprintf( devIDBuf, sizeof(devIDBuf),
"devids[%d] = \'%s\', devTypes[%d] = %d,",
newID, devID->m_devIDString.c_str(),
newID, devID->m_devIDType );
"devids[%d] = %d, ", newID, devID );
}
const char* fmt = "UPDATE " GAMES_TABLE " SET nPerDevice[%d] = %d,"
@ -569,20 +617,45 @@ DBMgr::readArray( const char* const connName, int arr[] ) /* len 4 */
PQclear( result );
}
void
DBMgr::getDevID( const char* connName, int hid, DevID& devID )
DBMgr::DevIDRelay
DBMgr::getDevID( const char* connName, int hid )
{
const char* fmt = "SELECT devids[%d], devTypes[%d] FROM " GAMES_TABLE " WHERE connName='%s'";
DBMgr::DevIDRelay devID;
const char* fmt = "SELECT devids[%d] FROM " GAMES_TABLE " WHERE connName='%s'";
char query[256];
snprintf( query, sizeof(query), fmt, hid, hid, connName );
snprintf( query, sizeof(query), fmt, hid, connName );
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
PGresult* result = PQexec( getThreadConn(), query );
assert( 1 == PQntuples( result ) );
devID.m_devIDString = PQgetvalue( result, 0, 0 );
devID.m_devIDType = (unsigned char)atoi( PQgetvalue( result, 0, 1 ) );
assert( devID.m_devIDType <= 2 ); // for now!!!
devID = (DBMgr::DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 );
PQclear( result );
return devID;
}
DBMgr::DevIDRelay
DBMgr::getDevID( const DevID* devID )
{
DBMgr::DevIDRelay rDevID = DEVID_NONE;
DevIDType devIDType = devID->m_devIDType;
assert( ID_TYPE_NONE < devIDType );
const char* asStr = devID->m_devIDString.c_str();
if ( ID_TYPE_RELAY == devIDType ) {
rDevID = strtoul( asStr, NULL, 16 );
} else {
const char* fmt = "SELECT id FROM " DEVICES_TABLE " WHERE devtype=%d and devid = '%s'";
char query[512];
snprintf( query, sizeof(query), fmt, devIDType, asStr );
logf( XW_LOGINFO, "%s: query: %s", __func__, query );
PGresult* result = PQexec( getThreadConn(), query );
assert( 1 >= PQntuples( result ) );
if ( 1 == PQntuples( result ) ) {
rDevID = (DBMgr::DevIDRelay)strtoul( PQgetvalue( result, 0, 0 ), NULL, 10 );
}
PQclear( result );
}
return rDevID;
}
/*
@ -625,13 +698,12 @@ void
DBMgr::StoreMessage( const char* const connName, int hid,
const unsigned char* buf, int len )
{
DevID devID;
getDevID( connName, hid, devID );
DevIDRelay devID = getDevID( connName, hid );
size_t newLen;
const char* fmt = "INSERT INTO " MSGS_TABLE
" (connname, hid, devid, devType, msg, msglen)"
" VALUES( '%s', %d, '%s', %d, E'%s', %d)";
" (connname, hid, devid, msg, msglen)"
" VALUES( '%s', %d, %d, E'%s', %d)";
unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf,
len, &newLen );
@ -639,8 +711,7 @@ DBMgr::StoreMessage( const char* const connName, int hid,
char query[1024];
size_t siz = snprintf( query, sizeof(query), fmt, connName, hid,
devID.m_devIDString.c_str(),
devID.m_devIDType, bytes, len );
devID, bytes, len );
PQfreemem( bytes );

View file

@ -26,13 +26,19 @@
#include "xwrelay.h"
#include "xwrelay_priv.h"
#include "cref.h"
#include "devid.h"
#include <libpq-fe.h>
using namespace std;
class DBMgr {
public:
/* DevIDs on various platforms are stored in devices table. This is the
key, and used in msgs and games tables as a shorter way to refer to
them. */
typedef unsigned int DevIDRelay;
static const DevIDRelay DEVID_NONE = 0;
static DBMgr* Get();
~DBMgr();
@ -56,9 +62,11 @@ class DBMgr {
char* connNameBuf, int bufLen, int* nPlayersHP );
bool AllDevsAckd( const char* const connName );
DevIDRelay RegisterDevice( const DevID* hosts );
HostID AddDevice( const char* const connName, HostID curID, int clientVersion,
int nToAdd, unsigned short seed, const in_addr& addr,
const DevID* devID, bool unAckd );
DevIDRelay devID, bool unAckd );
void NoteAckd( const char* const connName, HostID id );
HostID HIDForSeed( const char* const connName, unsigned short seed );
bool RmDeviceByHid( const char* const connName, HostID id );
@ -99,7 +107,8 @@ class DBMgr {
DBMgr();
bool execSql( const char* const query ); /* no-results query */
void readArray( const char* const connName, int arr[] );
void getDevID( const char* connName, int hid, DevID& devID );
DevIDRelay getDevID( const char* connName, int hid );
DevIDRelay getDevID( const DevID* devID );
PGconn* getThreadConn( void );

64
xwords4/relay/devid.h Normal file
View file

@ -0,0 +1,64 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2005-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.
*
* 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.
*/
#ifndef _DEVID_H_
#define _DEVID_H_
#include <string>
#include "xwrelay.h"
/* DevID protocol.
*
* There are two types. The first, with a DevIDType greater than
* ID_TYPE_RELAY, is platform-specific and meaningless to the relay (though as
* with GCM-based IDs on Android there may be server code that uses it.) The
* second, with type of ID_TYPE_RELAY, is specific to the relay. When the
* relay sees one of the first type, it creates an entry in the devices table
* with a new random 32-bit index that is then used in the msgs and games
* tables. This index is the second type.
*
* A device always includes a DevID when creating a new game. It may be of
* either type, and generally should use the latter when possible. Since the
* latter comes from the relay, the first time a device connects (after
* whatever local event, e.g. registration with GCM, causes it to have an ID of
* the first type that it wants to share) it will have to send the first type.
* When replying to a registration message that included a DevID of the first
* type, the relay always sends the corresponding DevID of the second type,
* which it expects the device to remember. But when replying after receiving a
* DevID of the second type the relay does not echo that value (sends an empty
* string).
*
* Devices or platforms not providing a DevID will return ID_TYPE_NONE as the
* type via util_getDevID(). That single byte will be transmitted to the relay
* which will then skip the registration process and will return an empty
* string as the relay-type DevID in the connection response.
*
*/
using namespace std;
class DevID {
public:
DevID() { m_devIDType = ID_TYPE_NONE; }
string m_devIDString;
DevIDType m_devIDType;
};
#endif

View file

@ -26,12 +26,15 @@ QUERY="WHERE NOT -NTOTAL = sum_array(nperdevice)"
echo "Device (pid) count: $(pidof xwords | wc | awk '{print $2}')"
echo "Row count:" $(psql -t xwgames -c "select count(*) FROM games $QUERY;")
echo "SELECT dead,connname,cid,room,lang,clntVers,ntotal,nperdevice,seeds,devTypes,devids,ack,nsent "\
echo "SELECT dead,connname,cid,room,lang,clntVers,ntotal,nperdevice,seeds,devids,ack,nsent "\
"FROM games $QUERY ORDER BY NOT dead, connname LIMIT $LIMIT;" \
| psql xwgames
echo "SELECT connname, hid, devType, devid, count(*), sum(msglen) "\
echo "SELECT connname, hid, devid, count(*), sum(msglen) "\
"FROM msgs where connname in (SELECT connname from games where not games.dead group by connname)" \
"GROUP BY connname, hid, devType, devid ORDER BY connname;" \
"GROUP BY connname, hid, devid ORDER BY connname;" \
| psql xwgames
echo "SELECT * from devices;" \
| psql xwgames

View file

@ -386,7 +386,7 @@ processConnect( unsigned char* bufp, int bufLen, int socket, in_addr& addr )
if ( getNetByte( &bufp, end, &devIDType )
&& 0 != devIDType ) {
getNetString( &bufp, end, devID.m_devIDString );
devID.m_devIDType = devIDType;
devID.m_devIDType = (DevIDType)devIDType;
}
}

View file

@ -105,6 +105,13 @@ enum { XWRELAY_NONE /* 0 is an illegal value */
#endif
;
typedef enum {
ID_TYPE_NONE
,ID_TYPE_RELAY /* assigned by relay as replacement for one of the below */
,ID_TYPE_LINUX
,ID_TYPE_ANDROID_GCM
} DevIDType;
#ifndef CANT_DO_TYPEDEF
typedef unsigned char XWRELAY_Cmd;
#endif
@ -115,6 +122,7 @@ typedef unsigned char XWRELAY_Cmd;
#define MAX_INVITE_LEN 31
#define MAX_MSG_LEN 1024 /* Used for proxy too! */
#define MAX_CONNNAME_LEN 48 /* host ID, boot time, and seeds as hex? */
#define MAX_DEVID_LEN 8 /* 32-bit number as hex */
#define XWRELAY_PROTO_VERSION_ORIG 0x01
#define XWRELAY_PROTO_VERSION_LATE_NAME 0x02

View file

@ -58,8 +58,7 @@ cid integer
,ctime TIMESTAMP (0) DEFAULT CURRENT_TIMESTAMP
,mtimes TIMESTAMP(0)[]
,addrs INET[]
,devTypes INTEGER[]
,devids TEXT[]
,devids INTEGER[]
);
EOF
@ -69,12 +68,20 @@ id SERIAL
,connName VARCHAR(64)
,hid INTEGER
,ctime TIMESTAMP DEFAULT CURRENT_TIMESTAMP
,devType INTEGER
,devid TEXT
,devid INTEGER
,msg BYTEA
,msglen INTEGER
,UNIQUE ( connName, hid, msg )
);
EOF
cat | psql $DBNAME --file - <<EOF
CREATE TABLE devices (
id INTEGER UNIQUE PRIMARY KEY
,devType INTEGER
,devid TEXT
,ctime TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
EOF
}