diff --git a/xwords4/android/app/src/main/AndroidManifest.xml b/xwords4/android/app/src/main/AndroidManifest.xml index a86398fde..3a03fbc78 100644 --- a/xwords4/android/app/src/main/AndroidManifest.xml +++ b/xwords4/android/app/src/main/AndroidManifest.xml @@ -186,10 +186,6 @@ android:permission="android.permission.BIND_JOB_SERVICE" android:exported="false" /> - s_sentDied = new HashSet(); + + 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 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 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 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 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; + } +} diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetLaunchInfo.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetLaunchInfo.java index 53c8a62f0..b2edc4f6c 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetLaunchInfo.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetLaunchInfo.java @@ -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; diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsDelegate.java index e9c60b1b8..b3239ca8b 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsDelegate.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsDelegate.java @@ -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: diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java index 8fdffe282..7de48cf56 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java @@ -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 ); } } } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSReceiver.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSReceiver.java index b3f979866..a22c3b700 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSReceiver.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSReceiver.java @@ -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 ); } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java deleted file mode 100644 index dc942c39a..000000000 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java +++ /dev/null @@ -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 s_sentDied = new HashSet(); - - 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 map, final String phone ) - { - String result = phone; - Set keys = map.keySet(); - if ( ! keys.contains( result ) ) { - for ( Iterator 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 ); - } - } -} diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java index c677b06a4..a89166cfc 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java @@ -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 ); diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/WiDirService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/WiDirService.java index d9382b530..315f095c5 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/WiDirService.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/WiDirService.java @@ -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" ); } } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java index 28673f063..c9fc6edb9 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java @@ -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 diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWServiceHelper.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWServiceHelper.java index 06ece2e07..e86597e1f 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWServiceHelper.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWServiceHelper.java @@ -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; } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommsAddrRec.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommsAddrRec.java index d357bc86b..448253c40 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommsAddrRec.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommsAddrRec.java @@ -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;