mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-09 05:24:44 +01:00
Rename SMSService NBSProxy, and make it not a service
Just keep a thread for each phone we're sending to or receiving from, and pass them packets and other things to send or (incoming case) process. So far the threads don't die, but they will soon once there's nothing in their queues. Just need to be sure there's no racing there. This is to work around the frequent failure of the OS to pass enqeued work into the service within a reasonable amount of time (I expect seconds, but see delays of minutes or even hours.)
This commit is contained in:
parent
0251ff0b44
commit
35bcc3910c
20 changed files with 589 additions and 724 deletions
|
@ -186,10 +186,6 @@
|
|||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false"
|
||||
/>
|
||||
<service android:name="SMSService"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
/>
|
||||
<service android:name="RelayService"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
|
|
|
@ -457,9 +457,7 @@ public class BTService extends XWJIService {
|
|||
CommsAddrRec addr = new CommsAddrRec( host.getName(),
|
||||
host.getAddress() );
|
||||
XWServiceHelper.ReceiveResult rslt
|
||||
= mHelper.receiveMessage( this, gameID,
|
||||
m_btMsgSink,
|
||||
buf, addr );
|
||||
= mHelper.receiveMessage( gameID, m_btMsgSink, buf, addr );
|
||||
|
||||
BTCmd response = rslt == XWServiceHelper.ReceiveResult.GAME_GONE ?
|
||||
BTCmd.MESG_GAMEGONE : BTCmd.MESG_ACCPT;
|
||||
|
@ -748,8 +746,7 @@ public class BTService extends XWJIService {
|
|||
String btAddr )
|
||||
{
|
||||
BTCmd result;
|
||||
if ( mHelper.handleInvitation( this, nli, btName,
|
||||
DictFetchOwner.OWNER_BT ) ) {
|
||||
if ( mHelper.handleInvitation( nli, btName, DictFetchOwner.OWNER_BT ) ) {
|
||||
result = BTCmd.INVITE_ACCPT;
|
||||
} else {
|
||||
result = BTCmd.INVITE_DUP_INVITE; // dupe of rematch
|
||||
|
|
|
@ -2998,7 +2998,7 @@ public class BoardDelegate extends DelegateBase
|
|||
boolean askOk )
|
||||
{
|
||||
if ( XWPrefs.getNBSEnabled( m_activity ) ) {
|
||||
SMSService.inviteRemote( m_activity, phone, nli );
|
||||
NBSProto.inviteRemote( m_activity, phone, nli );
|
||||
recordInviteSent( InviteMeans.SMS_DATA, phone );
|
||||
} else if ( askOk ) {
|
||||
makeConfirmThenBuilder( R.string.warn_sms_disabled,
|
||||
|
|
|
@ -427,8 +427,8 @@ public class CommsTransport implements TransportProcs,
|
|||
nSent = RelayService.sendPacket( context, rowID, buf );
|
||||
break;
|
||||
case COMMS_CONN_SMS:
|
||||
nSent = SMSService.sendPacket( context, addr.sms_phone,
|
||||
gameID, buf );
|
||||
nSent = NBSProto.sendPacket( context, addr.sms_phone,
|
||||
gameID, buf );
|
||||
break;
|
||||
case COMMS_CONN_BT:
|
||||
nSent = BTService.sendPacket( context, buf, msgID, addr, gameID );
|
||||
|
|
|
@ -782,7 +782,7 @@ public class GameConfigDelegate extends DelegateBase
|
|||
|
||||
private void showConnAfterCheck()
|
||||
{
|
||||
if ( null == SMSService.getPhoneInfo( m_activity ) ) {
|
||||
if ( null == NBSProto.getPhoneInfo( m_activity ) ) {
|
||||
Perms23.tryGetPerms( this, Perms23.Perm.READ_PHONE_STATE,
|
||||
R.string.phone_state_rationale,
|
||||
Action.ASKED_PHONE_STATE );
|
||||
|
|
|
@ -1272,7 +1272,7 @@ public class GameUtils {
|
|||
BTService.gameDied( context, addr.bt_btAddr, gameID );
|
||||
break;
|
||||
case COMMS_CONN_SMS:
|
||||
SMSService.gameDied( context, gameID, addr.sms_phone );
|
||||
NBSProto.gameDied( context, gameID, addr.sms_phone );
|
||||
break;
|
||||
case COMMS_CONN_P2P:
|
||||
WiDirService.gameDied( addr.p2p_addr, gameID );
|
||||
|
|
|
@ -2230,7 +2230,7 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
int bits = extras.getInt( REMATCH_ADDRS_EXTRA, -1 );
|
||||
final CommsConnTypeSet addrs = new CommsConnTypeSet( bits );
|
||||
boolean hasSMS = addrs.contains( CommsConnType.COMMS_CONN_SMS );
|
||||
if ( !hasSMS || null != SMSService.getPhoneInfo( m_activity ) ) {
|
||||
if ( !hasSMS || null != NBSProto.getPhoneInfo( m_activity ) ) {
|
||||
rematchWithNameAndPerm( gameName, addrs );
|
||||
} else {
|
||||
int id = (1 == addrs.size())
|
||||
|
|
|
@ -70,7 +70,7 @@ public class MultiMsgSink implements TransportProcs {
|
|||
|
||||
public int sendViaSMS( byte[] buf, int gameID, CommsAddrRec addr )
|
||||
{
|
||||
return SMSService.sendPacket( m_context, addr.sms_phone, gameID, buf );
|
||||
return NBSProto.sendPacket( m_context, addr.sms_phone, gameID, buf );
|
||||
}
|
||||
|
||||
public int sendViaP2P( byte[] buf, int gameID, CommsAddrRec addr )
|
||||
|
|
|
@ -209,7 +209,7 @@ public class MultiService {
|
|||
DictFetchOwner owner = DictFetchOwner.values()[ordinal];
|
||||
switch ( owner ) {
|
||||
case OWNER_SMS:
|
||||
SMSService.onGameDictDownload( context, intent );
|
||||
NBSProto.onGameDictDownload( context, intent );
|
||||
break;
|
||||
case OWNER_RELAY:
|
||||
case OWNER_BT:
|
||||
|
|
|
@ -0,0 +1,542 @@
|
|||
/* -*- compile-command: "find-and-gradle.sh inXw4Deb"; -*- */
|
||||
/*
|
||||
* Copyright 2010 - 2018 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.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.telephony.SmsManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eehouse.android.nbsplib.NBSProxy;
|
||||
|
||||
import org.eehouse.android.xw4.MultiService.DictFetchOwner;
|
||||
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
import org.eehouse.android.xw4.jni.XwJNI.SMSProtoMsg;
|
||||
import org.eehouse.android.xw4.jni.XwJNI.SMS_CMD;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
public class NBSProto {
|
||||
private static final String TAG = NBSProto.class.getSimpleName();
|
||||
|
||||
private static final String MSG_SENT = "MSG_SENT";
|
||||
private static final String MSG_DELIVERED = "MSG_DELIVERED";
|
||||
|
||||
private static Set<Integer> s_sentDied = new HashSet<Integer>();
|
||||
|
||||
public static void handleFrom( Context context, byte[] buffer,
|
||||
String phone, short port )
|
||||
{
|
||||
getCurReceiver( phone, port ).addPacket( context, buffer );
|
||||
}
|
||||
|
||||
public static void onGameDictDownload( Context context, Intent intentOld )
|
||||
{
|
||||
}
|
||||
|
||||
public static void inviteRemote( Context context, String phone,
|
||||
NetLaunchInfo nli )
|
||||
{
|
||||
getCurSender( phone ).addInvite( context, nli );
|
||||
}
|
||||
|
||||
public static int sendPacket( Context context, String phone,
|
||||
int gameID, byte[] binmsg )
|
||||
{
|
||||
getCurSender( phone ).addPacket( context, gameID, binmsg );
|
||||
return binmsg.length;
|
||||
}
|
||||
|
||||
public static void gameDied( Context context, int gameID, String phone )
|
||||
{
|
||||
getCurSender( phone ).addDeathNotice( context, gameID );
|
||||
}
|
||||
|
||||
public static void stopService( Context context )
|
||||
{
|
||||
Log.d( TAG, "stopService() does nothing" );
|
||||
}
|
||||
|
||||
private static boolean s_showToasts;
|
||||
public static void smsToastEnable( boolean newVal )
|
||||
{
|
||||
s_showToasts = newVal;
|
||||
}
|
||||
|
||||
public static class SMSPhoneInfo {
|
||||
public SMSPhoneInfo( boolean isAPhone, String num, boolean gsm ) {
|
||||
isPhone = isAPhone;
|
||||
number = num;
|
||||
isGSM = gsm;
|
||||
}
|
||||
public boolean isPhone;
|
||||
public String number;
|
||||
public boolean isGSM;
|
||||
}
|
||||
|
||||
private static SMSPhoneInfo s_phoneInfo;
|
||||
public static SMSPhoneInfo getPhoneInfo( Context context )
|
||||
{
|
||||
if ( null == s_phoneInfo ) {
|
||||
try {
|
||||
String number = null;
|
||||
boolean isGSM = false;
|
||||
boolean isPhone = false;
|
||||
TelephonyManager mgr = (TelephonyManager)
|
||||
context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if ( null != mgr ) {
|
||||
number = mgr.getLine1Number(); // needs permission
|
||||
int type = mgr.getPhoneType();
|
||||
isGSM = TelephonyManager.PHONE_TYPE_GSM == type;
|
||||
isPhone = true;
|
||||
}
|
||||
|
||||
String radio =
|
||||
XWPrefs.getPrefsString( context, R.string.key_force_radio );
|
||||
int[] ids = { R.string.radio_name_real,
|
||||
R.string.radio_name_tablet,
|
||||
R.string.radio_name_gsm,
|
||||
R.string.radio_name_cdma,
|
||||
};
|
||||
|
||||
// default so don't crash before set
|
||||
int id = R.string.radio_name_real;
|
||||
for ( int ii = 0; ii < ids.length; ++ii ) {
|
||||
if ( radio.equals(context.getString(ids[ii])) ) {
|
||||
id = ids[ii];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch( id ) {
|
||||
case R.string.radio_name_real:
|
||||
break; // go with above
|
||||
case R.string.radio_name_tablet:
|
||||
number = null;
|
||||
isPhone = false;
|
||||
break;
|
||||
case R.string.radio_name_gsm:
|
||||
case R.string.radio_name_cdma:
|
||||
isGSM = id == R.string.radio_name_gsm;
|
||||
if ( null == number ) {
|
||||
number = "000-000-0000";
|
||||
}
|
||||
isPhone = true;
|
||||
break;
|
||||
}
|
||||
|
||||
s_phoneInfo = new SMSPhoneInfo( isPhone, number, isGSM );
|
||||
} catch ( SecurityException se ) {
|
||||
Log.e( TAG, "got SecurityException" );
|
||||
}
|
||||
}
|
||||
return s_phoneInfo;
|
||||
}
|
||||
|
||||
public static void resetPhoneInfo()
|
||||
{
|
||||
s_phoneInfo = null;
|
||||
}
|
||||
|
||||
|
||||
private static class SendThread extends Thread {
|
||||
private String mPhone;
|
||||
private short mPort;
|
||||
private LinkedBlockingQueue<SendElem> mQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
SendThread( String phone ) {
|
||||
mPhone = phone;
|
||||
mPort = getNBSPort();
|
||||
}
|
||||
|
||||
String getPhone() { return mPhone; }
|
||||
|
||||
void addPacket( Context context, int gameID, byte[] binmsg )
|
||||
{
|
||||
mQueue.add( new SendElem( context, SMS_CMD.DATA, gameID, binmsg ) );
|
||||
}
|
||||
|
||||
void addInvite( Context context, NetLaunchInfo nli )
|
||||
{
|
||||
mQueue.add( new SendElem( context, SMS_CMD.INVITE, nli ) );
|
||||
}
|
||||
|
||||
void addDeathNotice( Context context, int gameID )
|
||||
{
|
||||
mQueue.add( new SendElem( context, SMS_CMD.DEATH, gameID, null ) );
|
||||
}
|
||||
|
||||
void addAck( Context context, int gameID )
|
||||
{
|
||||
mQueue.add( new SendElem( context, SMS_CMD.ACK_INVITE, gameID, null ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d( TAG, "%s.run() starting for %s", this, mPhone );
|
||||
int[] waitSecs = { 5 };
|
||||
|
||||
while ( !isInterrupted() ) {
|
||||
SendElem elem;
|
||||
|
||||
try {
|
||||
elem = mQueue.poll( waitSecs[0], TimeUnit.SECONDS );
|
||||
|
||||
byte[][] msgs;
|
||||
if ( null == elem ) { // timed out?
|
||||
// removeSelf( this );
|
||||
msgs = XwJNI.smsproto_prepOutbound( SMS_CMD.NONE, 0, null, mPhone,
|
||||
mPort, true, waitSecs );
|
||||
} else {
|
||||
boolean newSMSEnabled = XWPrefs.getSMSProtoEnabled( elem.context );
|
||||
boolean forceNow = !newSMSEnabled; // || cmd == SMS_CMD.INVITE;
|
||||
msgs = XwJNI.smsproto_prepOutbound( elem.cmd, elem.gameID, elem.data, mPhone,
|
||||
mPort, forceNow, waitSecs );
|
||||
}
|
||||
|
||||
if ( null != msgs ) {
|
||||
sendBuffers( msgs, mPhone, mPort );
|
||||
}
|
||||
if ( waitSecs[0] <= 0 ) {
|
||||
waitSecs[0] = 5;
|
||||
}
|
||||
} catch ( InterruptedException iex ) {
|
||||
Log.d( TAG, "poll() threw: %s", iex.getMessage() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// To be safe, call again
|
||||
removeSelf( this );
|
||||
|
||||
// Better not be leaving anything behind!
|
||||
Assert.assertTrue( 0 == mQueue.size() || !BuildConfig.DEBUG );
|
||||
Log.d( TAG, "%s.run() DONE", this );
|
||||
}
|
||||
|
||||
private void sendBuffers( byte[][] fragments, String phone, short nbsPort )
|
||||
{
|
||||
Context context = XWApp.getContext();
|
||||
boolean success = false;
|
||||
if ( XWPrefs.getNBSEnabled( context ) ) {
|
||||
|
||||
// Try send-to-self
|
||||
if ( XWPrefs.getSMSToSelfEnabled( context ) ) {
|
||||
String myPhone = getPhoneInfo( context ).number;
|
||||
if ( null != myPhone
|
||||
&& PhoneNumberUtils.compare( phone, myPhone ) ) {
|
||||
for ( byte[] fragment : fragments ) {
|
||||
handleFrom( context, fragment, phone, nbsPort );
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !success ) {
|
||||
try {
|
||||
SmsManager mgr = SmsManager.getDefault();
|
||||
boolean useProxy = Perms23.Perm.SEND_SMS.isBanned( context )
|
||||
&& NBSProxy.isInstalled( context );
|
||||
PendingIntent sent = useProxy ? null
|
||||
: makeStatusIntent( context, MSG_SENT );
|
||||
PendingIntent delivery = useProxy ? null
|
||||
: makeStatusIntent( context, MSG_DELIVERED );
|
||||
for ( byte[] fragment : fragments ) {
|
||||
if ( useProxy ) {
|
||||
NBSProxy.send( context, phone, nbsPort, fragment );
|
||||
} else {
|
||||
mgr.sendDataMessage( phone, null, nbsPort, fragment,
|
||||
sent, delivery );
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
} catch ( IllegalArgumentException iae ) {
|
||||
Log.w( TAG, "sendBuffers(%s): %s", phone, iae.toString() );
|
||||
} catch ( NullPointerException npe ) {
|
||||
Assert.assertFalse( BuildConfig.DEBUG ); // shouldn't be trying to do this!!!
|
||||
} catch ( java.lang.SecurityException se ) {
|
||||
// getHelper(context).postEvent( MultiEvent.SMS_SEND_FAILED_NOPERMISSION );
|
||||
Assert.assertFalse( BuildConfig.DEBUG );
|
||||
} catch ( Exception ee ) {
|
||||
Log.ex( TAG, ee );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.i( TAG, "dropping because SMS disabled" );
|
||||
}
|
||||
|
||||
// if ( showToasts( context ) && success && (0 == (++s_nSent % 5)) ) {
|
||||
// DbgUtils.showf( context, "Sent msg %d", s_nSent );
|
||||
// }
|
||||
|
||||
ConnStatusHandler.updateStatusOut( context, null,
|
||||
CommsConnType.COMMS_CONN_SMS,
|
||||
success );
|
||||
}
|
||||
|
||||
private PendingIntent makeStatusIntent( Context context, String msg )
|
||||
{
|
||||
Intent intent = new Intent( msg );
|
||||
return PendingIntent.getBroadcast( context, 0, intent, 0 );
|
||||
}
|
||||
|
||||
private static class SendElem {
|
||||
Context context;
|
||||
SMS_CMD cmd;
|
||||
int gameID;
|
||||
byte[] data;
|
||||
SendElem( Context context, SMS_CMD cmd, int gameID, byte[] data ) {
|
||||
this.context = context;
|
||||
this.cmd = cmd;
|
||||
this.gameID = gameID;
|
||||
this.data = data;
|
||||
}
|
||||
SendElem( Context context, SMS_CMD cmd, NetLaunchInfo nli ) {
|
||||
this( context, cmd, 0, nli.asByteArray() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, SendThread> sSendersMap = new HashMap<>();
|
||||
|
||||
private static SendThread getCurSender( String toPhone )
|
||||
{
|
||||
SendThread result = null;
|
||||
synchronized ( sSendersMap ) {
|
||||
result = sSendersMap.get( toPhone );
|
||||
if ( result == null ) {
|
||||
result = new SendThread( toPhone );
|
||||
result.start();
|
||||
sSendersMap.put( toPhone, result );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void removeSelf( SendThread self )
|
||||
{
|
||||
synchronized ( sSendersMap ) {
|
||||
String phone = self.getPhone();
|
||||
if ( sSendersMap.get(phone) == self ) {
|
||||
sSendersMap.remove( phone );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReceiveThread extends Thread {
|
||||
private String mPhone;
|
||||
private short mPort;
|
||||
|
||||
private LinkedBlockingQueue<ReceiveElem> mQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
ReceiveThread( String fromPhone, short port ) {
|
||||
mPhone = fromPhone;
|
||||
mPort = port;
|
||||
}
|
||||
|
||||
short getPort() { return mPort; }
|
||||
|
||||
void addPacket( Context context, byte[] data )
|
||||
{
|
||||
mQueue.add( new ReceiveElem( context, data ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d( TAG, "%s.run() starting", this );
|
||||
while ( ! isInterrupted() ) {
|
||||
ReceiveElem elem;
|
||||
try {
|
||||
elem = mQueue.poll( 10, TimeUnit.SECONDS );
|
||||
|
||||
if ( null != elem ) {
|
||||
SMSProtoMsg[] msgs = XwJNI.smsproto_prepInbound( elem.data, mPhone, mPort );
|
||||
if ( null != msgs ) {
|
||||
Log.d( TAG, "got %d msgs combined!", msgs.length );
|
||||
for ( int ii = 0; ii < msgs.length; ++ii ) {
|
||||
Log.d( TAG, "%d: type: %s; len: %d", ii, msgs[ii].cmd, msgs[ii].data.length );
|
||||
}
|
||||
for ( SMSProtoMsg msg : msgs ) {
|
||||
receive( elem.context, msg );
|
||||
}
|
||||
getHelper(elem.context).postEvent( MultiEvent.SMS_RECEIVE_OK );
|
||||
} else {
|
||||
Log.d( TAG, "receiveBuffer(): bogus or incomplete message from %s",
|
||||
mPhone );
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException iex) {
|
||||
Log.d( TAG, "%s.poll() threw: %s", this, iex.getMessage() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
Log.d( TAG, "%s.run() DONE", this );
|
||||
}
|
||||
|
||||
private void receive( Context context, SMSProtoMsg msg )
|
||||
{
|
||||
Log.i( TAG, "receive(cmd=%s)", msg.cmd );
|
||||
switch( msg.cmd ) {
|
||||
case INVITE:
|
||||
makeForInvite( context, NetLaunchInfo.makeFrom( context, msg.data ) );
|
||||
break;
|
||||
case DATA:
|
||||
if ( feedMessage( context, msg.gameID, msg.data, new CommsAddrRec( mPhone ) ) ) {
|
||||
SMSResendReceiver.resetTimer( context );
|
||||
}
|
||||
break;
|
||||
case DEATH:
|
||||
getHelper(context).postEvent( MultiEvent.MESSAGE_NOGAME, msg.gameID );
|
||||
break;
|
||||
case ACK_INVITE:
|
||||
getHelper(context).postEvent( MultiEvent.NEWGAME_SUCCESS, msg.gameID );
|
||||
break;
|
||||
default:
|
||||
Log.w( TAG, "unexpected cmd %s", msg.cmd );
|
||||
Assert.assertFalse( BuildConfig.DEBUG );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean feedMessage( Context context, int gameID, byte[] msg,
|
||||
CommsAddrRec addr )
|
||||
{
|
||||
XWServiceHelper.ReceiveResult rslt = getHelper(context)
|
||||
.receiveMessage( gameID, null, msg, addr );
|
||||
if ( XWServiceHelper.ReceiveResult.GAME_GONE == rslt ) {
|
||||
sendDiedPacket( context, addr.sms_phone, gameID );
|
||||
}
|
||||
return rslt == XWServiceHelper.ReceiveResult.OK;
|
||||
}
|
||||
|
||||
private void sendDiedPacket( Context context, String phone, int gameID )
|
||||
{
|
||||
if ( !s_sentDied.contains( gameID ) ) {
|
||||
getCurSender( phone ).addDeathNotice( context, gameID );
|
||||
// resendFor( phone, SMS_CMD.DEATH, gameID, null );
|
||||
s_sentDied.add( gameID );
|
||||
}
|
||||
}
|
||||
|
||||
private void makeForInvite( Context context, NetLaunchInfo nli )
|
||||
{
|
||||
if ( nli != null ) {
|
||||
getHelper(context).handleInvitation( nli, mPhone, DictFetchOwner.OWNER_SMS );
|
||||
getCurSender( mPhone ).addAck( context, nli.gameID() );
|
||||
}
|
||||
}
|
||||
|
||||
private SMSServiceHelper mHelper = null;
|
||||
private SMSServiceHelper getHelper( Context context )
|
||||
{
|
||||
if ( null == mHelper ) {
|
||||
mHelper = new SMSServiceHelper( context );
|
||||
}
|
||||
return mHelper;
|
||||
}
|
||||
|
||||
private static class ReceiveElem {
|
||||
Context context;
|
||||
byte[] data;
|
||||
ReceiveElem( Context context, byte[] data )
|
||||
{
|
||||
this.context = context;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, ReceiveThread> sReceiversMap = new HashMap<>();
|
||||
private static ReceiveThread getCurReceiver( String fromPhone, short port )
|
||||
{
|
||||
ReceiveThread result = null;
|
||||
synchronized ( sReceiversMap ) {
|
||||
result = sReceiversMap.get( fromPhone );
|
||||
if ( result == null ) {
|
||||
result = new ReceiveThread( fromPhone, port );
|
||||
result.start();
|
||||
sReceiversMap.put( fromPhone, result );
|
||||
} else {
|
||||
Assert.assertTrue( port == result.getPort() || !BuildConfig.DEBUG );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class SMSMsgSink extends MultiMsgSink {
|
||||
private Context mContext;
|
||||
public SMSMsgSink( Context context ) {
|
||||
super( context );
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendViaSMS( byte[] buf, int gameID, CommsAddrRec addr )
|
||||
{
|
||||
return sendPacket( mContext, addr.sms_phone, gameID, buf );
|
||||
}
|
||||
}
|
||||
|
||||
private static class SMSServiceHelper extends XWServiceHelper {
|
||||
private Context mContext;
|
||||
|
||||
SMSServiceHelper( Context context ) {
|
||||
super( context );
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiMsgSink getSink( long rowid )
|
||||
{
|
||||
return new SMSMsgSink( mContext );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postNotification( String phone, int gameID, long rowid )
|
||||
{
|
||||
String owner = Utils.phoneToContact( mContext, phone, true );
|
||||
String body = LocUtils.getString( mContext, R.string.new_name_body_fmt,
|
||||
owner );
|
||||
GameUtils.postInvitedNotification( mContext, gameID, body, rowid );
|
||||
}
|
||||
}
|
||||
|
||||
private static Short s_nbsPort = null;
|
||||
private static short getNBSPort()
|
||||
{
|
||||
if ( null == s_nbsPort ) {
|
||||
String asStr = XWApp.getContext().getString( R.string.nbs_port );
|
||||
s_nbsPort = new Short((short)Integer.parseInt( asStr ) );
|
||||
}
|
||||
return s_nbsPort;
|
||||
}
|
||||
}
|
|
@ -591,7 +591,7 @@ public class NetLaunchInfo implements Serializable {
|
|||
|
||||
public void addSMSInfo( Context context )
|
||||
{
|
||||
SMSService.SMSPhoneInfo pi = SMSService.getPhoneInfo( context );
|
||||
NBSProto.SMSPhoneInfo pi = NBSProto.getPhoneInfo( context );
|
||||
if ( null != pi ) {
|
||||
phone = pi.number;
|
||||
isGSM = pi.isGSM;
|
||||
|
|
|
@ -189,11 +189,11 @@ public class PrefsDelegate extends DelegateBase
|
|||
Log.enable( sp.getBoolean( key, false ) );
|
||||
break;
|
||||
case R.string.key_show_sms:
|
||||
SMSService.smsToastEnable( sp.getBoolean( key, false ) );
|
||||
NBSProto.smsToastEnable( sp.getBoolean( key, false ) );
|
||||
break;
|
||||
case R.string.key_enable_nbs:
|
||||
if ( ! sp.getBoolean( key, true ) ) {
|
||||
SMSService.stopService( m_activity );
|
||||
NBSProto.stopService( m_activity );
|
||||
}
|
||||
break;
|
||||
case R.string.key_download_path:
|
||||
|
@ -224,7 +224,7 @@ public class PrefsDelegate extends DelegateBase
|
|||
forceDictsMatch( sp.getString( key, null ) );
|
||||
break;
|
||||
case R.string.key_force_radio:
|
||||
SMSService.resetPhoneInfo();
|
||||
NBSProto.resetPhoneInfo();
|
||||
break;
|
||||
case R.string.key_disable_nag:
|
||||
case R.string.key_disable_nag_solo:
|
||||
|
|
|
@ -298,7 +298,7 @@ public class RelayService extends XWJIService
|
|||
{
|
||||
Log.d( TAG, "receiveInvitation: got nli from %d: %s", srcDevID,
|
||||
nli.toString() );
|
||||
if ( !mHelper.handleInvitation( this, nli, null,
|
||||
if ( !mHelper.handleInvitation( nli, null,
|
||||
DictFetchOwner.OWNER_RELAY ) ) {
|
||||
Log.d( TAG, "handleInvitation() failed" );
|
||||
}
|
||||
|
@ -491,7 +491,7 @@ public class RelayService extends XWJIService
|
|||
String msgNo = intent.getStringExtra( MSGNUM );
|
||||
sendNoConnMessage( rowid, relayID, msg, msgNo, timestamp );
|
||||
} else {
|
||||
mHelper.receiveMessage( this, rowid, null, msg, s_addr );
|
||||
mHelper.receiveMessage( rowid, null, msg, s_addr );
|
||||
}
|
||||
break;
|
||||
case INVITE:
|
||||
|
@ -1253,7 +1253,7 @@ public class RelayService extends XWJIService
|
|||
long rowid = rowIDs[ii];
|
||||
sink.setRowID( rowid );
|
||||
for ( byte[] msg : forOne ) {
|
||||
mHelper.receiveMessage( this, rowid, sink, msg, s_addr );
|
||||
mHelper.receiveMessage( rowid, sink, msg, s_addr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ public class SMSReceiver extends BroadcastReceiver {
|
|||
try {
|
||||
String phone = sms.getOriginatingAddress();
|
||||
byte[] body = sms.getUserData();
|
||||
SMSService.handleFrom( context, body, phone );
|
||||
NBSProto.handleFrom( context, body, phone,
|
||||
getConfiguredPort( context ) );
|
||||
} catch ( NullPointerException npe ) {
|
||||
Log.ex( TAG, npe );
|
||||
}
|
||||
|
|
|
@ -1,670 +0,0 @@
|
|||
/* -*- compile-command: "find-and-gradle.sh inXw4Deb"; -*- */
|
||||
/*
|
||||
* Copyright 2010 - 2018 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.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.telephony.SmsManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import org.eehouse.android.nbsplib.NBSProxy;
|
||||
|
||||
import org.eehouse.android.xw4.MultiService.DictFetchOwner;
|
||||
import org.eehouse.android.xw4.MultiService.MultiEvent;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
import org.eehouse.android.xw4.jni.XwJNI.SMSProtoMsg;
|
||||
import org.eehouse.android.xw4.jni.XwJNI.SMS_CMD;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SMSService extends XWJIService {
|
||||
private static final String TAG = SMSService.class.getSimpleName();
|
||||
|
||||
private final static int sJobID = 218719980;
|
||||
static {
|
||||
XWJIService.register( SMSService.class, sJobID );
|
||||
}
|
||||
|
||||
private static final String MSG_SENT = "MSG_SENT";
|
||||
private static final String MSG_DELIVERED = "MSG_DELIVERED";
|
||||
|
||||
private static final int SMS_PROTO_VERSION_WITHPORT = 1;
|
||||
private static final int SMS_PROTO_VERSION = SMS_PROTO_VERSION_WITHPORT;
|
||||
private enum SMSAction implements XWJICmds { _NONE,
|
||||
INVITE,
|
||||
SEND,
|
||||
REMOVE,
|
||||
ADDED_MISSING,
|
||||
STOP_SELF,
|
||||
HANDLEDATA,
|
||||
RESEND,
|
||||
};
|
||||
|
||||
private static final String CMD_STR = "CMD";
|
||||
private static final String BUFFER = "BUFFER";
|
||||
private static final String BINBUFFER = "BINBUFFER";
|
||||
private static final String PHONE = "PHONE";
|
||||
private static final String GAMEDATA_BA = "GD";
|
||||
|
||||
private static final String PHONE_RECS_KEY =
|
||||
SMSService.class.getName() + "_PHONES";
|
||||
|
||||
private static Boolean s_showToasts = null;
|
||||
|
||||
private BroadcastReceiver m_sentReceiver;
|
||||
private BroadcastReceiver m_receiveReceiver;
|
||||
private OnSharedPreferenceChangeListener m_prefsListener;
|
||||
private SMSServiceHelper mHelper;
|
||||
|
||||
private int m_nReceived = 0;
|
||||
private static int s_nSent = 0;
|
||||
private static Set<Integer> s_sentDied = new HashSet<Integer>();
|
||||
|
||||
public static class SMSPhoneInfo {
|
||||
public SMSPhoneInfo( boolean isAPhone, String num, boolean gsm ) {
|
||||
isPhone = isAPhone;
|
||||
number = num;
|
||||
isGSM = gsm;
|
||||
}
|
||||
public boolean isPhone;
|
||||
public String number;
|
||||
public boolean isGSM;
|
||||
}
|
||||
|
||||
private static SMSPhoneInfo s_phoneInfo;
|
||||
public static SMSPhoneInfo getPhoneInfo( Context context )
|
||||
{
|
||||
if ( null == s_phoneInfo ) {
|
||||
try {
|
||||
String number = null;
|
||||
boolean isGSM = false;
|
||||
boolean isPhone = false;
|
||||
TelephonyManager mgr = (TelephonyManager)
|
||||
context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if ( null != mgr ) {
|
||||
number = mgr.getLine1Number(); // needs permission
|
||||
int type = mgr.getPhoneType();
|
||||
isGSM = TelephonyManager.PHONE_TYPE_GSM == type;
|
||||
isPhone = true;
|
||||
}
|
||||
|
||||
String radio =
|
||||
XWPrefs.getPrefsString( context, R.string.key_force_radio );
|
||||
int[] ids = { R.string.radio_name_real,
|
||||
R.string.radio_name_tablet,
|
||||
R.string.radio_name_gsm,
|
||||
R.string.radio_name_cdma,
|
||||
};
|
||||
|
||||
// default so don't crash before set
|
||||
int id = R.string.radio_name_real;
|
||||
for ( int ii = 0; ii < ids.length; ++ii ) {
|
||||
if ( radio.equals(context.getString(ids[ii])) ) {
|
||||
id = ids[ii];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch( id ) {
|
||||
case R.string.radio_name_real:
|
||||
break; // go with above
|
||||
case R.string.radio_name_tablet:
|
||||
number = null;
|
||||
isPhone = false;
|
||||
break;
|
||||
case R.string.radio_name_gsm:
|
||||
case R.string.radio_name_cdma:
|
||||
isGSM = id == R.string.radio_name_gsm;
|
||||
if ( null == number ) {
|
||||
number = "000-000-0000";
|
||||
}
|
||||
isPhone = true;
|
||||
break;
|
||||
}
|
||||
|
||||
s_phoneInfo = new SMSPhoneInfo( isPhone, number, isGSM );
|
||||
} catch ( SecurityException se ) {
|
||||
Log.e( TAG, "got SecurityException" );
|
||||
}
|
||||
}
|
||||
return s_phoneInfo;
|
||||
}
|
||||
|
||||
public static void resetPhoneInfo()
|
||||
{
|
||||
s_phoneInfo = null;
|
||||
}
|
||||
|
||||
public static void smsToastEnable( boolean newVal )
|
||||
{
|
||||
s_showToasts = newVal;
|
||||
}
|
||||
|
||||
public static void stopService( Context context )
|
||||
{
|
||||
Intent intent = getIntentTo( context, SMSAction.STOP_SELF );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
// NBS case
|
||||
public static void handleFrom( Context context, byte[] buffer,
|
||||
String phone )
|
||||
{
|
||||
Intent intent = getIntentTo( context, SMSAction.HANDLEDATA )
|
||||
.putExtra( BUFFER, buffer )
|
||||
.putExtra( PHONE, phone );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
public static void inviteRemote( Context context, String phone,
|
||||
NetLaunchInfo nli )
|
||||
{
|
||||
Log.w( TAG, "inviteRemote(%s, '%s')", phone, nli );
|
||||
byte[] data = nli.asByteArray();
|
||||
Intent intent = getIntentTo( context, SMSAction.INVITE )
|
||||
.putExtra( PHONE, phone )
|
||||
.putExtra( GAMEDATA_BA, data );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
public static int sendPacket( Context context, String phone,
|
||||
int gameID, byte[] binmsg )
|
||||
{
|
||||
int nSent = -1;
|
||||
if ( XWPrefs.getNBSEnabled( context ) ) {
|
||||
Intent intent = getIntentTo( context, SMSAction.SEND )
|
||||
.putExtra( PHONE, phone )
|
||||
.putExtra( MultiService.GAMEID, gameID )
|
||||
.putExtra( BINBUFFER, binmsg );
|
||||
enqueueWork( context, intent );
|
||||
nSent = binmsg.length;
|
||||
} else {
|
||||
Log.i( TAG, "sendPacket: dropping because SMS disabled" );
|
||||
}
|
||||
Log.d( TAG, "sendPacket(len=%d) => %d", binmsg.length, nSent );
|
||||
return nSent;
|
||||
}
|
||||
|
||||
public static void gameDied( Context context, int gameID, String phone )
|
||||
{
|
||||
Intent intent = getIntentTo( context, SMSAction.REMOVE )
|
||||
.putExtra( PHONE, phone )
|
||||
.putExtra( MultiService.GAMEID, gameID );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
public static void onGameDictDownload( Context context, Intent intentOld )
|
||||
{
|
||||
Intent intent = getIntentTo( context, SMSAction.ADDED_MISSING );
|
||||
intent.fillIn( intentOld, 0 );
|
||||
enqueueWork( context, intent );
|
||||
}
|
||||
|
||||
public static String fromPublicFmt( String msg )
|
||||
{
|
||||
String result = null;
|
||||
if ( null != msg && msg.startsWith( XWApp.SMS_PUBLIC_HEADER ) ) {
|
||||
// Number format exception etc. can result from malicious
|
||||
// messages. Be safe: use try;
|
||||
try {
|
||||
String tmp = msg.substring( 1 + msg.lastIndexOf( " " ) );
|
||||
|
||||
int headerLen = XWApp.SMS_PUBLIC_HEADER.length();
|
||||
String hashString =
|
||||
msg.substring( headerLen, headerLen + 4 );
|
||||
int hashRead = Integer.parseInt( hashString, 16 );
|
||||
int hashCode = 0xFFFF & tmp.hashCode();
|
||||
if ( hashRead == hashCode ) {
|
||||
result = tmp;
|
||||
} else {
|
||||
Log.w( TAG, "fromPublicFmt: hash code mismatch" );
|
||||
}
|
||||
} catch( Exception e ) {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void enqueueWork( Context context, Intent intent )
|
||||
{
|
||||
enqueueWork( context, SMSService.class, intent );
|
||||
Log.d( TAG, "called enqueueWork(%s)", intent );
|
||||
}
|
||||
|
||||
private static Intent getIntentTo( Context context, SMSAction cmd )
|
||||
{
|
||||
return getIntentTo( context, SMSService.class, cmd );
|
||||
}
|
||||
|
||||
private static boolean showToasts( Context context )
|
||||
{
|
||||
if ( null == s_showToasts ) {
|
||||
s_showToasts =
|
||||
XWPrefs.getPrefsBoolean( context, R.string.key_show_sms, false );
|
||||
}
|
||||
return s_showToasts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
super.onCreate();
|
||||
|
||||
mHelper = new SMSServiceHelper( this );
|
||||
if ( Utils.deviceSupportsNBS( this ) ) {
|
||||
registerReceivers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
XWJICmds[] getCmds() { return SMSAction.values(); }
|
||||
|
||||
@Override
|
||||
void onHandleWorkImpl( Intent intent, XWJICmds jicmd, long timestamp )
|
||||
{
|
||||
Log.d( TAG, "onHandleWorkImpl()" );
|
||||
SMSAction cmd = (SMSAction)jicmd;
|
||||
switch( cmd ) {
|
||||
case STOP_SELF:
|
||||
stopSelf();
|
||||
break;
|
||||
case HANDLEDATA:
|
||||
++m_nReceived;
|
||||
ConnStatusHandler.
|
||||
updateStatusIn( this, null,
|
||||
CommsConnType.COMMS_CONN_SMS, true );
|
||||
if ( showToasts( this ) && (0 == (m_nReceived % 5)) ) {
|
||||
DbgUtils.showf( this, "Got msg %d", m_nReceived );
|
||||
}
|
||||
String phone = intent.getStringExtra( PHONE );
|
||||
byte[] buffer = intent.getByteArrayExtra( BUFFER );
|
||||
receiveBuffer( buffer, phone );
|
||||
break;
|
||||
case INVITE:
|
||||
phone = intent.getStringExtra( PHONE );
|
||||
buffer = intent.getByteArrayExtra( GAMEDATA_BA );
|
||||
inviteRemote( phone, buffer );
|
||||
break;
|
||||
case ADDED_MISSING:
|
||||
NetLaunchInfo nli
|
||||
= MultiService.getMissingDictData( this, intent );
|
||||
phone = intent.getStringExtra( PHONE );
|
||||
makeForInvite( phone, nli );
|
||||
break;
|
||||
case SEND:
|
||||
phone = intent.getStringExtra( PHONE );
|
||||
byte[] bytes = intent.getByteArrayExtra( BINBUFFER );
|
||||
int gameID = intent.getIntExtra( MultiService.GAMEID, -1 );
|
||||
sendPacket( phone, gameID, bytes );
|
||||
break;
|
||||
case REMOVE:
|
||||
gameID = intent.getIntExtra( MultiService.GAMEID, -1 );
|
||||
phone = intent.getStringExtra( PHONE );
|
||||
sendDiedPacket( phone, gameID );
|
||||
break;
|
||||
case RESEND:
|
||||
phone = intent.getStringExtra( PHONE );
|
||||
resendFor( phone );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
if ( null != m_sentReceiver ) {
|
||||
unregisterReceiver( m_sentReceiver );
|
||||
m_sentReceiver = null;
|
||||
}
|
||||
if ( null != m_receiveReceiver ) {
|
||||
unregisterReceiver( m_receiveReceiver );
|
||||
m_receiveReceiver = null;
|
||||
}
|
||||
if ( null != m_prefsListener ) {
|
||||
SharedPreferences sp
|
||||
= PreferenceManager.getDefaultSharedPreferences( this );
|
||||
sp.unregisterOnSharedPreferenceChangeListener( m_prefsListener );
|
||||
m_prefsListener = null;
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void inviteRemote( String phone, byte[] asBytes )
|
||||
{
|
||||
resendFor( phone, SMS_CMD.INVITE, 0, asBytes, true );
|
||||
}
|
||||
|
||||
private void ackInvite( String phone, int gameID )
|
||||
{
|
||||
resendFor( phone, SMS_CMD.ACK_INVITE, gameID, null );
|
||||
}
|
||||
|
||||
private void sendDiedPacket( String phone, int gameID )
|
||||
{
|
||||
if ( !s_sentDied.contains( gameID ) ) {
|
||||
resendFor( phone, SMS_CMD.DEATH, gameID, null );
|
||||
s_sentDied.add( gameID );
|
||||
}
|
||||
}
|
||||
|
||||
public int sendPacket( String phone, int gameID, byte[] bytes )
|
||||
{
|
||||
resendFor( phone, SMS_CMD.DATA, gameID, bytes );
|
||||
return bytes.length;
|
||||
}
|
||||
|
||||
private void sendOrRetry( byte[][] msgs, String toPhone, int waitSecs )
|
||||
{
|
||||
if ( null != msgs ) {
|
||||
sendBuffers( msgs, toPhone );
|
||||
}
|
||||
if ( waitSecs > 0 ) {
|
||||
// PENDING
|
||||
Log.d( TAG, "calling postResend(), but might want to avoid enqueueWork!" );
|
||||
postResend( toPhone, waitSecs );
|
||||
}
|
||||
}
|
||||
|
||||
private void resendFor( String toPhone, SMS_CMD cmd, int gameID, byte[] data )
|
||||
{
|
||||
boolean newSMSEnabled = XWPrefs.getSMSProtoEnabled( this );
|
||||
boolean forceNow = !newSMSEnabled; // || cmd == SMS_CMD.INVITE;
|
||||
resendFor( toPhone, cmd, gameID, data, forceNow );
|
||||
}
|
||||
|
||||
private void resendFor( String toPhone )
|
||||
{
|
||||
resendFor( toPhone, SMS_CMD.NONE, 0, null, false );
|
||||
}
|
||||
|
||||
private void resendFor( String toPhone, SMS_CMD cmd, int gameID, byte[] data,
|
||||
boolean forceNow )
|
||||
{
|
||||
int[] waitSecs = { 0 };
|
||||
byte[][] msgs = XwJNI.smsproto_prepOutbound( cmd, gameID, data, toPhone,
|
||||
getNBSPort(), forceNow,
|
||||
waitSecs );
|
||||
sendOrRetry( msgs, toPhone, waitSecs[0] );
|
||||
}
|
||||
|
||||
private void postResend( final String phone, final int waitSecs )
|
||||
{
|
||||
Log.d( TAG, "postResend" );
|
||||
new Thread( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep( waitSecs * 1000 );
|
||||
|
||||
Log.d( TAG, "postResend.run()" );
|
||||
Intent intent = getIntentTo( SMSService.this,
|
||||
SMSAction.RESEND );
|
||||
intent.putExtra( PHONE, phone );
|
||||
enqueueWork( SMSService.this, intent );
|
||||
} catch ( InterruptedException ie ) {
|
||||
Log.e( TAG, ie.getMessage() );
|
||||
}
|
||||
}
|
||||
} ).start();
|
||||
}
|
||||
|
||||
private void receive( SMSProtoMsg msg, String phone )
|
||||
{
|
||||
Log.i( TAG, "receive(cmd=%s)", msg.cmd );
|
||||
switch( msg.cmd ) {
|
||||
case INVITE:
|
||||
makeForInvite( phone, NetLaunchInfo.makeFrom( this, msg.data ) );
|
||||
break;
|
||||
case DATA:
|
||||
if ( feedMessage( msg.gameID, msg.data, new CommsAddrRec( phone ) ) ) {
|
||||
SMSResendReceiver.resetTimer( this );
|
||||
}
|
||||
break;
|
||||
case DEATH:
|
||||
mHelper.postEvent( MultiEvent.MESSAGE_NOGAME, msg.gameID );
|
||||
break;
|
||||
case ACK_INVITE:
|
||||
mHelper.postEvent( MultiEvent.NEWGAME_SUCCESS, msg.gameID );
|
||||
break;
|
||||
default:
|
||||
Log.w( TAG, "unexpected cmd %s", msg.cmd );
|
||||
Assert.assertFalse( BuildConfig.DEBUG );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void receiveBuffer( byte[] buffer, String senderPhone )
|
||||
{
|
||||
SMSProtoMsg[] msgs = XwJNI.smsproto_prepInbound( buffer, senderPhone,
|
||||
getNBSPort() );
|
||||
if ( null != msgs ) {
|
||||
for ( SMSProtoMsg msg : msgs ) {
|
||||
receive( msg, senderPhone );
|
||||
}
|
||||
mHelper.postEvent( MultiEvent.SMS_RECEIVE_OK );
|
||||
} else {
|
||||
Log.d( TAG, "receiveBuffer(): bogus or incomplete message from %s",
|
||||
senderPhone );
|
||||
}
|
||||
}
|
||||
|
||||
private void makeForInvite( String phone, NetLaunchInfo nli )
|
||||
{
|
||||
if ( nli != null ) {
|
||||
mHelper.handleInvitation( this, nli, phone, DictFetchOwner.OWNER_SMS );
|
||||
ackInvite( phone, nli.gameID() );
|
||||
}
|
||||
}
|
||||
|
||||
private PendingIntent makeStatusIntent( String msg )
|
||||
{
|
||||
Intent intent = new Intent( msg );
|
||||
return PendingIntent.getBroadcast( this, 0, intent, 0 );
|
||||
}
|
||||
|
||||
private boolean sendBuffers( byte[][] fragments, String phone )
|
||||
{
|
||||
boolean success = false;
|
||||
if ( XWPrefs.getNBSEnabled( this ) ) {
|
||||
|
||||
// Try send-to-self
|
||||
if ( XWPrefs.getSMSToSelfEnabled( this ) ) {
|
||||
String myPhone = getPhoneInfo( this ).number;
|
||||
if ( null != myPhone
|
||||
&& PhoneNumberUtils.compare( phone, myPhone ) ) {
|
||||
for ( byte[] fragment : fragments ) {
|
||||
handleFrom( this, fragment, phone );
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !success ) {
|
||||
short nbsPort = getNBSPort();
|
||||
try {
|
||||
SmsManager mgr = SmsManager.getDefault();
|
||||
boolean useProxy = Perms23.Perm.SEND_SMS.isBanned( this )
|
||||
&& NBSProxy.isInstalled( this );
|
||||
PendingIntent sent = useProxy ? null : makeStatusIntent( MSG_SENT );
|
||||
PendingIntent delivery = useProxy ? null : makeStatusIntent( MSG_DELIVERED );
|
||||
for ( byte[] fragment : fragments ) {
|
||||
if ( useProxy ) {
|
||||
NBSProxy.send( this, phone, nbsPort, fragment );
|
||||
} else {
|
||||
mgr.sendDataMessage( phone, null, nbsPort, fragment,
|
||||
sent, delivery );
|
||||
}
|
||||
// Log.i( TAG, "sendBuffers(): sent %d byte fragment to %s",
|
||||
// fragment.length, phone );
|
||||
}
|
||||
success = true;
|
||||
} catch ( IllegalArgumentException iae ) {
|
||||
Log.w( TAG, "sendBuffers(%s): %s", phone, iae.toString() );
|
||||
} catch ( NullPointerException npe ) {
|
||||
Assert.assertFalse( BuildConfig.DEBUG ); // shouldn't be trying to do this!!!
|
||||
} catch ( java.lang.SecurityException se ) {
|
||||
mHelper.postEvent( MultiEvent.SMS_SEND_FAILED_NOPERMISSION );
|
||||
} catch ( Exception ee ) {
|
||||
Log.ex( TAG, ee );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.i( TAG, "dropping because SMS disabled" );
|
||||
}
|
||||
|
||||
if ( showToasts( this ) && success && (0 == (++s_nSent % 5)) ) {
|
||||
DbgUtils.showf( this, "Sent msg %d", s_nSent );
|
||||
}
|
||||
|
||||
ConnStatusHandler.updateStatusOut( this, null,
|
||||
CommsConnType.COMMS_CONN_SMS,
|
||||
success );
|
||||
return success;
|
||||
}
|
||||
|
||||
private boolean feedMessage( int gameID, byte[] msg, CommsAddrRec addr )
|
||||
{
|
||||
XWServiceHelper.ReceiveResult rslt = mHelper
|
||||
.receiveMessage( this, gameID, null, msg, addr );
|
||||
if ( XWServiceHelper.ReceiveResult.GAME_GONE == rslt ) {
|
||||
sendDiedPacket( addr.sms_phone, gameID );
|
||||
}
|
||||
return rslt == XWServiceHelper.ReceiveResult.OK;
|
||||
}
|
||||
|
||||
private void registerReceivers()
|
||||
{
|
||||
m_sentReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context arg0, Intent arg1)
|
||||
{
|
||||
switch ( getResultCode() ) {
|
||||
case Activity.RESULT_OK:
|
||||
mHelper.postEvent( MultiEvent.SMS_SEND_OK );
|
||||
break;
|
||||
case SmsManager.RESULT_ERROR_RADIO_OFF:
|
||||
mHelper.postEvent( MultiEvent.SMS_SEND_FAILED_NORADIO );
|
||||
break;
|
||||
case SmsManager.RESULT_ERROR_NO_SERVICE:
|
||||
default:
|
||||
Log.w( TAG, "FAILURE!!!" );
|
||||
mHelper.postEvent( MultiEvent.SMS_SEND_FAILED );
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
registerReceiver( m_sentReceiver, new IntentFilter(MSG_SENT) );
|
||||
|
||||
m_receiveReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive( Context context, Intent intent )
|
||||
{
|
||||
if ( Activity.RESULT_OK != getResultCode() ) {
|
||||
Log.w( TAG, "SMS delivery result: FAILURE" );
|
||||
}
|
||||
}
|
||||
};
|
||||
registerReceiver( m_receiveReceiver, new IntentFilter(MSG_DELIVERED) );
|
||||
}
|
||||
|
||||
private static String matchKeyIf( Map<String, ?> map, final String phone )
|
||||
{
|
||||
String result = phone;
|
||||
Set<String> keys = map.keySet();
|
||||
if ( ! keys.contains( result ) ) {
|
||||
for ( Iterator<String> iter = keys.iterator(); iter.hasNext(); ) {
|
||||
String key = iter.next();
|
||||
if ( PhoneNumberUtils.compare( key, phone ) ) {
|
||||
result = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.i( TAG, "matchKeyIf(%s) => %s", phone, result );
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Short s_nbsPort = null;
|
||||
private short getNBSPort()
|
||||
{
|
||||
if ( null == s_nbsPort ) {
|
||||
String asStr = getString( R.string.nbs_port );
|
||||
s_nbsPort = new Short((short)Integer.parseInt( asStr ) );
|
||||
}
|
||||
return s_nbsPort;
|
||||
}
|
||||
|
||||
private class SMSMsgSink extends MultiMsgSink {
|
||||
public SMSMsgSink( Context context ) {
|
||||
super( context );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendViaSMS( byte[] buf, int gameID, CommsAddrRec addr )
|
||||
{
|
||||
return sendPacket( addr.sms_phone, gameID, buf );
|
||||
}
|
||||
}
|
||||
|
||||
private class SMSServiceHelper extends XWServiceHelper {
|
||||
private Service mService;
|
||||
|
||||
SMSServiceHelper( Service service ) {
|
||||
super( service );
|
||||
mService = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiMsgSink getSink( long rowid )
|
||||
{
|
||||
return new SMSMsgSink( SMSService.this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postNotification( String phone, int gameID, long rowid )
|
||||
{
|
||||
String owner = Utils.phoneToContact( mService, phone, true );
|
||||
String body = LocUtils.getString( mService, R.string.new_name_body_fmt,
|
||||
owner );
|
||||
GameUtils.postInvitedNotification( mService, gameID, body, rowid );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -126,7 +126,7 @@ public class Utils {
|
|||
{
|
||||
boolean result = false;
|
||||
if ( Perms23.havePermissions( context, Perm.READ_PHONE_STATE ) ) {
|
||||
SMSService.SMSPhoneInfo info = SMSService.getPhoneInfo( context );
|
||||
NBSProto.SMSPhoneInfo info = NBSProto.getPhoneInfo( context );
|
||||
result = null != info && info.isPhone && info.isGSM;
|
||||
}
|
||||
Log.d( TAG, "isGSMPhone() => %b", result );
|
||||
|
|
|
@ -754,7 +754,7 @@ public class WiDirService extends XWService {
|
|||
.setP2PParams( macAddress );
|
||||
|
||||
XWServiceHelper.ReceiveResult rslt = mHelper
|
||||
.receiveMessage( this, gameID, m_sink, data, addr );
|
||||
.receiveMessage( gameID, m_sink, data, addr );
|
||||
if ( XWServiceHelper.ReceiveResult.GAME_GONE == rslt ) {
|
||||
sendNoGame( null, macAddress, gameID );
|
||||
}
|
||||
|
@ -767,7 +767,7 @@ public class WiDirService extends XWService {
|
|||
NetLaunchInfo nli = NetLaunchInfo.makeFrom( this, nliData );
|
||||
String returnMac = intent.getStringExtra( KEY_SRC );
|
||||
|
||||
if ( !mHelper.handleInvitation( this, nli, returnMac, DictFetchOwner.OWNER_P2P ) ) {
|
||||
if ( !mHelper.handleInvitation( nli, returnMac, DictFetchOwner.OWNER_P2P ) ) {
|
||||
Log.d( TAG, "handleInvitation() failed" );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ public class XWApp extends Application
|
|||
public void onDataReceived( short port, String fromPhone, byte[] data )
|
||||
{
|
||||
Assert.assertTrue( port == mPort || !BuildConfig.DEBUG );
|
||||
SMSService.handleFrom( this, data, fromPhone );
|
||||
NBSProto.handleFrom( this, data, fromPhone, port );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -38,31 +38,31 @@ import java.util.Map;
|
|||
|
||||
abstract class XWServiceHelper {
|
||||
private static final String TAG = XWServiceHelper.class.getSimpleName();
|
||||
private Service mService;
|
||||
private Context mContext;
|
||||
private static MultiService s_srcMgr = new MultiService();
|
||||
|
||||
public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED };
|
||||
|
||||
XWServiceHelper( Service service )
|
||||
XWServiceHelper( Context context )
|
||||
{
|
||||
mService = service;
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
abstract MultiMsgSink getSink( long rowid );
|
||||
abstract void postNotification( String device, int gameID, long rowid );
|
||||
|
||||
protected ReceiveResult receiveMessage( Context context, int gameID,
|
||||
protected ReceiveResult receiveMessage( int gameID,
|
||||
MultiMsgSink sink, byte[] msg,
|
||||
CommsAddrRec addr )
|
||||
{
|
||||
ReceiveResult result;
|
||||
long[] rowids = DBUtils.getRowIDsFor( context, gameID );
|
||||
long[] rowids = DBUtils.getRowIDsFor( mContext, gameID );
|
||||
if ( null == rowids || 0 == rowids.length ) {
|
||||
result = ReceiveResult.GAME_GONE;
|
||||
} else {
|
||||
result = ReceiveResult.UNCONSUMED;
|
||||
for ( long rowid : rowids ) {
|
||||
if ( receiveMessage( context, rowid, sink, msg, addr ) ) {
|
||||
if ( receiveMessage( rowid, sink, msg, addr ) ) {
|
||||
result = ReceiveResult.OK;
|
||||
}
|
||||
}
|
||||
|
@ -70,9 +70,8 @@ abstract class XWServiceHelper {
|
|||
return result;
|
||||
}
|
||||
|
||||
protected boolean receiveMessage( Context context, long rowid,
|
||||
MultiMsgSink sink, byte[] msg,
|
||||
CommsAddrRec addr )
|
||||
protected boolean receiveMessage( long rowid, MultiMsgSink sink,
|
||||
byte[] msg, CommsAddrRec addr )
|
||||
{
|
||||
boolean allConsumed = true;
|
||||
boolean[] isLocalP = new boolean[1];
|
||||
|
@ -87,9 +86,9 @@ abstract class XWServiceHelper {
|
|||
if ( null == sink ) {
|
||||
sink = getSink( rowid );
|
||||
}
|
||||
if ( GameUtils.feedMessage( context, rowid, msg, addr,
|
||||
if ( GameUtils.feedMessage( mContext, rowid, msg, addr,
|
||||
sink, bmr, isLocalP ) ) {
|
||||
GameUtils.postMoveNotification( context, rowid, bmr,
|
||||
GameUtils.postMoveNotification( mContext, rowid, bmr,
|
||||
isLocalP[0] );
|
||||
consumed = true;
|
||||
}
|
||||
|
@ -119,17 +118,17 @@ abstract class XWServiceHelper {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean handleInvitation( Context context, NetLaunchInfo nli,
|
||||
protected boolean handleInvitation( NetLaunchInfo nli,
|
||||
String device, DictFetchOwner dfo )
|
||||
{
|
||||
boolean success = nli.isValid() && checkNotInFlight( nli );
|
||||
CurGameInfo gi = null;
|
||||
if ( success ) {
|
||||
long[] rowids = DBUtils.getRowIDsFor( mService, nli.gameID() );
|
||||
long[] rowids = DBUtils.getRowIDsFor( mContext, nli.gameID() );
|
||||
if ( 0 == rowids.length ) {
|
||||
// cool: we're good
|
||||
} else if ( rowids.length < nli.nPlayersT ) {
|
||||
success = XWPrefs.getSecondInviteAllowed( mService );
|
||||
success = XWPrefs.getSecondInviteAllowed( mContext );
|
||||
|
||||
// Allowing a second game allows the common testing action of
|
||||
// sending invitation to myself. But we still need to check
|
||||
|
@ -139,12 +138,12 @@ abstract class XWServiceHelper {
|
|||
try ( GameLock lock = GameLock.tryLockRO( rowid ) ) {
|
||||
// drop invite if can't open game; likely a dupe!
|
||||
if ( null != lock ) {
|
||||
gi = new CurGameInfo( mService );
|
||||
gi = new CurGameInfo( mContext );
|
||||
GamePtr gamePtr = GameUtils
|
||||
.loadMakeGame( mService, gi, lock );
|
||||
.loadMakeGame( mContext, gi, lock );
|
||||
gamePtr.release();
|
||||
} else {
|
||||
DbgUtils.toastNoLock( TAG, context, rowid,
|
||||
DbgUtils.toastNoLock( TAG, mContext, rowid,
|
||||
"handleInvitation()" );
|
||||
}
|
||||
}
|
||||
|
@ -164,20 +163,20 @@ abstract class XWServiceHelper {
|
|||
}
|
||||
|
||||
if ( success ) {
|
||||
if ( DictLangCache.haveDict( mService, nli.lang, nli.dict ) ) {
|
||||
long rowid = GameUtils.makeNewMultiGame( mService, nli,
|
||||
if ( DictLangCache.haveDict( mContext, nli.lang, nli.dict ) ) {
|
||||
long rowid = GameUtils.makeNewMultiGame( mContext, nli,
|
||||
getSink( 0 ),
|
||||
getUtilCtxt() );
|
||||
|
||||
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
|
||||
DBUtils.setName( mService, rowid, nli.gameName );
|
||||
DBUtils.setName( mContext, rowid, nli.gameName );
|
||||
}
|
||||
|
||||
postNotification( device, nli.gameID(), rowid );
|
||||
} else {
|
||||
Intent intent = MultiService
|
||||
.makeMissingDictIntent( mService, nli, dfo );
|
||||
MultiService.postMissingDictNotification( mService, intent,
|
||||
.makeMissingDictIntent( mContext, nli, dfo );
|
||||
MultiService.postMissingDictNotification( mContext, intent,
|
||||
nli.gameID() );
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +189,7 @@ abstract class XWServiceHelper {
|
|||
protected UtilCtxt getUtilCtxt()
|
||||
{
|
||||
if ( null == m_utilCtxt ) {
|
||||
m_utilCtxt = new UtilCtxtImpl( mService );
|
||||
m_utilCtxt = new UtilCtxtImpl( mContext );
|
||||
}
|
||||
return m_utilCtxt;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.eehouse.android.xw4.BuildConfig;
|
|||
import org.eehouse.android.xw4.GameUtils;
|
||||
import org.eehouse.android.xw4.Log;
|
||||
import org.eehouse.android.xw4.R;
|
||||
import org.eehouse.android.xw4.SMSService;
|
||||
import org.eehouse.android.xw4.NBSProto;
|
||||
import org.eehouse.android.xw4.Utils;
|
||||
import org.eehouse.android.xw4.WiDirService;
|
||||
import org.eehouse.android.xw4.WiDirWrapper;
|
||||
|
@ -373,7 +373,7 @@ public class CommsAddrRec {
|
|||
}
|
||||
break;
|
||||
case COMMS_CONN_SMS:
|
||||
SMSService.SMSPhoneInfo pi = SMSService.getPhoneInfo( context );
|
||||
NBSProto.SMSPhoneInfo pi = NBSProto.getPhoneInfo( context );
|
||||
// Do we have phone permission? If not, shouldn't be set at all!
|
||||
if ( null != pi ) {
|
||||
sms_phone = pi.number;
|
||||
|
|
Loading…
Reference in a new issue