From 7f46e99f9cbe282df1c6a3dde7e1b6755ff008b3 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Wed, 2 Aug 2017 12:51:46 -0700 Subject: [PATCH] Use SIM country code when registered number is unavailable Convert directory operations to Addresses Fixes #6845 // FREEBIE --- .../securesms/ConversationActivity.java | 3 +- .../securesms/RegistrationActivity.java | 27 ++--- .../securesms/contacts/ContactsDatabase.java | 107 ++++++++---------- .../securesms/database/Address.java | 33 ++++-- .../database/TextSecureDirectory.java | 28 ++--- .../securesms/jobs/DirectoryRefreshJob.java | 2 +- .../service/RegistrationService.java | 3 +- .../securesms/util/DirectoryHelper.java | 66 +++++------ .../securesms/util/TextSecurePreferences.java | 2 +- src/org/thoughtcrime/securesms/util/Util.java | 27 +++-- .../securesms/database/AddressTest.java | 9 ++ 11 files changed, 155 insertions(+), 152 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 68ea836ee2..543b8d0096 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -1023,8 +1023,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity capabilities.getVideoCapability() == Capability.UNKNOWN) { try { - capabilities = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients, - TextSecurePreferences.getLocalNumber(context)); + capabilities = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients); } catch (IOException e) { Log.w(TAG, e); } diff --git a/src/org/thoughtcrime/securesms/RegistrationActivity.java b/src/org/thoughtcrime/securesms/RegistrationActivity.java index eb8b349f7e..974ae1b11c 100644 --- a/src/org/thoughtcrime/securesms/RegistrationActivity.java +++ b/src/org/thoughtcrime/securesms/RegistrationActivity.java @@ -24,7 +24,6 @@ import android.widget.Toast; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import com.google.i18n.phonenumbers.AsYouTypeFormatter; -import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber; @@ -32,6 +31,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; +import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; /** @@ -147,26 +147,17 @@ public class RegistrationActivity extends BaseActionBarActivity { } private void initializeNumber() { - PhoneNumberUtil numberUtil = PhoneNumberUtil.getInstance(); - String localNumber = Util.getDeviceE164Number(this); + Optional localNumber = Util.getDeviceNumber(this); - try { - if (!TextUtils.isEmpty(localNumber)) { - Phonenumber.PhoneNumber localNumberObject = numberUtil.parse(localNumber, null); + if (localNumber.isPresent()) { + this.countryCode.setText(String.valueOf(localNumber.get().getCountryCode())); + this.number.setText(String.valueOf(localNumber.get().getNationalNumber())); + } else { + Optional simCountryIso = Util.getSimCountryIso(this); - if (localNumberObject != null) { - this.countryCode.setText(String.valueOf(localNumberObject.getCountryCode())); - this.number.setText(String.valueOf(localNumberObject.getNationalNumber())); - } - } else { - String simCountryIso = Util.getSimCountryIso(this); - - if (!TextUtils.isEmpty(simCountryIso)) { - this.countryCode.setText(numberUtil.getCountryCodeForRegion(simCountryIso)+""); - } + if (simCountryIso.isPresent() && !TextUtils.isEmpty(simCountryIso.get())) { + this.countryCode.setText(PhoneNumberUtil.getInstance().getCountryCodeForRegion(simCountryIso.get())+""); } - } catch (NumberParseException npe) { - Log.w(TAG, npe); } } diff --git a/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java b/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java index 8b4b37b18d..e81059e7ba 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java @@ -35,6 +35,7 @@ import android.util.Log; import android.util.Pair; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.push.ContactTokenDetails; @@ -76,29 +77,28 @@ public class ContactsDatabase { this.context = context; } - public synchronized @NonNull List setRegisteredUsers(@NonNull Account account, - @NonNull String localNumber, - @NonNull List registeredContacts, - boolean remove) + public synchronized @NonNull List
setRegisteredUsers(@NonNull Account account, + @NonNull List registeredContacts, + boolean remove) throws RemoteException, OperationApplicationException { - Map registeredNumbers = new HashMap<>(); - List addedNumbers = new LinkedList<>(); - ArrayList operations = new ArrayList<>(); - Map currentContacts = getSignalRawContacts(account, localNumber); + Map registeredAddresses = new HashMap<>(); + List
addedAddresses = new LinkedList<>(); + ArrayList operations = new ArrayList<>(); + Map currentContacts = getSignalRawContacts(account); for (ContactTokenDetails registeredContact : registeredContacts) { - String registeredNumber = registeredContact.getNumber(); + Address registeredAddress = Address.fromSerialized(registeredContact.getNumber()); - registeredNumbers.put(registeredNumber, registeredContact); + registeredAddresses.put(registeredAddress, registeredContact); - if (!currentContacts.containsKey(registeredNumber)) { - Optional systemContactInfo = getSystemContactInfo(registeredNumber, localNumber); + if (!currentContacts.containsKey(registeredAddress)) { + Optional systemContactInfo = getSystemContactInfo(registeredAddress); if (systemContactInfo.isPresent()) { - Log.w(TAG, "Adding number: " + registeredNumber); - addedNumbers.add(registeredNumber); + Log.w(TAG, "Adding number: " + registeredAddress); + addedAddresses.add(registeredAddress); addTextSecureRawContact(operations, account, systemContactInfo.get().number, systemContactInfo.get().name, systemContactInfo.get().id, true); @@ -106,8 +106,8 @@ public class ContactsDatabase { } } - for (Map.Entry currentContactEntry : currentContacts.entrySet()) { - ContactTokenDetails tokenDetails = registeredNumbers.get(currentContactEntry.getKey()); + for (Map.Entry currentContactEntry : currentContacts.entrySet()) { + ContactTokenDetails tokenDetails = registeredAddresses.get(currentContactEntry.getKey()); if (tokenDetails == null) { if (remove) { @@ -129,7 +129,7 @@ public class ContactsDatabase { context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations); } - return addedNumbers; + return addedAddresses; } @NonNull Cursor querySystemContacts(String filter) { @@ -220,7 +220,7 @@ public class ContactsDatabase { } private void addContactVoiceSupport(List operations, - @NonNull String e164number, long rawContactId) + @NonNull Address address, long rawContactId) { operations.add(ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) .withSelection(RawContacts._ID + " = ?", new String[] {String.valueOf(rawContactId)}) @@ -230,9 +230,9 @@ public class ContactsDatabase { operations.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build()) .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) .withValue(ContactsContract.Data.MIMETYPE, CALL_MIMETYPE) - .withValue(ContactsContract.Data.DATA1, e164number) + .withValue(ContactsContract.Data.DATA1, address.toPhoneString()) .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) - .withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, e164number)) + .withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, address.toPhoneString())) .withYieldAllowed(true) .build()); } @@ -346,15 +346,13 @@ public class ContactsDatabase { .build()); } - private @NonNull Map getSignalRawContacts(@NonNull Account account, - @NonNull String localNumber) - { + private @NonNull Map getSignalRawContacts(@NonNull Account account) { Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon() .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type).build(); - Map signalContacts = new HashMap<>(); - Cursor cursor = null; + Map signalContacts = new HashMap<>(); + Cursor cursor = null; try { String[] projection; @@ -368,21 +366,13 @@ public class ContactsDatabase { cursor = context.getContentResolver().query(currentContactsUri, projection, null, null, null); while (cursor != null && cursor.moveToNext()) { - String currentNumber; - - try { - currentNumber = PhoneNumberFormatter.formatNumber(cursor.getString(1), localNumber); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - currentNumber = cursor.getString(1); - } - - long rawContactId = cursor.getLong(0); - long contactId = cursor.getLong(3); - String supportsVoice = cursor.getString(2); - String rawContactDisplayName = null; - String aggregateDisplayName = null; - int rawContactDisplayNameSource = 0; + Address currentAddress = Address.fromExternal(context, cursor.getString(1)); + long rawContactId = cursor.getLong(0); + long contactId = cursor.getLong(3); + String supportsVoice = cursor.getString(2); + String rawContactDisplayName = null; + String aggregateDisplayName = null; + int rawContactDisplayNameSource = 0; if (Build.VERSION.SDK_INT >= 11) { rawContactDisplayName = cursor.getString(4); @@ -390,7 +380,7 @@ public class ContactsDatabase { aggregateDisplayName = getDisplayName(contactId); } - signalContacts.put(currentNumber, new SignalContact(rawContactId, supportsVoice, rawContactDisplayName, aggregateDisplayName, rawContactDisplayNameSource)); + signalContacts.put(currentAddress, new SignalContact(rawContactId, supportsVoice, rawContactDisplayName, aggregateDisplayName, rawContactDisplayNameSource)); } } finally { if (cursor != null) @@ -400,10 +390,11 @@ public class ContactsDatabase { return signalContacts; } - private Optional getSystemContactInfo(@NonNull String e164number, - @NonNull String localNumber) + private Optional getSystemContactInfo(@NonNull Address address) { - Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(e164number)); + if (!address.isPhone()) return Optional.absent(); + + Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString())); String[] projection = {ContactsContract.PhoneLookup.NUMBER, ContactsContract.PhoneLookup._ID, ContactsContract.PhoneLookup.DISPLAY_NAME}; @@ -414,25 +405,21 @@ public class ContactsDatabase { numberCursor = context.getContentResolver().query(uri, projection, null, null, null); while (numberCursor != null && numberCursor.moveToNext()) { - try { - String systemNumber = numberCursor.getString(0); - String canonicalizedSystemNumber = PhoneNumberFormatter.formatNumber(systemNumber, localNumber); + String systemNumber = numberCursor.getString(0); + Address systemAddress = Address.fromExternal(context, systemNumber); - if (canonicalizedSystemNumber.equals(e164number)) { - idCursor = context.getContentResolver().query(RawContacts.CONTENT_URI, - new String[] {RawContacts._ID}, - RawContacts.CONTACT_ID + " = ? ", - new String[] {String.valueOf(numberCursor.getLong(1))}, - null); + if (systemAddress.equals(address)) { + idCursor = context.getContentResolver().query(RawContacts.CONTENT_URI, + new String[] {RawContacts._ID}, + RawContacts.CONTACT_ID + " = ? ", + new String[] {String.valueOf(numberCursor.getLong(1))}, + null); - if (idCursor != null && idCursor.moveToNext()) { - return Optional.of(new SystemContactInfo(numberCursor.getString(2), - numberCursor.getString(0), - idCursor.getLong(0))); - } + if (idCursor != null && idCursor.moveToNext()) { + return Optional.of(new SystemContactInfo(numberCursor.getString(2), + numberCursor.getString(0), + idCursor.getLong(0))); } - } catch (InvalidNumberException e) { - Log.w(TAG, e); } } } finally { diff --git a/src/org/thoughtcrime/securesms/database/Address.java b/src/org/thoughtcrime/securesms/database/Address.java index 8902daea93..15fe20a4b2 100644 --- a/src/org/thoughtcrime/securesms/database/Address.java +++ b/src/org/thoughtcrime/securesms/database/Address.java @@ -7,6 +7,7 @@ import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; +import android.text.TextUtils; import android.util.Log; import com.google.i18n.phonenumbers.NumberParseException; @@ -20,7 +21,6 @@ import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; @@ -60,7 +60,17 @@ public class Address implements Parcelable, Comparable
{ } public static Address fromExternal(@NonNull Context context, @Nullable String external) { - return new Address(new ExternalAddressFormatter(TextSecurePreferences.getLocalNumber(context)).format(external)); + String localNumber = TextSecurePreferences.getLocalNumber(context); + + ExternalAddressFormatter formatter; + + if (!TextUtils.isEmpty(localNumber)) { + formatter = new ExternalAddressFormatter(localNumber); + } else { + formatter = new ExternalAddressFormatter(Util.getSimCountryIso(context).or("US"), true); + } + + return new Address(formatter.format(external)); } public static @NonNull List
fromSerializedList(@NonNull String serialized, char delimiter) { @@ -171,23 +181,28 @@ public class Address implements Parcelable, Comparable
{ add("AC"); }}; - private final Phonenumber.PhoneNumber localNumber; - private final String localNumberString; - private final String localCountryCode; + private final String localNumberString; + private final String localCountryCode; private final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]"); - public ExternalAddressFormatter(String localNumber) { + ExternalAddressFormatter(@NonNull String localNumberString) { try { - this.localNumberString = localNumber; - this.localNumber = phoneNumberUtil.parse(localNumber, null); - this.localCountryCode = phoneNumberUtil.getRegionCodeForNumber(this.localNumber); + Phonenumber.PhoneNumber localNumber = phoneNumberUtil.parse(localNumberString, null); + + this.localNumberString = localNumberString; + this.localCountryCode = phoneNumberUtil.getRegionCodeForNumber(localNumber); } catch (NumberParseException e) { throw new AssertionError(e); } } + ExternalAddressFormatter(@NonNull String localCountryCode, boolean countryCode) { + this.localNumberString = ""; + this.localCountryCode = localCountryCode; + } + public String format(@Nullable String number) { if (number == null) return "Unknown"; if (number.startsWith("__textsecure_group__!")) return number; diff --git a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java b/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java index a43849fee7..6ba71f17c6 100644 --- a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java +++ b/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java @@ -8,11 +8,10 @@ import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.support.annotation.NonNull; +import android.text.TextUtils; import android.util.Log; import org.whispersystems.signalservice.api.push.ContactTokenDetails; -import org.whispersystems.signalservice.api.util.InvalidNumberException; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.util.ArrayList; import java.util.Collection; @@ -173,7 +172,7 @@ public class TextSecureDirectory { db.replace(TABLE_NAME, null, values); } - public void setNumbers(List activeTokens, Collection inactiveTokens) { + public void setNumbers(List activeTokens, Collection
inactiveAddresses) { long timestamp = System.currentTimeMillis(); SQLiteDatabase db = databaseHelper.getWritableDatabase(); db.beginTransaction(); @@ -191,9 +190,9 @@ public class TextSecureDirectory { db.replace(TABLE_NAME, null, values); } - for (String token : inactiveTokens) { + for (Address address : inactiveAddresses) { ContentValues values = new ContentValues(); - values.put(NUMBER, token); + values.put(NUMBER, address.serialize()); values.put(REGISTERED, 0); values.put(TIMESTAMP, timestamp); db.replace(TABLE_NAME, null, values); @@ -205,23 +204,18 @@ public class TextSecureDirectory { } } - public Set getPushEligibleContactNumbers(String localNumber) { - final Uri uri = Phone.CONTENT_URI; - final Set results = new HashSet<>(); - Cursor cursor = null; + public Set
getPushEligibleContactNumbers() { + final Uri uri = Phone.CONTENT_URI; + final Set
results = new HashSet<>(); + Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, new String[] {Phone.NUMBER}, null, null, null); while (cursor != null && cursor.moveToNext()) { final String rawNumber = cursor.getString(0); - if (rawNumber != null) { - try { - final String e164Number = PhoneNumberFormatter.formatNumber(rawNumber, localNumber); - results.add(e164Number); - } catch (InvalidNumberException e) { - Log.w("Directory", "Invalid number: " + rawNumber); - } + if (!TextUtils.isEmpty(rawNumber)) { + results.add(Address.fromExternal(context, rawNumber)); } } @@ -234,7 +228,7 @@ public class TextSecureDirectory { null, null, null, null, null); while (cursor != null && cursor.moveToNext()) { - results.add(cursor.getString(0)); + results.add(Address.fromSerialized(cursor.getString(0))); } } diff --git a/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java b/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java index cdc6a515dd..a7939573dd 100644 --- a/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java +++ b/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java @@ -54,7 +54,7 @@ public class DirectoryRefreshJob extends ContextJob { if (recipients == null) { DirectoryHelper.refreshDirectory(context, KeyCachingService.getMasterSecret(context)); } else { - DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients, TextSecurePreferences.getLocalNumber(context)); + DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients); } SecurityEvent.broadcastSecurityUpdateEvent(context); } finally { diff --git a/src/org/thoughtcrime/securesms/service/RegistrationService.java b/src/org/thoughtcrime/securesms/service/RegistrationService.java index b7359528dc..6c8fa6223a 100644 --- a/src/org/thoughtcrime/securesms/service/RegistrationService.java +++ b/src/org/thoughtcrime/securesms/service/RegistrationService.java @@ -206,6 +206,7 @@ public class RegistrationService extends Service { String challenge = waitForChallenge(); accountManager.verifyAccountWithCode(challenge, signalingKey, registrationId, !supportsGcm); + TextSecurePreferences.setLocalNumber(this, number); handleCommonRegistration(accountManager, number, password, signalingKey, supportsGcm); markAsVerified(number, password, signalingKey); @@ -257,7 +258,7 @@ public class RegistrationService extends Service { TextSecurePreferences.setWebsocketRegistered(this, true); DatabaseFactory.getIdentityDatabase(this).saveIdentity(self, identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED, true, System.currentTimeMillis(), true); - DirectoryHelper.refreshDirectory(this, accountManager, number); + DirectoryHelper.refreshDirectory(this, accountManager); DirectoryRefreshListener.schedule(this); RotateSignedPreKeyListener.schedule(this); diff --git a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java index 34fb2489db..c413b27fc4 100644 --- a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java +++ b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java @@ -9,6 +9,7 @@ import android.os.RemoteException; import android.provider.ContactsContract; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.TextUtils; import android.util.Log; import org.thoughtcrime.securesms.ApplicationContext; @@ -32,6 +33,7 @@ import org.whispersystems.signalservice.api.push.ContactTokenDetails; import java.io.IOException; import java.util.Calendar; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -75,9 +77,9 @@ public class DirectoryHelper { public static void refreshDirectory(@NonNull Context context, @Nullable MasterSecret masterSecret) throws IOException { - RefreshResult result = refreshDirectory(context, - AccountManagerFactory.createManager(context), - TextSecurePreferences.getLocalNumber(context)); + if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) return; + + RefreshResult result = refreshDirectory(context, AccountManagerFactory.createManager(context)); if (!result.getNewUsers().isEmpty() && TextSecurePreferences.isMultiDevice(context)) { ApplicationContext.getInstance(context) @@ -90,32 +92,38 @@ public class DirectoryHelper { } } - public static @NonNull RefreshResult refreshDirectory(@NonNull Context context, - @NonNull SignalServiceAccountManager accountManager, - @NonNull String localNumber) + public static @NonNull RefreshResult refreshDirectory(@NonNull Context context, @NonNull SignalServiceAccountManager accountManager) throws IOException { - TextSecureDirectory directory = TextSecureDirectory.getInstance(context); - Set eligibleContactNumbers = directory.getPushEligibleContactNumbers(localNumber); - List activeTokens = accountManager.getContacts(eligibleContactNumbers); + if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) { + return new RefreshResult(new LinkedList
(), false); + } + + TextSecureDirectory directory = TextSecureDirectory.getInstance(context); + Set
eligibleContactNumbers = directory.getPushEligibleContactNumbers(); + Set serializedAddresses = new HashSet<>(); + + for (Address address : eligibleContactNumbers) { + serializedAddresses.add(address.serialize()); + } + + List activeTokens = accountManager.getContacts(serializedAddresses); if (activeTokens != null) { for (ContactTokenDetails activeToken : activeTokens) { - eligibleContactNumbers.remove(activeToken.getNumber()); - activeToken.setNumber(activeToken.getNumber()); + eligibleContactNumbers.remove(Address.fromSerialized(activeToken.getNumber())); } directory.setNumbers(activeTokens, eligibleContactNumbers); - return updateContactsDatabase(context, localNumber, activeTokens, true); + return updateContactsDatabase(context, activeTokens, true); } - return new RefreshResult(new LinkedList(), false); + return new RefreshResult(new LinkedList
(), false); } public static UserCapabilities refreshDirectoryFor(@NonNull Context context, @Nullable MasterSecret masterSecret, - @NonNull Recipients recipients, - @NonNull String localNumber) + @NonNull Recipients recipients) throws IOException { TextSecureDirectory directory = TextSecureDirectory.getInstance(context); @@ -126,7 +134,7 @@ public class DirectoryHelper { if (details.isPresent()) { directory.setNumber(details.get(), true); - RefreshResult result = updateContactsDatabase(context, localNumber, details.get()); + RefreshResult result = updateContactsDatabase(context, details.get()); if (!result.getNewUsers().isEmpty() && TextSecurePreferences.isMultiDevice(context)) { ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context)); @@ -181,16 +189,12 @@ public class DirectoryHelper { } private static @NonNull RefreshResult updateContactsDatabase(@NonNull Context context, - @NonNull String localNumber, @NonNull final ContactTokenDetails activeToken) { - return updateContactsDatabase(context, localNumber, - new LinkedList() {{add(activeToken);}}, - false); + return updateContactsDatabase(context, new LinkedList() {{add(activeToken);}}, false); } private static @NonNull RefreshResult updateContactsDatabase(@NonNull Context context, - @NonNull String localNumber, @NonNull List activeTokens, boolean removeMissing) { @@ -198,8 +202,8 @@ public class DirectoryHelper { if (account.isPresent()) { try { - List newUsers = DatabaseFactory.getContactsDatabase(context) - .setRegisteredUsers(account.get().getAccount(), localNumber, activeTokens, removeMissing); + List
newUsers = DatabaseFactory.getContactsDatabase(context) + .setRegisteredUsers(account.get().getAccount(), activeTokens, removeMissing); return new RefreshResult(newUsers, account.get().isFresh()); } catch (RemoteException | OperationApplicationException e) { @@ -207,18 +211,16 @@ public class DirectoryHelper { } } - return new RefreshResult(new LinkedList(), false); + return new RefreshResult(new LinkedList
(), false); } private static void notifyNewUsers(@NonNull Context context, @Nullable MasterSecret masterSecret, - @NonNull List newUsers) + @NonNull List
newUsers) { if (!TextSecurePreferences.isNewContactsNotificationEnabled(context)) return; - for (String newUserString : newUsers) { - Address newUser = Address.fromSerialized(newUserString); - + for (Address newUser: newUsers) { if (!SessionUtil.hasSession(context, masterSecret, newUser) && !Util.isOwnNumber(context, newUser)) { IncomingJoinedMessage message = new IncomingJoinedMessage(newUser); Optional insertResult = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message); @@ -287,15 +289,15 @@ public class DirectoryHelper { private static class RefreshResult { - private final List newUsers; - private final boolean fresh; + private final List
newUsers; + private final boolean fresh; - private RefreshResult(List newUsers, boolean fresh) { + private RefreshResult(List
newUsers, boolean fresh) { this.newUsers = newUsers; this.fresh = fresh; } - public List getNewUsers() { + public List
getNewUsers() { return newUsers; } diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 9f0fade5a1..7dcc4e8b46 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -304,7 +304,7 @@ public class TextSecurePreferences { } public static String getLocalNumber(Context context) { - return getStringPreference(context, LOCAL_NUMBER_PREF, "No Stored Number"); + return getStringPreference(context, LOCAL_NUMBER_PREF, null); } public static void setLocalNumber(Context context, String localNumber) { diff --git a/src/org/thoughtcrime/securesms/util/Util.java b/src/org/thoughtcrime/securesms/util/Util.java index bb6dce3790..22c2bb46c2 100644 --- a/src/org/thoughtcrime/securesms/util/Util.java +++ b/src/org/thoughtcrime/securesms/util/Util.java @@ -42,11 +42,14 @@ import android.widget.EditText; import com.google.android.mms.pdu_alt.CharacterSets; import com.google.android.mms.pdu_alt.EncodedStringValue; +import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.mms.OutgoingLegacyMmsConnection; +import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.ByteArrayOutputStream; @@ -234,22 +237,24 @@ public class Util { return total; } - public static @Nullable String getDeviceE164Number(Context context) { - final String localNumber = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number(); - final String countryIso = getSimCountryIso(context); - final Integer countryCode = PhoneNumberUtil.getInstance().getCountryCodeForRegion(countryIso); + public static Optional getDeviceNumber(Context context) { + try { + final String localNumber = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number(); + final Optional countryIso = getSimCountryIso(context); - if (TextUtils.isEmpty(localNumber)) return null; + if (TextUtils.isEmpty(localNumber)) return Optional.absent(); + if (!countryIso.isPresent()) return Optional.absent(); - if (localNumber.startsWith("+")) return localNumber; - else if (!TextUtils.isEmpty(countryIso)) return PhoneNumberFormatter.formatE164(String.valueOf(countryCode), localNumber); - else if (localNumber.length() == 10) return "+1" + localNumber; - else return "+" + localNumber; + return Optional.fromNullable(PhoneNumberUtil.getInstance().parse(localNumber, countryIso.get())); + } catch (NumberParseException e) { + Log.w(TAG, e); + return Optional.absent(); + } } - public static @Nullable String getSimCountryIso(Context context) { + public static Optional getSimCountryIso(Context context) { String simCountryIso = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getSimCountryIso(); - return simCountryIso != null ? simCountryIso.toUpperCase() : null; + return Optional.fromNullable(simCountryIso != null ? simCountryIso.toUpperCase() : null); } public static List> partition(List list, int partitionSize) { diff --git a/test/unitTest/java/org/thoughtcrime/securesms/database/AddressTest.java b/test/unitTest/java/org/thoughtcrime/securesms/database/AddressTest.java index 6c156866a0..be6ec02e41 100644 --- a/test/unitTest/java/org/thoughtcrime/securesms/database/AddressTest.java +++ b/test/unitTest/java/org/thoughtcrime/securesms/database/AddressTest.java @@ -39,6 +39,9 @@ public class AddressTest { assertEquals(formatter.format("+1 415.111.1126"), "+14151111126"); assertEquals(formatter.format("+1 415 111 1127"), "+14151111127"); assertEquals(formatter.format("+1 (415) 111 1128"), "+14151111128"); + + formatter = new Address.ExternalAddressFormatter("+442079460010"); + assertEquals(formatter.format("(020) 7946 0018"), "+442079460018"); } @Test @@ -47,4 +50,10 @@ public class AddressTest { assertEquals(formatter.format("__textsecure_group__!foobar"), "__textsecure_group__!foobar"); } + @Test + public void testLostLocalNumber() throws Exception { + Address.ExternalAddressFormatter formatter = new Address.ExternalAddressFormatter("US", true); + assertEquals(formatter.format("(415) 111-1122"), "+14151111122"); + } + }