UUID migration.

master
Greyson Parrelli 2019-09-06 23:40:06 -04:00 committed by Alan Evans
parent 3dcc2d8171
commit c60909272b
128 changed files with 1556 additions and 1259 deletions

View File

@ -97,7 +97,7 @@ dependencies {
implementation 'org.conscrypt:conscrypt-android:2.0.0'
implementation 'org.signal:aesgcmprovider:0.0.3'
implementation 'org.whispersystems:signal-service-android:2.13.9'
implementation 'org.whispersystems:signal-service-android:2.14.0'
implementation 'org.signal:ringrtc-android:0.1.7.2'

View File

@ -5,7 +5,6 @@ import androidx.annotation.Nullable;
import android.view.View;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;

View File

@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.VerifySpan;
@ -95,7 +96,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
@Override
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireAddress().toPhoneString(), 1);
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireServiceId(), 1);
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
@ -167,7 +168,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
boolean legacy = !messageRecord.isContentBundleKeyExchange();
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
messageRecord.getIndividualRecipient().requireAddress().toPhoneString(),
RecipientUtil.toSignalServiceAddress(getContext(), messageRecord.getIndividualRecipient()),
messageRecord.getRecipientDeviceId(),
messageRecord.getDateSent(),
legacy ? Base64.decode(messageRecord.getBody()) : null,

View File

@ -205,7 +205,7 @@ public class ConversationListItem extends RelativeLayout
fromView.setText(contact);
fromView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), new SpannableString(fromView.getText()), highlightSubstring));
subjectView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), contact.requireAddress().toString(), highlightSubstring));
subjectView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), contact.getE164().or(""), highlightSubstring));
dateView.setText("");
archivedView.setVisibility(GONE);
unreadIndicator.setVisibility(GONE);

View File

@ -36,9 +36,7 @@ import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
import org.thoughtcrime.securesms.logging.Log;

View File

@ -20,6 +20,7 @@ import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
@ -177,7 +178,7 @@ public class DeviceActivity extends PassphraseRequiredActionBarActivity
try {
Context context = DeviceActivity.this;
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
String verificationCode = accountManager.getNewDeviceVerificationCode();
String ephemeralId = uri.getQueryParameter("uuid");
String publicKeyEncoded = uri.getQueryParameter("pub_key");

View File

@ -52,7 +52,6 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
@ -93,7 +92,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private final static String TAG = GroupCreateActivity.class.getSimpleName();
public static final String GROUP_ADDRESS_EXTRA = "group_recipient";
public static final String GROUP_ID_EXTRA = "group_id";
public static final String GROUP_THREAD_EXTRA = "group_thread";
private final DynamicTheme dynamicTheme = new DynamicTheme();
@ -203,10 +202,10 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private void initializeExistingGroup() {
final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA);
final String groupId = getIntent().getStringExtra(GROUP_ID_EXTRA);
if (groupAddress != null) {
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupAddress.toGroupString());
if (groupId != null) {
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupId);
}
}
@ -458,7 +457,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (!activity.isFinishing()) {
Intent intent = activity.getIntent();
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().requireAddress());
intent.putExtra(GROUP_ID_EXTRA, result.get().getGroupRecipient().requireGroupId());
activity.setResult(RESULT_OK, intent);
activity.finish();
}
@ -501,7 +500,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (failIfNotPush && !isPush) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
recipient.toShortString())));
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.requireAddress().serialize())) {
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getE164().or(""))) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group)));
} else {
results.add(new Result(recipient, isPush, null));

View File

@ -32,7 +32,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
@Override
protected List<Recipient> doInBackground(Void... params) {
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireAddress().toGroupString(), true);
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireGroupId(), true);
}
@Override
@ -90,7 +90,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
public GroupMembers(List<Recipient> recipients) {
for (Recipient recipient : recipients) {
if (isLocalNumber(recipient)) {
if (recipient.isLocalNumber()) {
members.push(recipient);
} else {
members.add(recipient);
@ -102,7 +102,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
List<String> recipientStrings = new LinkedList<>();
for (Recipient recipient : members) {
if (isLocalNumber(recipient)) {
if (recipient.isLocalNumber()) {
recipientStrings.add(context.getString(R.string.GroupMembersDialog_me));
} else {
String name = recipient.toShortString();
@ -121,9 +121,5 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
public Recipient get(int index) {
return members.get(index);
}
private boolean isLocalNumber(Recipient recipient) {
return Util.isOwnNumber(context, recipient.requireAddress());
}
}
}

View File

@ -5,7 +5,7 @@ import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import java.io.Closeable;
import java.io.IOException;
import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock;
@ -81,12 +82,7 @@ public class IncomingMessageProcessor {
*/
public @Nullable String processEnvelope(@NonNull SignalServiceEnvelope envelope) {
if (envelope.hasSource()) {
Recipient recipient = Recipient.external(context, envelope.getSource());
if (!isActiveNumber(recipient)) {
recipientDatabase.setRegistered(recipient.getId(), RecipientDatabase.RegisteredState.REGISTERED);
jobManager.add(new DirectoryRefreshJob(recipient, false));
}
Recipient.externalPush(context, envelope.getSourceAddress());
}
if (envelope.isReceipt()) {
@ -113,14 +109,10 @@ public class IncomingMessageProcessor {
private void processReceipt(@NonNull SignalServiceEnvelope envelope) {
Log.i(TAG, String.format(Locale.ENGLISH, "Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
mmsSmsDatabase.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.external(context, envelope.getSource()).getId(), envelope.getTimestamp()),
mmsSmsDatabase.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.externalPush(context, envelope.getSourceAddress()).getId(), envelope.getTimestamp()),
System.currentTimeMillis());
}
private boolean isActiveNumber(@NonNull Recipient recipient) {
return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED;
}
@Override
public void close() {
release();

View File

@ -52,7 +52,6 @@ import androidx.viewpager.widget.ViewPager;
import org.thoughtcrime.securesms.animation.DepthPageTransformer;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;

View File

@ -368,7 +368,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
if (receiptInfoList.isEmpty()) {
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().requireAddress().toGroupString(), false);
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().requireGroupId(), false);
for (Recipient recipient : group) {
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));

View File

@ -11,7 +11,9 @@ import android.widget.BaseAdapter;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Conversions;
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@ -19,11 +21,12 @@ import java.util.List;
class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
private final Context context;
private final GlideRequests glideRequests;
private final MessageRecord record;
private final List<RecipientDeliveryStatus> members;
private final boolean isPushGroup;
private final Context context;
private final GlideRequests glideRequests;
private final MessageRecord record;
private final List<RecipientDeliveryStatus> members;
private final boolean isPushGroup;
private final StableIdGenerator<RecipientId> idGenerator;
MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull MessageRecord record, @NonNull List<RecipientDeliveryStatus> members,
@ -34,6 +37,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
this.record = record;
this.isPushGroup = isPushGroup;
this.members = members;
this.idGenerator = new StableIdGenerator<>();
}
@Override
@ -48,11 +52,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
@Override
public long getItemId(int position) {
try {
return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(members.get(position).recipient.requireAddress().serialize().getBytes()));
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
return idGenerator.getId(members.get(position).recipient.getId());
}
@Override

View File

@ -57,7 +57,6 @@ import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.color.MaterialColors;
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
@ -421,10 +420,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
if (aboutDivider != null) aboutDivider.setVisible(false);
if (divider != null) divider.setVisible(false);
} else {
colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(getActivity()));
colorPreference.setColor(recipient.getColor().toActionBarColor(getActivity()));
colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(requireActivity()));
colorPreference.setColor(recipient.getColor().toActionBarColor(requireActivity()));
aboutPreference.setTitle(formatAddress(recipient.requireAddress()));
aboutPreference.setTitle(formatRecipient(recipient));
aboutPreference.setSummary(recipient.getCustomLabel());
aboutPreference.setSecure(recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED);
@ -453,10 +452,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
}
}
private @NonNull String formatAddress(@NonNull Address address) {
if (address.isPhone()) return PhoneNumberUtils.formatNumber(address.toPhoneString());
else if (address.isEmail()) return address.toEmailString();
else return "";
private @NonNull String formatRecipient(@NonNull Recipient recipient) {
if (recipient.getE164().isPresent()) return PhoneNumberUtils.formatNumber(recipient.requireE164());
else if (recipient.getEmail().isPresent()) return recipient.requireEmail();
else return "";
}
private @NonNull String getRingtoneSummary(@NonNull Context context, @Nullable Uri ringtone) {
@ -697,7 +696,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
if (recipient.get().isGroup()) {
bodyRes = R.string.RecipientPreferenceActivity_block_and_leave_group_description;
if (recipient.get().isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.get().requireAddress().toGroupString())) {
if (recipient.get().isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.get().requireGroupId())) {
titleRes = R.string.RecipientPreferenceActivity_block_and_leave_group;
} else {
titleRes = R.string.RecipientPreferenceActivity_block_group;
@ -745,7 +744,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
DatabaseFactory.getRecipientDatabase(context)
.setBlocked(recipient.getId(), blocked);
if (recipient.isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.requireAddress().toGroupString())) {
if (recipient.isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.requireGroupId())) {
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, recipient);
@ -753,7 +752,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
MessageSender.send(context, leaveMessage.get(), threadId, false, null);
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
String groupId = recipient.requireAddress().toGroupString();
String groupId = recipient.requireGroupId();
groupDatabase.setActive(groupId, false);
groupDatabase.remove(groupId, Recipient.self().getId());
} else {
@ -790,7 +789,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
public void onInSecureCallClicked() {
try {
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
Uri.parse("tel:" + recipient.get().requireAddress().serialize()));
Uri.parse("tel:" + recipient.get().requireE164()));
startActivity(dialIntent);
} catch (ActivityNotFoundException anfe) {
Log.w(TAG, anfe);

View File

@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.logging.Log;
import android.widget.Toast;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Rfc5724Uri;

View File

@ -269,12 +269,16 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
if (localIdentityParcelable == null) throw new AssertionError("local identity required");
if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required");
this.localNumber = getArguments().getString(LOCAL_NUMBER);
this.localNumber = TextSecurePreferences.getLocalNumber(requireContext());
this.localIdentity = localIdentityParcelable.get();
this.recipient = Recipient.live(recipientId);
this.remoteNumber = recipient.get().requireAddress().serialize();
this.remoteNumber = recipient.resolve().getE164().or("");
this.remoteIdentity = remoteIdentityParcelable.get();
if (TextUtils.isEmpty(remoteNumber)) {
Log.w(TAG, "Empty remote number! Did we get a UUID-only message somehow?");
}
this.recipient.observe(this, this::setRecipientText);
new AsyncTask<Void, Void, Fingerprint>() {
@ -573,7 +577,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
protected Void doInBackground(Recipient... params) {
synchronized (SESSION_LOCK) {
if (isChecked) {
Log.i(TAG, "Saving identity: " + params[0].requireAddress());
Log.i(TAG, "Saving identity: " + params[0].getId());
DatabaseFactory.getIdentityDatabase(getActivity())
.saveIdentity(params[0].getId(),
remoteIdentity,

View File

@ -281,7 +281,7 @@ public class WebRtcCallActivity extends Activity {
public void onClick(View v) {
synchronized (SESSION_LOCK) {
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(WebRtcCallActivity.this);
identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.requireAddress().serialize(), 1), theirIdentity, true);
identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.requireServiceId(), 1), theirIdentity, true);
}
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);

View File

@ -20,12 +20,10 @@ import org.thoughtcrime.securesms.backup.BackupProtos.SqlStatement;
import org.thoughtcrime.securesms.backup.BackupProtos.Sticker;
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.SearchDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;

View File

@ -31,7 +31,6 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.RecipientsAdapter;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;

View File

@ -187,11 +187,10 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
}
private void setQuoteAuthor(@NonNull Recipient author) {
boolean outgoing = messageType != MESSAGE_TYPE_INCOMING;
boolean isOwnNumber = Util.isOwnNumber(getContext(), author.requireAddress());
boolean outgoing = messageType != MESSAGE_TYPE_INCOMING;
authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you)
: author.toShortString());
authorView.setText(author.isLocalNumber() ? getContext().getString(R.string.QuoteView_you)
: author.toShortString());
// We use the raw color resource because Android 4.x was struggling with tints here
quoteBarView.setImageResource(author.getColor().toQuoteBarColorResource(getContext(), outgoing));

View File

@ -43,7 +43,7 @@ public class TypingStatusRepository {
}
public synchronized void onTypingStarted(@NonNull Context context, long threadId, @NonNull Recipient author, int device) {
if (author.requireAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context))) {
if (author.isLocalNumber()) {
return;
}
@ -67,7 +67,7 @@ public class TypingStatusRepository {
}
public synchronized void onTypingStopped(@NonNull Context context, long threadId, @NonNull Recipient author, int device, boolean isReplacedByIncomingMessage) {
if (author.requireAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context))) {
if (author.isLocalNumber()) {
return;
}

View File

@ -308,9 +308,10 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObs
this.name.setText(recipient.getName());
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
this.phoneNumber.setText(recipient.requireAddress().serialize() + " (~" + recipient.getProfileName() + ")");
// TODO [greyson] This will need to change in UI PR?
this.phoneNumber.setText(recipient.requireE164() + " (~" + recipient.getProfileName() + ")");
} else {
this.phoneNumber.setText(recipient.requireAddress().serialize());
this.phoneNumber.setText(recipient.requireE164());
}
}

View File

@ -32,7 +32,6 @@ import android.text.TextUtils;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
@ -70,13 +69,13 @@ public class ContactAccessor {
return instance;
}
public Set<Address> getAllContactsWithNumbers(Context context) {
Set<Address> results = new HashSet<>();
public Set<String> getAllContactsWithNumbers(Context context) {
Set<String> results = new HashSet<>();
try (Cursor cursor = context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER}, null ,null, null)) {
while (cursor != null && cursor.moveToNext()) {
if (!TextUtils.isEmpty(cursor.getString(0))) {
results.add(Address.fromSerialized(PhoneNumberFormatter.get(context).format(cursor.getString(0))));
results.add(PhoneNumberFormatter.get(context).format(cursor.getString(0)));
}
}
}
@ -109,17 +108,21 @@ public class ContactAccessor {
final ContentResolver resolver = context.getContentResolver();
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
final List<Address> registeredAddresses = Stream.of(DatabaseFactory.getRecipientDatabase(context).getRegistered()).map(Recipient::resolved).map(Recipient::requireAddress).toList();
final List<String> registeredAddresses = Stream.of(DatabaseFactory.getRecipientDatabase(context).getRegistered())
.map(Recipient::resolved)
.filter(r -> r.getE164().isPresent())
.map(Recipient::requireE164)
.toList();
final Collection<ContactData> lookupData = new ArrayList<>(registeredAddresses.size());
for (Address registeredAddress : registeredAddresses) {
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(registeredAddress.serialize()));
for (String registeredAddress : registeredAddresses) {
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(registeredAddress));
Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null);
try {
if (lookupCursor != null && lookupCursor.moveToFirst()) {
final ContactData contactData = new ContactData(lookupCursor.getLong(0), lookupCursor.getString(1));
contactData.numbers.add(new NumberData("TextSecure", registeredAddress.serialize()));
contactData.numbers.add(new NumberData("TextSecure", registeredAddress));
lookupData.add(contactData);
}
} finally {

View File

@ -88,12 +88,12 @@ public class ContactRepository {
if (noteToSelfTitle.toLowerCase().contains(query.toLowerCase())) {
Recipient self = Recipient.self();
boolean nameMatch = self.getDisplayName().toLowerCase().contains(query.toLowerCase());
boolean numberMatch = self.requireAddress().serialize() != null && self.requireAddress().serialize().contains(query);
boolean numberMatch = self.getE164().isPresent() && self.requireE164().contains(query);
boolean shouldAdd = !nameMatch && !numberMatch;
if (shouldAdd) {
MatrixCursor selfCursor = new MatrixCursor(RecipientDatabase.SEARCH_PROJECTION);
selfCursor.addRow(new Object[]{ self.getId().serialize(), noteToSelfTitle, null, self.requireAddress().serialize(), null, null, -1, RecipientDatabase.RegisteredState.REGISTERED.getId(), noteToSelfTitle });
selfCursor.addRow(new Object[]{ self.getId().serialize(), noteToSelfTitle, null, self.getE164().or(""), self.getEmail().orNull(), null, -1, RecipientDatabase.RegisteredState.REGISTERED.getId(), noteToSelfTitle });
cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor });
}

View File

@ -14,14 +14,12 @@ import android.widget.TextView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
public class ContactSelectionListItem extends LinearLayout implements RecipientForeverObserver {

View File

@ -183,7 +183,7 @@ public class ContactsCursorLoader extends CursorLoader {
while ((threadRecord = reader.getNext()) != null) {
recentConversations.addRow(new Object[] { threadRecord.getRecipient().getId().serialize(),
threadRecord.getRecipient().toShortString(),
threadRecord.getRecipient().requireAddress().serialize(),
threadRecord.getRecipient().requireStringId(),
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactRepository.RECENT_TYPE });

View File

@ -31,7 +31,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.util.Util;
@ -82,17 +81,17 @@ public class ContactsDatabase {
}
public synchronized void setRegisteredUsers(@NonNull Account account,
@NonNull List<Address> registeredAddressList,
@NonNull List<String> registeredAddressList,
boolean remove)
throws RemoteException, OperationApplicationException
{
Set<Address> registeredAddressSet = new HashSet<>(registeredAddressList);
Set<String> registeredAddressSet = new HashSet<>(registeredAddressList);
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
Map<Address, SignalContact> currentContacts = getSignalRawContacts(account);
List<List<Address>> registeredChunks = Util.chunk(registeredAddressList, 50);
Map<String, SignalContact> currentContacts = getSignalRawContacts(account);
List<List<String>> registeredChunks = Util.chunk(registeredAddressList, 50);
for (List<Address> registeredChunk : registeredChunks) {
for (Address registeredAddress : registeredChunk) {
for (List<String> registeredChunk : registeredChunks) {
for (String registeredAddress : registeredChunk) {
if (!currentContacts.containsKey(registeredAddress)) {
Optional<SystemContactInfo> systemContactInfo = getSystemContactInfo(registeredAddress);
@ -109,7 +108,7 @@ public class ContactsDatabase {
}
}
for (Map.Entry<Address, SignalContact> currentContactEntry : currentContacts.entrySet()) {
for (Map.Entry<String, SignalContact> currentContactEntry : currentContacts.entrySet()) {
if (!registeredAddressSet.contains(currentContactEntry.getKey())) {
if (remove) {
Log.i(TAG, "Removing number: " + currentContactEntry.getKey());
@ -240,7 +239,7 @@ public class ContactsDatabase {
private void addContactVoiceSupport(List<ContentProviderOperation> operations,
@NonNull Address address, long rawContactId)
@NonNull String address, long rawContactId)
{
operations.add(ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI)
.withSelection(RawContacts._ID + " = ?", new String[] {String.valueOf(rawContactId)})
@ -250,9 +249,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, address.toPhoneString())
.withValue(ContactsContract.Data.DATA1, address)
.withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name))
.withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, address.toPhoneString()))
.withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, address))
.withYieldAllowed(true)
.build());
}
@ -348,13 +347,13 @@ public class ContactsDatabase {
.build());
}
private @NonNull Map<Address, SignalContact> getSignalRawContacts(@NonNull Account account) {
private @NonNull Map<String, SignalContact> getSignalRawContacts(@NonNull Account account) {
Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon()
.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type).build();
Map<Address, SignalContact> signalContacts = new HashMap<>();
Cursor cursor = null;
Map<String, SignalContact> signalContacts = new HashMap<>();
Cursor cursor = null;
try {
String[] projection = new String[] {BaseColumns._ID, RawContacts.SYNC1, RawContacts.SYNC4, RawContacts.CONTACT_ID, RawContacts.DISPLAY_NAME_PRIMARY, RawContacts.DISPLAY_NAME_SOURCE};
@ -362,7 +361,7 @@ public class ContactsDatabase {
cursor = context.getContentResolver().query(currentContactsUri, projection, null, null, null);
while (cursor != null && cursor.moveToNext()) {
Address currentAddress = Address.fromSerialized(PhoneNumberFormatter.get(context).format(cursor.getString(1)));
String currentAddress = PhoneNumberFormatter.get(context).format(cursor.getString(1));
long rawContactId = cursor.getLong(0);
long contactId = cursor.getLong(3);
String supportsVoice = cursor.getString(2);
@ -380,11 +379,9 @@ public class ContactsDatabase {
return signalContacts;
}
private Optional<SystemContactInfo> getSystemContactInfo(@NonNull Address address)
private Optional<SystemContactInfo> getSystemContactInfo(@NonNull String address)
{
if (!address.isPhone()) return Optional.absent();
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString()));
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address));
String[] projection = {ContactsContract.PhoneLookup.NUMBER,
ContactsContract.PhoneLookup._ID,
ContactsContract.PhoneLookup.DISPLAY_NAME};
@ -395,8 +392,8 @@ public class ContactsDatabase {
numberCursor = context.getContentResolver().query(uri, projection, null, null, null);
while (numberCursor != null && numberCursor.moveToNext()) {
String systemNumber = numberCursor.getString(0);
Address systemAddress = Address.fromSerialized(PhoneNumberFormatter.get(context).format(systemNumber));
String systemNumber = numberCursor.getString(0);
String systemAddress = PhoneNumberFormatter.get(context).format(systemNumber);
if (systemAddress.equals(address)) {
idCursor = context.getContentResolver().query(RawContacts.CONTENT_URI,

View File

@ -187,7 +187,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
public static CharSequence contactToToken(Recipient c) {
String name = c.getName();
String number = c.requireAddress().serialize();
String number = c.getE164().or(c.getEmail()).or("");
SpannableString s = new SpannableString(RecipientsFormatter.formatNameAndNumber(name, number));
int len = s.length();
@ -195,7 +195,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
return s;
}
s.setSpan(new Annotation("number", c.requireAddress().serialize()), 0, len,
s.setSpan(new Annotation("number", number), 0, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return s;

View File

@ -6,7 +6,6 @@ import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.util.Conversions;
@ -19,24 +18,24 @@ import java.security.MessageDigest;
public class GroupRecordContactPhoto implements ContactPhoto {
private final @NonNull Address address;
private final long avatarId;
private final String groupId;
private final long avatarId;
public GroupRecordContactPhoto(@NonNull Address address, long avatarId) {
this.address = address;
public GroupRecordContactPhoto(@NonNull String groupId, long avatarId) {
this.groupId = groupId;
this.avatarId = avatarId;
}
@Override
public InputStream openInputStream(Context context) throws IOException {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(address.toGroupString());
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(groupId);
if (groupRecord.isPresent() && groupRecord.get().getAvatar() != null) {
return new ByteArrayInputStream(groupRecord.get().getAvatar());
}
throw new IOException("Couldn't load avatar for group: " + address.toGroupString());
throw new IOException("Couldn't load avatar for group: " + groupId);
}
@Override
@ -51,7 +50,7 @@ public class GroupRecordContactPhoto implements ContactPhoto {
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
messageDigest.update(address.serialize().getBytes());
messageDigest.update(groupId.getBytes());
messageDigest.update(Conversions.longToByteArray(avatarId));
}
@ -60,12 +59,11 @@ public class GroupRecordContactPhoto implements ContactPhoto {
if (other == null || !(other instanceof GroupRecordContactPhoto)) return false;
GroupRecordContactPhoto that = (GroupRecordContactPhoto)other;
return this.address.equals(that.address) && this.avatarId == that.avatarId;
return this.groupId.equals(that.groupId) && this.avatarId == that.avatarId;
}
@Override
public int hashCode() {
return this.address.hashCode() ^ (int) avatarId;
return this.groupId.hashCode() ^ (int) avatarId;
}
}

View File

@ -7,7 +7,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.io.File;

View File

@ -6,7 +6,7 @@ import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Conversions;
import java.io.FileNotFoundException;
@ -15,12 +15,12 @@ import java.security.MessageDigest;
public class SystemContactPhoto implements ContactPhoto {
private final @NonNull Address address;
private final @NonNull Uri contactPhotoUri;
private final long lastModifiedTime;
private final RecipientId recipientId;
private final Uri contactPhotoUri;
private final long lastModifiedTime;
public SystemContactPhoto(@NonNull Address address, @NonNull Uri contactPhotoUri, long lastModifiedTime) {
this.address = address;
public SystemContactPhoto(@NonNull RecipientId recipientId, @NonNull Uri contactPhotoUri, long lastModifiedTime) {
this.recipientId = recipientId;
this.contactPhotoUri = contactPhotoUri;
this.lastModifiedTime = lastModifiedTime;
}
@ -42,7 +42,7 @@ public class SystemContactPhoto implements ContactPhoto {
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
messageDigest.update(address.serialize().getBytes());
messageDigest.update(recipientId.serialize().getBytes());
messageDigest.update(contactPhotoUri.toString().getBytes());
messageDigest.update(Conversions.longToByteArray(lastModifiedTime));
}
@ -53,12 +53,12 @@ public class SystemContactPhoto implements ContactPhoto {
SystemContactPhoto that = (SystemContactPhoto)other;
return this.address.equals(that.address) && this.contactPhotoUri.equals(that.contactPhotoUri) && this.lastModifiedTime == that.lastModifiedTime;
return this.recipientId.equals(that.recipientId) && this.contactPhotoUri.equals(that.contactPhotoUri) && this.lastModifiedTime == that.lastModifiedTime;
}
@Override
public int hashCode() {
return address.hashCode() ^ contactPhotoUri.hashCode() ^ (int)lastModifiedTime;
return recipientId.hashCode() ^ contactPhotoUri.hashCode() ^ (int)lastModifiedTime;
}
}

View File

@ -1,527 +1,35 @@
package org.thoughtcrime.securesms.contacts.sync;
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import androidx.annotation.WorkerThread;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.logging.Log;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.push.IasTrustStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.api.push.TrustStore;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.internal.contacts.crypto.Quote;
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException;
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
import org.thoughtcrime.securesms.util.FeatureFlags;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class DirectoryHelper {
private static final String TAG = DirectoryHelper.class.getSimpleName();
private static final int CONTACT_DISCOVERY_BATCH_SIZE = 2048;
public static void refreshDirectory(@NonNull Context context, boolean notifyOfNewUsers)
throws IOException
{
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) return;
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) return;
List<RecipientId> newlyActiveUsers = refreshDirectory(context, AccountManagerFactory.createManager(context));
if (TextSecurePreferences.isMultiDevice(context)) {
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
}
if (notifyOfNewUsers) notifyNewUsers(context, newlyActiveUsers);
}
@SuppressLint("CheckResult")
private static @NonNull List<RecipientId> refreshDirectory(@NonNull Context context, @NonNull SignalServiceAccountManager accountManager)
throws IOException
{
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) {
return Collections.emptyList();
}
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
return Collections.emptyList();
}
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
Stream<String> eligibleRecipientDatabaseContactNumbers = Stream.of(recipientDatabase.getAllAddresses()).filter(Address::isPhone).map(Address::toPhoneString);
Stream<String> eligibleSystemDatabaseContactNumbers = Stream.of(ContactAccessor.getInstance().getAllContactsWithNumbers(context)).map(Address::serialize);
Set<String> eligibleContactNumbers = Stream.concat(eligibleRecipientDatabaseContactNumbers, eligibleSystemDatabaseContactNumbers).collect(Collectors.toSet());
Future<DirectoryResult> legacyRequest = getLegacyDirectoryResult(context, accountManager, recipientDatabase, eligibleContactNumbers);
List<Future<Set<String>>> contactServiceRequest = getContactServiceDirectoryResult(context, accountManager, eligibleContactNumbers);
try {
DirectoryResult legacyResult = legacyRequest.get();
Optional<Set<String>> contactServiceResult = executeAndMergeContactDiscoveryRequests(accountManager, contactServiceRequest);
if (!contactServiceResult.isPresent()) {
Log.i(TAG, "[Batch] New contact discovery service failed, so we're skipping the comparison.");
return legacyResult.getNewlyActiveRecipients();
}
if (legacyResult.getNumbers().size() == contactServiceResult.get().size() && legacyResult.getNumbers().containsAll(contactServiceResult.get())) {
Log.i(TAG, "[Batch] New contact discovery service request matched existing results.");
accountManager.reportContactDiscoveryServiceMatch();
} else {
Log.w(TAG, "[Batch] New contact discovery service request did NOT match existing results.");
accountManager.reportContactDiscoveryServiceMismatch();
}
return legacyResult.getNewlyActiveRecipients();
} catch (InterruptedException e) {
throw new IOException("[Batch] Operation was interrupted.", e);
} catch (ExecutionException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
Log.e(TAG, "[Batch] Experienced an unexpected exception.", e);
throw new AssertionError(e);
}
}
}
public static RegisteredState refreshDirectoryFor(@NonNull Context context,
@NonNull Recipient recipient)
throws IOException
{
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
Future<RegisteredState> legacyRequest = getLegacyRegisteredState(context, accountManager, recipientDatabase, recipient);
List<Future<Set<String>>> contactServiceRequest = getContactServiceDirectoryResult(context, accountManager, Collections.singleton(recipient.requireAddress().serialize()));
try {
RegisteredState legacyState = legacyRequest.get();
Optional<Set<String>> contactServiceResult = executeAndMergeContactDiscoveryRequests(accountManager, contactServiceRequest);
if (!contactServiceResult.isPresent()) {
Log.i(TAG, "[Singular] New contact discovery service failed, so we're skipping the comparison.");
return legacyState;
}
RegisteredState contactServiceState = contactServiceResult.get().size() == 1 ? RegisteredState.REGISTERED : RegisteredState.NOT_REGISTERED;
if (legacyState == contactServiceState) {
Log.i(TAG, "[Singular] New contact discovery service request matched existing results.");
accountManager.reportContactDiscoveryServiceMatch();
} else {
Log.w(TAG, "[Singular] New contact discovery service request did NOT match existing results.");
accountManager.reportContactDiscoveryServiceMismatch();
}
return legacyState;
} catch (InterruptedException e) {
throw new IOException("[Singular] Operation was interrupted.", e);
} catch (ExecutionException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
Log.e(TAG, "[Singular] Experienced an unexpected exception.", e);
throw new AssertionError(e);
}
}
}
private static void updateContactsDatabase(@NonNull Context context, @NonNull List<RecipientId> activeIds, boolean removeMissing) {
Optional<AccountHolder> account = getOrCreateAccount(context);
if (account.isPresent()) {
try {
List<Address> activeAddresses = Stream.of(activeIds).map(Recipient::resolved).map(Recipient::requireAddress).toList();
DatabaseFactory.getContactsDatabase(context).removeDeletedRawContacts(account.get().getAccount());
DatabaseFactory.getContactsDatabase(context).setRegisteredUsers(account.get().getAccount(), activeAddresses, removeMissing);
Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context);
RecipientDatabase.BulkOperationsHandle handle = DatabaseFactory.getRecipientDatabase(context).resetAllSystemContactInfo();
try {
while (cursor != null && cursor.moveToNext()) {
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
if (!TextUtils.isEmpty(number)) {
RecipientId recipientId = Recipient.external(context, number).getId();
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String contactPhotoUri = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
String contactLabel = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LABEL));
int phoneType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.TYPE));
Uri contactUri = ContactsContract.Contacts.getLookupUri(cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone._ID)),
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY)));
handle.setSystemContactInfo(recipientId, displayName, contactPhotoUri, contactLabel, phoneType, contactUri.toString());
}
}
} finally {
handle.finish();
}
if (NotificationChannels.supported()) {
try (RecipientDatabase.RecipientReader recipients = DatabaseFactory.getRecipientDatabase(context).getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = recipients.getNext()) != null) {
NotificationChannels.updateContactChannelName(context, recipient);
}
}
}
} catch (RemoteException | OperationApplicationException e) {
Log.w(TAG, "Failed to update contacts.", e);
}
}
}
private static void notifyNewUsers(@NonNull Context context,
@NonNull List<RecipientId> newUsers)
{
if (!TextSecurePreferences.isNewContactsNotificationEnabled(context)) return;
for (RecipientId newUser: newUsers) {
Recipient recipient = Recipient.resolved(newUser);
if (!SessionUtil.hasSession(context, recipient.requireAddress()) && !recipient.isLocalNumber()) {
IncomingJoinedMessage message = new IncomingJoinedMessage(newUser);
Optional<InsertResult> insertResult = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message);
if (insertResult.isPresent()) {
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if (hour >= 9 && hour < 23) {
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), true);
} else {
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), false);
}
}
}
}
}
private static Optional<AccountHolder> getOrCreateAccount(Context context) {
AccountManager accountManager = AccountManager.get(context);
Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms");
Optional<AccountHolder> account;
if (accounts.length == 0) account = createAccount(context);
else account = Optional.of(new AccountHolder(accounts[0], false));
if (account.isPresent() && !ContentResolver.getSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY)) {
ContentResolver.setSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY, true);
}
return account;
}
private static Optional<AccountHolder> createAccount(Context context) {
AccountManager accountManager = AccountManager.get(context);
Account account = new Account(context.getString(R.string.app_name), "org.thoughtcrime.securesms");
if (accountManager.addAccountExplicitly(account, null, null)) {
Log.i(TAG, "Created new account...");
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
return Optional.of(new AccountHolder(account, true));
@WorkerThread
public static void refreshDirectory(@NonNull Context context, boolean notifyOfNewUsers) throws IOException {
if (FeatureFlags.UUIDS) {
// TODO [greyson] Create a DirectoryHelperV2 when appropriate.
DirectoryHelperV1.refreshDirectory(context, notifyOfNewUsers);
} else {
Log.w(TAG, "Failed to create account!");
return Optional.absent();
DirectoryHelperV1.refreshDirectory(context, notifyOfNewUsers);
}
}
private static Future<DirectoryResult> getLegacyDirectoryResult(@NonNull Context context,
@NonNull SignalServiceAccountManager accountManager,
@NonNull RecipientDatabase recipientDatabase,
@NonNull Set<String> eligibleContactNumbers)
{
return SignalExecutors.UNBOUNDED.submit(() -> {
List<ContactTokenDetails> activeTokens = accountManager.getContacts(eligibleContactNumbers);
if (activeTokens != null) {
List<RecipientId> activeIds = new LinkedList<>();
List<RecipientId> inactiveIds = new LinkedList<>();
Set<String> inactiveContactNumbers = new HashSet<>(eligibleContactNumbers);
for (ContactTokenDetails activeToken : activeTokens) {
activeIds.add(recipientDatabase.getOrInsertFromE164(activeToken.getNumber()));
inactiveContactNumbers.remove(activeToken.getNumber());
}
for (String inactiveContactNumber : inactiveContactNumbers) {
inactiveIds.add(recipientDatabase.getOrInsertFromE164(inactiveContactNumber));
}
Set<RecipientId> currentActiveIds = new HashSet<>(recipientDatabase.getRegistered());
Set<RecipientId> contactIds = new HashSet<>(recipientDatabase.getSystemContacts());
List<RecipientId> newlyActiveIds = Stream.of(activeIds)
.filter(id -> !currentActiveIds.contains(id))
.filter(contactIds::contains)
.toList();
recipientDatabase.setRegistered(activeIds, inactiveIds);
updateContactsDatabase(context, activeIds, true);
Set<String> activeContactNumbers = Stream.of(activeIds).map(Recipient::resolved).map(Recipient::requireAddress).map(Address::serialize).collect(Collectors.toSet());
if (TextSecurePreferences.hasSuccessfullyRetrievedDirectory(context)) {
return new DirectoryResult(activeContactNumbers, newlyActiveIds);
} else {
TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(context, true);
return new DirectoryResult(activeContactNumbers);
}
}
return new DirectoryResult(Collections.emptySet(), Collections.emptyList());
});
}
private static Future<RegisteredState> getLegacyRegisteredState(@NonNull Context context,
@NonNull SignalServiceAccountManager accountManager,
@NonNull RecipientDatabase recipientDatabase,
@NonNull Recipient recipient)
{
return SignalExecutors.UNBOUNDED.submit(() -> {
boolean activeUser = recipient.resolve().getRegistered() == RegisteredState.REGISTERED;
boolean systemContact = recipient.isSystemContact();
String number = recipient.requireAddress().serialize();
Optional<ContactTokenDetails> details = accountManager.getContact(number);
if (details.isPresent()) {
recipientDatabase.setRegistered(recipient.getId(), RegisteredState.REGISTERED);
if (Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
updateContactsDatabase(context, Util.asList(recipient.getId()), false);
}
if (!activeUser && TextSecurePreferences.isMultiDevice(context)) {
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
}
if (!activeUser && systemContact && !TextSecurePreferences.getNeedsSqlCipherMigration(context)) {
notifyNewUsers(context, Collections.singletonList(recipient.getId()));
}
return RegisteredState.REGISTERED;
} else {
recipientDatabase.setRegistered(recipient.getId(), RegisteredState.NOT_REGISTERED);
return RegisteredState.NOT_REGISTERED;
}
});
}
private static List<Future<Set<String>>> getContactServiceDirectoryResult(@NonNull Context context,
@NonNull SignalServiceAccountManager accountManager,
@NonNull Set<String> eligibleContactNumbers)
{
Set<String> sanitizedNumbers = sanitizeNumbers(eligibleContactNumbers);
List<Set<String>> batches = splitIntoBatches(sanitizedNumbers, CONTACT_DISCOVERY_BATCH_SIZE);
List<Future<Set<String>>> futures = new ArrayList<>(batches.size());
KeyStore iasKeyStore = getIasKeyStore(context);
for (Set<String> batch : batches) {
Future<Set<String>> future = SignalExecutors.UNBOUNDED.submit(() -> {
return new HashSet<>(accountManager.getRegisteredUsers(iasKeyStore, batch, BuildConfig.MRENCLAVE));
});
futures.add(future);
}
return futures;
}
private static Set<String> sanitizeNumbers(@NonNull Set<String> numbers) {
return Stream.of(numbers).filter(number -> {
try {
return number.startsWith("+") && number.length() > 1 && number.charAt(1) != '0' && Long.parseLong(number.substring(1)) > 0;
} catch (NumberFormatException e) {
return false;
}
}).collect(Collectors.toSet());
}
private static List<Set<String>> splitIntoBatches(@NonNull Set<String> numbers, int batchSize) {
List<String> numberList = new ArrayList<>(numbers);
List<Set<String>> batches = new LinkedList<>();
for (int i = 0; i < numberList.size(); i += batchSize) {
List<String> batch = numberList.subList(i, Math.min(numberList.size(), i + batchSize));
batches.add(new HashSet<>(batch));
}
return batches;
}
private static Optional<Set<String>> executeAndMergeContactDiscoveryRequests(@NonNull SignalServiceAccountManager accountManager, @NonNull List<Future<Set<String>>> futures) {
Set<String> results = new HashSet<>();
try {
for (Future<Set<String>> future : futures) {
results.addAll(future.get());
}
} catch (InterruptedException e) {
Log.w(TAG, "Contact discovery batch was interrupted.", e);
accountManager.reportContactDiscoveryServiceUnexpectedError(buildErrorReason(e));
return Optional.absent();
} catch (ExecutionException e) {
if (isAttestationError(e.getCause())) {
Log.w(TAG, "Failed during attestation.", e);
accountManager.reportContactDiscoveryServiceAttestationError(buildErrorReason(e.getCause()));
return Optional.absent();
} else if (e.getCause() instanceof PushNetworkException) {
Log.w(TAG, "Failed due to poor network.", e);
return Optional.absent();
} else if (e.getCause() instanceof NonSuccessfulResponseCodeException) {
Log.w(TAG, "Failed due to non successful response code.", e);
return Optional.absent();
} else {
Log.w(TAG, "Failed for an unknown reason.", e);
accountManager.reportContactDiscoveryServiceUnexpectedError(buildErrorReason(e.getCause()));
return Optional.absent();
}
}
return Optional.of(results);
}
private static boolean isAttestationError(Throwable e) {
return e instanceof CertificateException ||
e instanceof SignatureException ||
e instanceof UnauthenticatedQuoteException ||
e instanceof UnauthenticatedResponseException ||
e instanceof Quote.InvalidQuoteFormatException;
}
private static KeyStore getIasKeyStore(@NonNull Context context) {
try {
TrustStore contactTrustStore = new IasTrustStore(context);
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(contactTrustStore.getKeyStoreInputStream(), contactTrustStore.getKeyStorePassword().toCharArray());
return keyStore;
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
private static String buildErrorReason(@Nullable Throwable t) {
if (t == null) {
return "null";
}
String rawString = android.util.Log.getStackTraceString(t);
List<String> lines = Arrays.asList(rawString.split("\\n"));
String errorString;
if (lines.size() > 1) {
errorString = t.getClass().getName() + "\n" + Util.join(lines.subList(1, lines.size()), "\n");
@WorkerThread
public static RegisteredState refreshDirectoryFor(@NonNull Context context, @NonNull Recipient recipient, boolean notifyOfNewUsers) throws IOException {
if (FeatureFlags.UUIDS) {
// TODO [greyson] Create a DirectoryHelperV2 when appropriate.
return DirectoryHelperV1.refreshDirectoryFor(context, recipient, notifyOfNewUsers);
} else {
errorString = t.getClass().getName();
}
if (errorString.length() > 1000) {
return errorString.substring(0, 1000);
} else {
return errorString;
return DirectoryHelperV1.refreshDirectoryFor(context, recipient, notifyOfNewUsers);
}
}
private static class DirectoryResult {
private final Set<String> numbers;
private final List<RecipientId> newlyActiveRecipients;
DirectoryResult(@NonNull Set<String> numbers) {
this(numbers, Collections.emptyList());
}
DirectoryResult(@NonNull Set<String> numbers, @NonNull List<RecipientId> newlyActiveRecipients) {
this.numbers = numbers;
this.newlyActiveRecipients = newlyActiveRecipients;
}
Set<String> getNumbers() {
return numbers;
}
List<RecipientId> getNewlyActiveRecipients() {
return newlyActiveRecipients;
}
}
private static class AccountHolder {
private final boolean fresh;
private final Account account;
private AccountHolder(Account account, boolean fresh) {
this.fresh = fresh;
this.account = account;
}
@SuppressWarnings("unused")
public boolean isFresh() {
return fresh;
}
public Account getAccount() {
return account;
}
}
}

View File

@ -0,0 +1,351 @@
package org.thoughtcrime.securesms.contacts.sync;
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import java.io.IOException;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
class DirectoryHelperV1 {
private static final String TAG = DirectoryHelperV1.class.getSimpleName();
@WorkerThread
static void refreshDirectory(@NonNull Context context, boolean notifyOfNewUsers) throws IOException {
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) return;
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) return;
List<RecipientId> newlyActiveUsers = refreshDirectory(context, ApplicationDependencies.getSignalServiceAccountManager());
if (TextSecurePreferences.isMultiDevice(context)) {
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
}
if (notifyOfNewUsers) notifyNewUsers(context, newlyActiveUsers);
}
@SuppressLint("CheckResult")
private static @NonNull List<RecipientId> refreshDirectory(@NonNull Context context, @NonNull SignalServiceAccountManager accountManager) throws IOException {
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) {
return Collections.emptyList();
}
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
return Collections.emptyList();
}
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
Stream<String> eligibleRecipientDatabaseContactNumbers = Stream.of(recipientDatabase.getAllPhoneNumbers());
Stream<String> eligibleSystemDatabaseContactNumbers = Stream.of(ContactAccessor.getInstance().getAllContactsWithNumbers(context));
Set<String> eligibleContactNumbers = Stream.concat(eligibleRecipientDatabaseContactNumbers, eligibleSystemDatabaseContactNumbers).collect(Collectors.toSet());
try {
Future<DirectoryResult> legacyRequest = getLegacyDirectoryResult(context, accountManager, recipientDatabase, eligibleContactNumbers);
DirectoryResult legacyResult = legacyRequest.get();
return legacyResult.getNewlyActiveRecipients();
} catch (InterruptedException e) {
throw new IOException("[Batch] Operation was interrupted.", e);
} catch (ExecutionException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
Log.e(TAG, "[Batch] Experienced an unexpected exception.", e);
throw new AssertionError(e);
}
}
}
@WorkerThread
static RegisteredState refreshDirectoryFor(@NonNull Context context, @NonNull Recipient recipient, boolean notifyOfNewUsers) throws IOException {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
Future<RegisteredState> legacyRequest = getLegacyRegisteredState(context, accountManager, recipientDatabase, recipient);
try {
return legacyRequest.get();
} catch (InterruptedException e) {
throw new IOException("[Singular] Operation was interrupted.", e);
} catch (ExecutionException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
Log.e(TAG, "[Singular] Experienced an unexpected exception.", e);
throw new AssertionError(e);
}
}
}
private static void updateContactsDatabase(@NonNull Context context, @NonNull List<RecipientId> activeIds, boolean removeMissing) {
Optional<AccountHolder> account = getOrCreateAccount(context);
if (account.isPresent()) {
try {
List<String> activeAddresses = Stream.of(activeIds).map(Recipient::resolved).filter(Recipient::hasE164).map(Recipient::requireE164).toList();
DatabaseFactory.getContactsDatabase(context).removeDeletedRawContacts(account.get().getAccount());
DatabaseFactory.getContactsDatabase(context).setRegisteredUsers(account.get().getAccount(), activeAddresses, removeMissing);
Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context);
RecipientDatabase.BulkOperationsHandle handle = DatabaseFactory.getRecipientDatabase(context).resetAllSystemContactInfo();
try {
while (cursor != null && cursor.moveToNext()) {
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
if (!TextUtils.isEmpty(number)) {
RecipientId recipientId = Recipient.external(context, number).getId();
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String contactPhotoUri = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
String contactLabel = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LABEL));
int phoneType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.TYPE));
Uri contactUri = ContactsContract.Contacts.getLookupUri(cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone._ID)),
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY)));
handle.setSystemContactInfo(recipientId, displayName, contactPhotoUri, contactLabel, phoneType, contactUri.toString());
}
}
} finally {
handle.finish();
}
if (NotificationChannels.supported()) {
try (RecipientDatabase.RecipientReader recipients = DatabaseFactory.getRecipientDatabase(context).getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = recipients.getNext()) != null) {
NotificationChannels.updateContactChannelName(context, recipient);
}
}
}
} catch (RemoteException | OperationApplicationException e) {
Log.w(TAG, "Failed to update contacts.", e);
}
}
}
private static void notifyNewUsers(@NonNull Context context,
@NonNull List<RecipientId> newUsers)
{
if (!TextSecurePreferences.isNewContactsNotificationEnabled(context)) return;
for (RecipientId newUser: newUsers) {
Recipient recipient = Recipient.resolved(newUser);
if (!SessionUtil.hasSession(context, recipient.getId()) && !recipient.isLocalNumber()) {
IncomingJoinedMessage message = new IncomingJoinedMessage(newUser);
Optional<InsertResult> insertResult = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message);
if (insertResult.isPresent()) {
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if (hour >= 9 && hour < 23) {
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), true);
} else {
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), false);
}
}
}
}
}
private static Optional<AccountHolder> getOrCreateAccount(Context context) {
AccountManager accountManager = AccountManager.get(context);
Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms");
Optional<AccountHolder> account;
if (accounts.length == 0) account = createAccount(context);
else account = Optional.of(new AccountHolder(accounts[0], false));
if (account.isPresent() && !ContentResolver.getSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY)) {
ContentResolver.setSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY, true);
}
return account;
}
private static Optional<AccountHolder> createAccount(Context context) {
AccountManager accountManager = AccountManager.get(context);
Account account = new Account(context.getString(R.string.app_name), "org.thoughtcrime.securesms");
if (accountManager.addAccountExplicitly(account, null, null)) {
Log.i(TAG, "Created new account...");
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
return Optional.of(new AccountHolder(account, true));
} else {
Log.w(TAG, "Failed to create account!");
return Optional.absent();
}
}
private static Future<DirectoryResult> getLegacyDirectoryResult(@NonNull Context context,
@NonNull SignalServiceAccountManager accountManager,
@NonNull RecipientDatabase recipientDatabase,
@NonNull Set<String> eligibleContactNumbers)
{
return SignalExecutors.UNBOUNDED.submit(() -> {
List<ContactTokenDetails> activeTokens = accountManager.getContacts(eligibleContactNumbers);
if (activeTokens != null) {
List<RecipientId> activeIds = new LinkedList<>();
List<RecipientId> inactiveIds = new LinkedList<>();
Set<String> inactiveContactNumbers = new HashSet<>(eligibleContactNumbers);
for (ContactTokenDetails activeToken : activeTokens) {
activeIds.add(recipientDatabase.getOrInsertFromE164(activeToken.getNumber()));
inactiveContactNumbers.remove(activeToken.getNumber());
}
for (String inactiveContactNumber : inactiveContactNumbers) {
inactiveIds.add(recipientDatabase.getOrInsertFromE164(inactiveContactNumber));
}
Set<RecipientId> currentActiveIds = new HashSet<>(recipientDatabase.getRegistered());
Set<RecipientId> contactIds = new HashSet<>(recipientDatabase.getSystemContacts());
List<RecipientId> newlyActiveIds = Stream.of(activeIds)
.filter(id -> !currentActiveIds.contains(id))
.filter(contactIds::contains)
.toList();
recipientDatabase.setRegistered(activeIds, inactiveIds);
updateContactsDatabase(context, activeIds, true);
Set<String> activeContactNumbers = Stream.of(activeIds).map(Recipient::resolved).filter(Recipient::hasSmsAddress).map(Recipient::requireSmsAddress).collect(Collectors.toSet());
if (TextSecurePreferences.hasSuccessfullyRetrievedDirectory(context)) {
return new DirectoryResult(activeContactNumbers, newlyActiveIds);
} else {
TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(context, true);
return new DirectoryResult(activeContactNumbers);
}
}
return new DirectoryResult(Collections.emptySet(), Collections.emptyList());
});
}
private static Future<RegisteredState> getLegacyRegisteredState(@NonNull Context context,
@NonNull SignalServiceAccountManager accountManager,
@NonNull RecipientDatabase recipientDatabase,
@NonNull Recipient recipient)
{
return SignalExecutors.UNBOUNDED.submit(() -> {
boolean activeUser = recipient.resolve().getRegistered() == RegisteredState.REGISTERED;
boolean systemContact = recipient.isSystemContact();
Optional<ContactTokenDetails> details = recipient.hasE164() ? accountManager.getContact(recipient.requireE164()) : Optional.absent();
if (details.isPresent()) {
recipientDatabase.setRegistered(recipient.getId(), RegisteredState.REGISTERED);
if (Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
updateContactsDatabase(context, Util.asList(recipient.getId()), false);
}
if (!activeUser && TextSecurePreferences.isMultiDevice(context)) {
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
}
if (!activeUser && systemContact && !TextSecurePreferences.getNeedsSqlCipherMigration(context)) {
notifyNewUsers(context, Collections.singletonList(recipient.getId()));
}
return RegisteredState.REGISTERED;
} else {
recipientDatabase.setRegistered(recipient.getId(), RegisteredState.NOT_REGISTERED);
return RegisteredState.NOT_REGISTERED;
}
});
}
private static class DirectoryResult {
private final Set<String> numbers;
private final List<RecipientId> newlyActiveRecipients;
DirectoryResult(@NonNull Set<String> numbers) {
this(numbers, Collections.emptyList());
}
DirectoryResult(@NonNull Set<String> numbers, @NonNull List<RecipientId> newlyActiveRecipients) {
this.numbers = numbers;
this.newlyActiveRecipients = newlyActiveRecipients;
}
Set<String> getNumbers() {
return numbers;
}
List<RecipientId> getNewlyActiveRecipients() {
return newlyActiveRecipients;
}
}
private static class AccountHolder {
private final boolean fresh;
private final Account account;
private AccountHolder(Account account, boolean fresh) {
this.fresh = fresh;
this.account = account;
}
@SuppressWarnings("unused")
public boolean isFresh() {
return fresh;
}
public Account getAccount() {
return account;
}
}
}

View File

@ -25,7 +25,6 @@ import org.thoughtcrime.securesms.contactshare.Contact.PostalAddress;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.SpanUtil;
@ -123,7 +122,7 @@ public final class ContactUtil {
CharSequence[] values = new CharSequence[choices.size()];
for (int i = 0; i < values.length; i++) {
values[i] = getPrettyPhoneNumber(choices.get(i).requireAddress().toPhoneString(), locale);
values[i] = getPrettyPhoneNumber(choices.get(i).requireE164(), locale);
}
new AlertDialog.Builder(context)

View File

@ -228,7 +228,7 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActionBarAct
inviteButtonView.setOnClickListener(v -> {
ContactUtil.selectRecipientThroughDialog(this, systemUsers, dynamicLanguage.getCurrentLocale(), recipient -> {
CommunicationActions.composeSmsThroughDefaultApp(this, recipient.requireAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
CommunicationActions.composeSmsThroughDefaultApp(this, recipient, getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
});
});
} else {

View File

@ -940,7 +940,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText.appendInvite(inviteText);
} else {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("smsto:" + recipient.get().requireAddress().serialize()));
intent.setData(Uri.parse("smsto:" + recipient.get().requireSmsAddress()));
intent.putExtra("sms_body", inviteText);
intent.putExtra(Intent.EXTRA_TEXT, inviteText);
startActivity(intent);
@ -984,7 +984,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void handleAddShortcut() {
Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.get().requireAddress());
Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.get().getId());
new AsyncTask<Void, Void, IconCompat>() {
@ -1018,7 +1018,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
.or(Optional.fromNullable(recipient.get().getProfileName()))
.or(recipient.get().toShortString());
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.get().requireAddress().serialize() + '-' + System.currentTimeMillis())
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.get().getId().serialize() + '-' + System.currentTimeMillis())
.setShortLabel(name)
.setIcon(icon)
.setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getId()))
@ -1056,7 +1056,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
MessageSender.send(this, leaveMessage.get(), threadId, false, null);
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
String groupId = groupRecipient.requireAddress().toGroupString();
String groupId = groupRecipient.requireGroupId();
groupDatabase.setActive(groupId, false);
groupDatabase.remove(groupId, Recipient.self().getId());
@ -1072,7 +1072,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleEditPushGroup() {
Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class);
intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipient.get().requireAddress());
intent.putExtra(GroupCreateActivity.GROUP_ID_EXTRA, recipient.get().requireGroupId());
startActivityForResult(intent, GROUP_EDIT);
}
@ -1116,7 +1116,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else {
try {
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
Uri.parse("tel:" + recipient.requireAddress().serialize()));
Uri.parse("tel:" + recipient.requireSmsAddress()));
startActivity(dialIntent);
} catch (ActivityNotFoundException anfe) {
Log.w(TAG, anfe);
@ -1132,7 +1132,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void handleAddToContacts() {
if (recipient.get().requireAddress().isGroup()) return;
if (recipient.get().isGroup()) return;
try {
startActivityForResult(RecipientExporter.export(recipient.get()).asAddContactIntent(), ADD_CONTACT);
@ -1142,7 +1142,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private boolean handleDisplayQuickContact() {
if (recipient.get().requireAddress().isGroup()) return false;
if (recipient.get().isGroup()) return false;
if (recipient.get().getContactUri() != null) {
ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.get().getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
@ -1397,8 +1397,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (registeredState == RegisteredState.UNKNOWN) {
try {
Log.i(TAG, "Refreshing directory for user: " + recipient.requireAddress().serialize());
registeredState = DirectoryHelper.refreshDirectoryFor(context, recipient);
Log.i(TAG, "Refreshing directory for user: " + recipient.getId().serialize());
registeredState = DirectoryHelper.refreshDirectoryFor(context, recipient, false);
} catch (IOException e) {
Log.w(TAG, e);
}
@ -1487,13 +1487,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (params[0].isGroup()) {
recipients.addAll(DatabaseFactory.getGroupDatabase(ConversationActivity.this)
.getGroupMembers(params[0].requireAddress().toGroupString(), false));
.getGroupMembers(params[0].requireGroupId(), false));
} else {
recipients.add(params[0]);
}
for (Recipient recipient : recipients) {
Log.i(TAG, "Loading identity for: " + recipient.requireAddress());
Log.i(TAG, "Loading identity for: " + recipient.getId());
identityRecordList.add(identityDatabase.getIdentity(recipient.getId()));
}
@ -2023,9 +2023,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@SuppressWarnings("SimplifiableIfStatement")
private boolean isSelfConversation() {
if (!TextSecurePreferences.isPushRegistered(this)) return false;
if (recipient.get().isGroup()) return false;
if (recipient.get().isGroup()) return false;
return Util.isOwnNumber(this, recipient.get().requireAddress());
return recipient.get().isLocalNumber();
}
private boolean isGroupConversation() {
@ -2152,8 +2152,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
boolean initiating = threadId == -1;
boolean needsSplit = !transport.isSms() && message.length() > transport.calculateCharacters(message).maxPrimaryMessageSize;
boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
recipient.isGroup() ||
recipient.requireAddress().isEmail() ||
recipient.isGroup() ||
recipient.getEmail().isPresent() ||
inputPanel.getQuote().isPresent() ||
linkPreviewViewModel.hasLinkPreview() ||
needsSplit;
@ -2161,7 +2161,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Log.i(TAG, "isManual Selection: " + sendButton.isManualSelection());
Log.i(TAG, "forceSms: " + forceSms);
if ((recipient.isMmsGroup() || recipient.requireAddress().isEmail()) && !isMmsEnabled) {
if ((recipient.isMmsGroup() || recipient.getEmail().isPresent()) && !isMmsEnabled) {
handleManualMmsRequired();
} else if (!forceSms && identityRecords.isUnverified()) {
handleUnverifiedRecipients();

View File

@ -1083,7 +1083,7 @@ public class ConversationFragment extends Fragment
if (getContext() == null) return;
ContactUtil.selectRecipientThroughDialog(getContext(), choices, locale, recipient -> {
CommunicationActions.composeSmsThroughDefaultApp(getContext(), recipient.requireAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
CommunicationActions.composeSmsThroughDefaultApp(getContext(), recipient, getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
});
}
}

View File

@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
public class ConversationTitleView extends RelativeLayout {
@ -116,7 +117,7 @@ public class ConversationTitleView extends RelativeLayout {
this.title.setText(recipient.getName());
this.subtitle.setText(Stream.of(recipient.getParticipants())
.filter(r -> !r.requireAddress().serialize().equals(localNumber))
.filterNot(Recipient::isLocalNumber)
.map(Recipient::toShortString)
.collect(Collectors.joining(", ")));
@ -130,7 +131,7 @@ public class ConversationTitleView extends RelativeLayout {
@SuppressLint("SetTextI18n")
private void setNonContactRecipientTitle(Recipient recipient) {
this.title.setText(recipient.requireAddress().serialize());
this.title.setText(Util.getFirstNonEmpty(recipient.getE164().orNull(), recipient.getUuid().orNull()));
if (TextUtils.isEmpty(recipient.getProfileName())) {
this.subtitle.setText(null);

View File

@ -4,16 +4,17 @@ import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.state.SessionStore;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
public class SessionUtil {
public static boolean hasSession(Context context, @NonNull Address address) {
public static boolean hasSession(@NonNull Context context, @NonNull RecipientId id) {
SessionStore sessionStore = new TextSecureSessionStore(context);
SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(address.serialize(), SignalServiceAddress.DEFAULT_DEVICE_ID);
SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(Recipient.resolved(id).requireServiceId(), SignalServiceAddress.DEFAULT_DEVICE_ID);
return sessionStore.containsSession(axolotlAddress);
}

View File

@ -48,7 +48,8 @@ public class UnidentifiedAccessUtil {
try {
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
byte[] ourUnidentifiedAccessCertificate = recipient.resolve().isUuidSupported() ? TextSecurePreferences.getUnidentifiedAccessCertificate(context)
: TextSecurePreferences.getUnidentifiedAccessCertificateLegacy(context) ;
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
@ -56,7 +57,8 @@ public class UnidentifiedAccessUtil {
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
" | Our access key present? " + (ourUnidentifiedAccessKey != null) +
" | Our certificate present? " + (ourUnidentifiedAccessCertificate != null));
" | Our certificate present? " + (ourUnidentifiedAccessCertificate != null) +
" | UUID certificate supported? " + recipient.isUuidSupported());
if (theirUnidentifiedAccessKey != null &&
ourUnidentifiedAccessKey != null &&
@ -83,7 +85,8 @@ public class UnidentifiedAccessUtil {
try {
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
byte[] ourUnidentifiedAccessCertificate = Recipient.self().isUuidSupported() ? TextSecurePreferences.getUnidentifiedAccessCertificate(context)
: TextSecurePreferences.getUnidentifiedAccessCertificateLegacy(context);
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
ourUnidentifiedAccessKey = Util.getSecretBytes(16);

View File

@ -5,12 +5,10 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.IdentityUtil;

View File

@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SessionDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -13,8 +13,10 @@ import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.protocol.CiphertextMessage;
import org.whispersystems.libsignal.state.SessionRecord;
import org.whispersystems.libsignal.state.SessionStore;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.List;
import java.util.logging.Logger;
public class TextSecureSessionStore implements SessionStore {
@ -54,12 +56,16 @@ public class TextSecureSessionStore implements SessionStore {
@Override
public boolean containsSession(SignalProtocolAddress address) {
synchronized (FILE_LOCK) {
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
return sessionRecord != null &&
sessionRecord.getSessionState().hasSenderChain() &&
sessionRecord.getSessionState().getSessionVersion() == CiphertextMessage.CURRENT_VERSION;
return sessionRecord != null &&
sessionRecord.getSessionState().hasSenderChain() &&
sessionRecord.getSessionState().getSessionVersion() == CiphertextMessage.CURRENT_VERSION;
} else {
return false;
}
}
}
@ -95,7 +101,7 @@ public class TextSecureSessionStore implements SessionStore {
for (SessionDatabase.SessionRow row : sessions) {
if (row.getDeviceId() != address.getDeviceId()) {
row.getRecord().archiveCurrentState();
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireAddress().serialize(), row.getDeviceId()), row.getRecord());
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireServiceId(), row.getDeviceId()), row.getRecord());
}
}
}
@ -107,7 +113,7 @@ public class TextSecureSessionStore implements SessionStore {
for (SessionDatabase.SessionRow row : sessions) {
row.getRecord().archiveCurrentState();
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireAddress().serialize(), row.getDeviceId()), row.getRecord());
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireServiceId(), row.getDeviceId()), row.getRecord());
}
}
}

View File

@ -1,112 +0,0 @@
package org.thoughtcrime.securesms.database;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
public class Address implements Parcelable, Comparable<Address> {
public static final Parcelable.Creator<Address> CREATOR = new Parcelable.Creator<Address>() {
public Address createFromParcel(Parcel in) {
return new Address(in);
}
public Address[] newArray(int size) {
return new Address[size];
}
};
public static final Address UNKNOWN = new Address("Unknown");
private static final String TAG = Address.class.getSimpleName();
private final String address;
private Address(@NonNull String address) {
if (address == null) throw new AssertionError(address);
this.address = address;
}
public Address(Parcel in) {
this(in.readString());
}
public static @NonNull Address fromSerialized(@NonNull String serialized) {
return new Address(serialized);
}
public boolean isGroup() {
return GroupUtil.isEncodedGroup(address);
}
public boolean isMmsGroup() {
return GroupUtil.isMmsGroup(address);
}
public boolean isEmail() {
return NumberUtil.isValidEmail(address);
}
public boolean isPhone() {
return !isGroup() && !isEmail();
}
public @NonNull String toGroupString() {
if (!isGroup()) throw new AssertionError("Not group");
return address;
}
public @NonNull String toPhoneString() {
if (!isPhone()) {
if (isEmail()) throw new AssertionError("Not e164, is email");
if (isGroup()) throw new AssertionError("Not e164, is group");
throw new AssertionError("Not e164, unknown");
}
return address;
}
public @NonNull String toEmailString() {
if (!isEmail()) throw new AssertionError("Not email");
return address;
}
@Override
public @NonNull String toString() {
return address;
}
public String serialize() {
return address;
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || !(other instanceof Address)) return false;
return address.equals(((Address) other).address);
}
@Override
public int hashCode() {
return address.hashCode();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(address);
}
@Override
public int compareTo(@NonNull Address other) {
return address.compareTo(other.address);
}
}

View File

@ -1014,8 +1014,8 @@ public class MmsDatabase extends MessagingDatabase {
long messageId = insertMediaMessage(message.getBody(), message.getAttachments(), quoteAttachments, message.getSharedContacts(), message.getLinkPreviews(), contentValues, insertListener);
if (message.getRecipient().requireAddress().isGroup()) {
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireAddress().toGroupString(), false);
if (message.getRecipient().isGroup()) {
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireGroupId(), false);
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
receiptDatabase.insert(Stream.of(members).map(Recipient::getId).toList(),

View File

@ -12,6 +12,8 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.util.Util;
import java.io.IOException;
@ -23,7 +25,8 @@ public class PushDatabase extends Database {
private static final String TABLE_NAME = "push";
public static final String ID = "_id";
public static final String TYPE = "type";
public static final String SOURCE = "source";
public static final String SOURCE_E164 = "source";
public static final String SOURCE_UUID = "source_uuid";
public static final String DEVICE_ID = "device_id";
public static final String LEGACY_MSG = "body";
public static final String CONTENT = "content";
@ -32,7 +35,7 @@ public class PushDatabase extends Database {
public static final String SERVER_GUID = "server_guid";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER, " +
TYPE + " INTEGER, " + SOURCE_E164 + " TEXT, " + SOURCE_UUID + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER, " +
SERVER_TIMESTAMP + " INTEGER DEFAULT 0, " + SERVER_GUID + " TEXT DEFAULT NULL);";
public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
@ -47,7 +50,8 @@ public class PushDatabase extends Database {
} else {
ContentValues values = new ContentValues();
values.put(TYPE, envelope.getType());
values.put(SOURCE, envelope.getSource());
values.put(SOURCE_UUID, envelope.getSourceUuid().orNull());
values.put(SOURCE_E164, envelope.getSourceE164().orNull());
values.put(DEVICE_ID, envelope.getSourceDevice());
values.put(LEGACY_MSG, envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "");
values.put(CONTENT, envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "");
@ -68,11 +72,14 @@ public class PushDatabase extends Database {
null, null, null);
if (cursor != null && cursor.moveToNext()) {
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
String uuid = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_UUID));
String e164 = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_E164));
SignalServiceAddress address = new SignalServiceAddress(UuidUtil.parseOrNull(uuid), e164);
return new SignalServiceEnvelope(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)),
cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)),
address,
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)),
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
Util.isEmpty(legacyMessage) ? null : Base64.decode(legacyMessage),
@ -105,27 +112,30 @@ public class PushDatabase extends Database {
private Optional<Long> find(SignalServiceEnvelope envelope) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
String query = TYPE + " = ? AND " +
DEVICE_ID + " = ? AND " +
LEGACY_MSG + " = ? AND " +
CONTENT + " = ? AND " +
TIMESTAMP + " = ? AND " +
"(" +
"(" + SOURCE_E164 + " NOT NULL AND " + SOURCE_E164 + " = ?) OR " +
"(" + SOURCE_UUID + " NOT NULL AND " + SOURCE_UUID + " = ?)" +
")";
String[] args = new String[] { String.valueOf(envelope.getType()),
String.valueOf(envelope.getSourceDevice()),
envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "",
envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "",
String.valueOf(envelope.getTimestamp()),
String.valueOf(envelope.getSourceUuid().orNull()),
String.valueOf(envelope.getSourceE164().orNull()) };
try {
cursor = database.query(TABLE_NAME, null, TYPE + " = ? AND " + SOURCE + " = ? AND " +
DEVICE_ID + " = ? AND " + LEGACY_MSG + " = ? AND " +
CONTENT + " = ? AND " + TIMESTAMP + " = ?" ,
new String[] {String.valueOf(envelope.getType()),
envelope.getSource(),
String.valueOf(envelope.getSourceDevice()),
envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "",
envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "",
String.valueOf(envelope.getTimestamp())},
null, null, null);
try (Cursor cursor = database.query(TABLE_NAME, null, query, args, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return Optional.of(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
} else {
return Optional.absent();
}
} finally {
if (cursor != null) cursor.close();
}
}
@ -142,7 +152,8 @@ public class PushDatabase extends Database {
return null;
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE));
String sourceUuid = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_UUID));
String sourceE164 = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_E164));
int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID));
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
@ -150,10 +161,14 @@ public class PushDatabase extends Database {
long serverTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP));
String serverGuid = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID));
return new SignalServiceEnvelope(type, source, deviceId, timestamp,
return new SignalServiceEnvelope(type,
new SignalServiceAddress(UuidUtil.parseOrNull(sourceUuid), sourceE164),
deviceId,
timestamp,
legacyMessage != null ? Base64.decode(legacyMessage) : null,
content != null ? Base64.decode(content) : null,
serverTimestamp, serverGuid);
serverTimestamp,
serverGuid);
} catch (IOException e) {
throw new AssertionError(e);
}

View File

@ -21,15 +21,18 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.Closeable;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class RecipientDatabase extends Database {
@ -64,6 +67,7 @@ public class RecipientDatabase extends Database {
private static final String PROFILE_SHARING = "profile_sharing";
private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode";
private static final String FORCE_SMS_SELECTION = "force_sms_selection";
private static final String UUID_SUPPORTED = "uuid_supported";
private static final String SORT_NAME = "sort_name";
@ -73,23 +77,14 @@ public class RecipientDatabase extends Database {
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI,
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL,
UNIDENTIFIED_ACCESS_MODE,
FORCE_SMS_SELECTION,
FORCE_SMS_SELECTION, UUID_SUPPORTED
};
private static final String[] ID_PROJECTION = new String[] { ID };
public static final String[] SEARCH_PROJECTION = new String[] { ID, SYSTEM_DISPLAY_NAME, SIGNAL_PROFILE_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, "IFNULL(" + SYSTEM_DISPLAY_NAME + ", " + SIGNAL_PROFILE_NAME + ") AS " + SORT_NAME };
private static Address addressFromCursor(Cursor cursor) {
String phone = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL));
String groupId = cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID));
return phone != null ? Address.fromSerialized(phone) : email != null ? Address.fromSerialized(email) : Address.fromSerialized(groupId);
}
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
.map(columnName -> TABLE_NAME + "." + columnName)
.toList();
private static final String[] ID_PROJECTION = new String[]{ID };
public static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_DISPLAY_NAME, SIGNAL_PROFILE_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, "IFNULL(" + SYSTEM_DISPLAY_NAME + ", " + SIGNAL_PROFILE_NAME + ") AS " + SORT_NAME};
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
.map(columnName -> TABLE_NAME + "." + columnName)
.toList();
public enum VibrateState {
DEFAULT(0), ENABLED(1), DISABLED(2);
@ -173,12 +168,23 @@ public class RecipientDatabase extends Database {
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " +
FORCE_SMS_SELECTION + " INTEGER DEFAULT 0);";
FORCE_SMS_SELECTION + " INTEGER DEFAULT 0, " +
UUID_SUPPORTED + " INTEGER DEFAULT 0);";
public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
}
public @NonNull boolean containsPhoneOrUuid(@NonNull String id) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = UUID + " = ? OR " + PHONE + " = ?";
String[] args = new String[]{id, id};
try (Cursor cursor = db.query(TABLE_NAME, new String[] { ID }, query, args, null, null, null)) {
return cursor != null && cursor.moveToFirst();
}
}
public @NonNull Optional<RecipientId> getByE164(@NonNull String e164) {
return getByColumn(PHONE, e164);
}
@ -189,6 +195,15 @@ public class RecipientDatabase extends Database {
public @NonNull Optional<RecipientId> getByGroupId(@NonNull String groupId) {
return getByColumn(GROUP_ID, groupId);
}
public @NonNull Optional<RecipientId> getByUuid(@NonNull UUID uuid) {
return getByColumn(UUID, uuid.toString());
}
public @NonNull RecipientId getOrInsertFromUuid(@NonNull UUID uuid) {
return getOrInsertByColumn(UUID, uuid.toString());
}
public @NonNull RecipientId getOrInsertFromE164(@NonNull String e164) {
@ -238,7 +253,10 @@ public class RecipientDatabase extends Database {
@NonNull RecipientSettings getRecipientSettings(@NonNull Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
Address address = addressFromCursor(cursor);
UUID uuid = UuidUtil.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(UUID)));
String e164 = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL));
String groupId = cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID));
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCKED)) == 1;
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(MESSAGE_RINGTONE));
String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE));
@ -261,6 +279,7 @@ public class RecipientDatabase extends Database {
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE));
boolean forceSmsSelection = cursor.getInt(cursor.getColumnIndexOrThrow(FORCE_SMS_SELECTION)) == 1;
boolean uuidSupported = cursor.getInt(cursor.getColumnIndexOrThrow(UUID_SUPPORTED)) == 1;
MaterialColor color;
byte[] profileKey = null;
@ -281,7 +300,7 @@ public class RecipientDatabase extends Database {
}
}
return new RecipientSettings(RecipientId.from(id), address, blocked, muteUntil,
return new RecipientSettings(RecipientId.from(id), uuid, e164, email, groupId, blocked, muteUntil,
VibrateState.fromId(messageVibrateState),
VibrateState.fromId(callVibrateState),
Util.uri(messageRingtone), Util.uri(callRingtone),
@ -292,7 +311,7 @@ public class RecipientDatabase extends Database {
systemPhoneLabel, systemContactUri,
signalProfileName, signalProfileAvatar, profileSharing,
notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode),
forceSmsSelection);
forceSmsSelection, uuidSupported);
}
public BulkOperationsHandle resetAllSystemContactInfo() {
@ -394,6 +413,13 @@ public class RecipientDatabase extends Database {
Recipient.live(id).refresh();
}
public void setUuidSupported(@NonNull RecipientId id, boolean supported) {
ContentValues values = new ContentValues(1);
values.put(UUID_SUPPORTED, supported ? "1" : "0");
update(id, values);
Recipient.live(id).refresh();
}
public void setProfileKey(@NonNull RecipientId id, @Nullable byte[] profileKey) {
ContentValues values = new ContentValues(1);
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
@ -429,19 +455,84 @@ public class RecipientDatabase extends Database {
Recipient.live(id).refresh();
}
public Set<Address> getAllAddresses() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Set<Address> results = new HashSet<>();
public void setPhoneNumber(@NonNull RecipientId id, @NonNull String e164) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(PHONE, e164);
update(id, contentValues);
Recipient.live(id).refresh();
}
try (Cursor cursor = db.query(TABLE_NAME, new String[] { ID, UUID, PHONE, EMAIL, GROUP_ID }, null, null, null, null, null)) {
public Set<String> getAllPhoneNumbers() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Set<String> results = new HashSet<>();
try (Cursor cursor = db.query(TABLE_NAME, new String[] { PHONE }, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
results.add(addressFromCursor(cursor));
String number = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
if (!TextUtils.isEmpty(number)) {
results.add(number);
}
}
}
return results;
}
public void markRegistered(@NonNull RecipientId id, @NonNull UUID uuid) {
ContentValues contentValues = new ContentValues(2);
contentValues.put(REGISTERED, RegisteredState.REGISTERED.getId());
contentValues.put(UUID, uuid.toString().toLowerCase());
update(id, contentValues);
Recipient.live(id).refresh();
}
/**
* Marks the user as registered without providing a UUID. This should only be used when one
* cannot be reasonably obtained. {@link #markRegistered(RecipientId, UUID)} should be strongly
* preferred.
*/
public void markRegistered(@NonNull RecipientId id) {
ContentValues contentValues = new ContentValues(2);
contentValues.put(REGISTERED, RegisteredState.REGISTERED.getId());
update(id, contentValues);
Recipient.live(id).refresh();
}
public void markUnregistered(@NonNull RecipientId id) {
ContentValues contentValues = new ContentValues(2);
contentValues.put(REGISTERED, RegisteredState.NOT_REGISTERED.getId());
contentValues.put(UUID, (String) null);
update(id, contentValues);
Recipient.live(id).refresh();
}
public void bulkUpdatedRegisteredStatus(@NonNull Map<RecipientId, String> registered, Collection<RecipientId> unregistered) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.beginTransaction();
try {
for (Map.Entry<RecipientId, String> entry : registered.entrySet()) {
ContentValues values = new ContentValues(2);
values.put(REGISTERED, RegisteredState.REGISTERED.getId());
values.put(UUID, entry.getValue().toLowerCase());
db.update(TABLE_NAME, values, ID_WHERE, new String[] { entry.getKey().serialize() });
}
for (RecipientId id : unregistered) {
ContentValues values = new ContentValues(1);
values.put(REGISTERED, RegisteredState.NOT_REGISTERED.getId());
values.put(UUID, (String) null);
db.update(TABLE_NAME, values, ID_WHERE, new String[] { id.serialize() });
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
@Deprecated
public void setRegistered(@NonNull RecipientId id, RegisteredState registeredState) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(REGISTERED, registeredState.getId());
@ -449,8 +540,9 @@ public class RecipientDatabase extends Database {
Recipient.live(id).refresh();
}
public void setRegistered(@NonNull List<RecipientId> activeIds,
@NonNull List<RecipientId> inactiveIds)
@Deprecated
public void setRegistered(@NonNull Collection<RecipientId> activeIds,
@NonNull Collection<RecipientId> inactiveIds)
{
for (RecipientId activeId : activeIds) {
ContentValues contentValues = new ContentValues(1);
@ -673,7 +765,10 @@ public class RecipientDatabase extends Database {
public static class RecipientSettings {
private final RecipientId id;
private final Address address;
private final UUID uuid;
private final String e164;
private final String email;
private final String groupId;
private final boolean blocked;
private final long muteUntil;
private final VibrateState messageVibrateState;
@ -696,9 +791,14 @@ public class RecipientDatabase extends Database {
private final String notificationChannel;
private final UnidentifiedAccessMode unidentifiedAccessMode;
private final boolean forceSmsSelection;
private final boolean uuidSupported;
RecipientSettings(@NonNull RecipientId id,
@NonNull Address address, boolean blocked, long muteUntil,
@Nullable UUID uuid,
@Nullable String e164,
@Nullable String email,
@Nullable String groupId,
boolean blocked, long muteUntil,
@NonNull VibrateState messageVibrateState,
@NonNull VibrateState callVibrateState,
@Nullable Uri messageRingtone,
@ -718,10 +818,14 @@ public class RecipientDatabase extends Database {
boolean profileSharing,
@Nullable String notificationChannel,
@NonNull UnidentifiedAccessMode unidentifiedAccessMode,
boolean forceSmsSelection)
boolean forceSmsSelection,
boolean uuidSupported)
{
this.id = id;
this.address = address;
this.uuid = uuid;
this.e164 = e164;
this.email = email;
this.groupId = groupId;
this.blocked = blocked;
this.muteUntil = muteUntil;
this.messageVibrateState = messageVibrateState;
@ -744,14 +848,27 @@ public class RecipientDatabase extends Database {
this.notificationChannel = notificationChannel;
this.unidentifiedAccessMode = unidentifiedAccessMode;
this.forceSmsSelection = forceSmsSelection;
this.uuidSupported = uuidSupported;
}
public RecipientId getId() {
return id;
}
public @NonNull Address getAddress() {
return address;
public @Nullable UUID getUuid() {
return uuid;
}
public @Nullable String getE164() {
return e164;
}
public @Nullable String getEmail() {
return email;
}
public @Nullable String getGroupId() {
return groupId;
}
public @Nullable MaterialColor getColor() {
@ -841,6 +958,10 @@ public class RecipientDatabase extends Database {
public boolean isForceSmsSelection() {
return forceSmsSelection;
}
public boolean isUuidSupported() {
return uuidSupported;
}
}
public static class RecipientReader implements Closeable {

View File

@ -570,7 +570,7 @@ public class SmsDatabase extends MessagingDatabase {
if (message.getGroupId() == null) {
groupRecipient = null;
} else {
RecipientId id = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(message.getGroupId().serialize());
RecipientId id = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(message.getGroupId());
groupRecipient = Recipient.resolved(id);
}

View File

@ -28,10 +28,8 @@ import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteStatement;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.HashSet;
import java.util.List;

View File

@ -8,7 +8,6 @@ import androidx.annotation.NonNull;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;

View File

@ -25,7 +25,6 @@ import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase;
@ -39,8 +38,10 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
@ -1269,10 +1270,10 @@ public class ClassicOpenHelper extends SQLiteOpenHelper {
if (Permissions.hasAny(context, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) {
try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids")));
String address = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
if (address.isPhone() && !TextUtils.isEmpty(address.toPhoneString())) {
Uri lookup = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString()));
if (!TextUtils.isEmpty(address) && !GroupUtil.isEncodedGroup(address) && !NumberUtil.isValidEmail(address)) {
Uri lookup = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address));
try (Cursor contactCursor = context.getContentResolver().query(lookup, new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME,
ContactsContract.PhoneLookup.LOOKUP_KEY,
@ -1288,7 +1289,7 @@ public class ClassicOpenHelper extends SQLiteOpenHelper {
contentValues.put("system_phone_label", contactCursor.getString(4));
contentValues.put("system_contact_uri", ContactsContract.Contacts.getLookupUri(contactCursor.getLong(2), contactCursor.getString(1)).toString());
db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address.toPhoneString()});
db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address});
}
}
}

View File

@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.File;
import java.util.List;
import java.util.UUID;
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
@ -90,12 +91,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int ATTACHMENT_TRANSFORM_PROPERTIES = 32;
private static final int ATTACHMENT_CLEAR_HASHES = 33;
private static final int ATTACHMENT_CLEAR_HASHES_2 = 34;
private static final int UUIDS = 35;
private static final int DATABASE_VERSION = 34;
private static final int DATABASE_VERSION = 35;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
private final DatabaseSecret databaseSecret;
public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) {
@ -552,15 +553,15 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
for (NotificationChannel oldChannel : channels) {
notificationManager.deleteNotificationChannel(oldChannel.getId());
int startIndex = "contact_".length();
int endIndex = oldChannel.getId().lastIndexOf("_");
String address = oldChannel.getId().substring(startIndex, endIndex);
int startIndex = "contact_".length();
int endIndex = oldChannel.getId().lastIndexOf("_");
String address = oldChannel.getId().substring(startIndex, endIndex);
String recipientId;
try (Cursor cursor = db.query("recipient", new String[] { "_id" }, "phone = ? OR email = ? OR group_id = ?", new String[] { address, address, address}, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
recipientId = cursor.getString(0);
recipientId = cursor.getString(cursor.getColumnIndexOrThrow("_id"));
} else {
Log.w(TAG, "Couldn't find recipient for address: " + address);
continue;
@ -614,6 +615,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
Glide.get(context).clearDiskCache();
}
if (oldVersion < UUIDS) {
db.execSQL("ALTER TABLE recipient ADD COLUMN uuid_supported INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE push ADD COLUMN source_uuid TEXT DEFAULT NULL");
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View File

@ -7,7 +7,6 @@ import org.thoughtcrime.securesms.logging.Log;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.SessionDatabase;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.libsignal.state.SessionRecord;
@ -41,7 +40,7 @@ class SessionStoreMigrationHelper {
for (File sessionFile : sessionFiles) {
try {
String[] parts = sessionFile.getName().split("[.]");
Address address = Address.fromSerialized(parts[0]);
String address = parts[0];
int deviceId;
@ -79,7 +78,7 @@ class SessionStoreMigrationHelper {
ContentValues contentValues = new ContentValues();
contentValues.put(SessionDatabase.RECIPIENT_ID, address.serialize());
contentValues.put(SessionDatabase.RECIPIENT_ID, address);
contentValues.put(SessionDatabase.DEVICE, deviceId);
contentValues.put(SessionDatabase.RECORD, sessionRecord.serialize());

View File

@ -10,7 +10,6 @@ import androidx.loader.content.AsyncTaskLoader;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;

View File

@ -6,10 +6,8 @@ import android.database.MatrixCursor;
import android.database.MergeCursor;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.AbstractCursorLoader;

View File

@ -5,7 +5,6 @@ import android.content.Context;
import android.database.Cursor;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;

View File

@ -4,9 +4,7 @@ package org.thoughtcrime.securesms.database.model;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
public class Quote {

View File

@ -34,6 +34,8 @@ import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.api.websocket.ConnectivityListener;
import java.util.UUID;
/**
* Implementation of {@link ApplicationDependencies.Provider} that provides real app dependencies.
*/
@ -120,7 +122,12 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
}
@Override
public String getUser() {
public UUID getUuid() {
return TextSecurePreferences.getLocalUuid(context);
}
@Override
public String getE164() {
return TextSecurePreferences.getLocalNumber(context);
}

View File

@ -118,6 +118,6 @@ public class WebRtcViewModel {
}
public @NonNull String toString() {
return "[State: " + state + ", recipient: " + recipient.requireAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localCameraState.isEnabled() + "]";
return "[State: " + state + ", recipient: " + recipient.getId().serialize() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localCameraState.isEnabled() + "]";
}
}

View File

@ -6,7 +6,6 @@ import android.graphics.Bitmap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.signalservice.api.util.InvalidNumberException;

View File

@ -5,6 +5,7 @@ import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.ApplicationContext;
@ -22,6 +23,7 @@ import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.Base64;
@ -32,6 +34,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.Collections;
import java.util.HashSet;
@ -90,8 +93,8 @@ public class GroupMessageProcessor {
List<RecipientId> members = new LinkedList<>();
if (group.getMembers().isPresent()) {
for (String member : group.getMembers().get()) {
members.add(Recipient.external(context, member).getId());
for (SignalServiceAddress member : group.getMembers().get()) {
members.add(Recipient.externalPush(context, member).getId());
}
}
@ -114,8 +117,8 @@ public class GroupMessageProcessor {
Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<RecipientId> messageMembers = new HashSet<>();
for (String messageMember : group.getMembers().get()) {
messageMembers.add(Recipient.external(context, messageMember).getId());
for (SignalServiceAddress messageMember : group.getMembers().get()) {
messageMembers.add(Recipient.externalPush(context, messageMember).getId());
}
Set<RecipientId> addedMembers = new HashSet<>(messageMembers);
@ -135,7 +138,13 @@ public class GroupMessageProcessor {
builder.clearMembers();
for (RecipientId addedMember : addedMembers) {
builder.addMembers(Recipient.resolved(addedMember).requireAddress().serialize());
Recipient recipient = Recipient.resolved(addedMember);
if (recipient.getE164().isPresent()) {
builder.addMembersE164(recipient.getE164().get());
}
builder.addMembers(createMember(RecipientUtil.toSignalServiceAddress(context, recipient)));
}
} else {
builder.clearMembers();
@ -164,7 +173,7 @@ public class GroupMessageProcessor {
@NonNull SignalServiceGroup group,
@NonNull GroupRecord record)
{
Recipient sender = Recipient.external(context, content.getSender());
Recipient sender = Recipient.externalPush(context, content.getSender());
if (record.getMembers().contains(sender.getId())) {
ApplicationDependencies.getJobManager().add(new PushGroupUpdateJob(sender.getId(), group.getGroupId()));
@ -186,8 +195,8 @@ public class GroupMessageProcessor {
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.QUIT);
if (members.contains(Recipient.external(context, content.getSender()).getId())) {
database.remove(id, Recipient.external(context, content.getSender()).getId());
if (members.contains(Recipient.externalPush(context, content.getSender()).getId())) {
database.remove(id, Recipient.externalPush(context, content.getSender()).getId());
if (outgoing) database.setActive(id, false);
return storeMessage(context, content, group, builder.build(), outgoing);
@ -223,7 +232,7 @@ public class GroupMessageProcessor {
} else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.external(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
@ -258,10 +267,29 @@ public class GroupMessageProcessor {
}
if (group.getMembers().isPresent()) {
builder.addAllMembers(group.getMembers().get());
builder.addAllMembersE164(Stream.of(group.getMembers().get())
.filter(a -> a.getNumber().isPresent())
.map(a -> a.getNumber().get())
.toList());
builder.addAllMembers(Stream.of(group.getMembers().get())
.map(GroupMessageProcessor::createMember)
.toList());
}
return builder;
}
public static GroupContext.Member createMember(SignalServiceAddress address) {
GroupContext.Member.Builder member = GroupContext.Member.newBuilder();
if (address.getUuid().isPresent()) {
member = member.setUuid(address.getUuid().get().toString());
}
if (address.getNumber().isPresent()) {
member = member.setE164(address.getNumber().get());
}
return member.build();
}
}

View File

@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
@ -48,7 +49,7 @@ final class V1GroupManager {
final RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
final Recipient groupRecipient = Recipient.resolved(groupRecipientId);
memberIds.add(Recipient.external(context, TextSecurePreferences.getLocalNumber(context)).getId());
memberIds.add(Recipient.self().getId());
groupDatabase.create(groupId, name, new LinkedList<>(memberIds), null, null);
if (!mms) {
@ -71,7 +72,7 @@ final class V1GroupManager {
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
memberAddresses.add(Recipient.external(context, TextSecurePreferences.getLocalNumber(context)).getId());
memberAddresses.add(Recipient.self().getId());
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
groupDatabase.updateTitle(groupId, name);
groupDatabase.updateAvatar(groupId, avatarBytes);
@ -97,16 +98,19 @@ final class V1GroupManager {
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
List<String> numbers = new LinkedList<>();
List<GroupContext.Member> uuidMembers = new LinkedList<>();
List<String> e164Members = new LinkedList<>();
for (RecipientId member : members) {
numbers.add(Recipient.resolved(member).requireAddress().serialize());
Recipient recipient = Recipient.resolved(member);
uuidMembers.add(GroupMessageProcessor.createMember(RecipientUtil.toSignalServiceAddress(context, recipient)));
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
.setType(GroupContext.Type.UPDATE)
.addAllMembers(numbers);
.addAllMembersE164(e164Members)
.addAllMembers(uuidMembers);
if (groupName != null) groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build();

View File

@ -69,7 +69,7 @@ public class DirectoryRefreshJob extends BaseJob {
if (recipient == null) {
DirectoryHelper.refreshDirectory(context, notifyOfNewUsers);
} else {
DirectoryHelper.refreshDirectoryFor(context, recipient);
DirectoryHelper.refreshDirectoryFor(context, recipient, notifyOfNewUsers);
}
}

View File

@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob;
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob;
import org.thoughtcrime.securesms.migrations.UuidMigrationJob;
import java.util.Arrays;
import java.util.HashMap;
@ -88,6 +89,7 @@ public final class JobManagerFactories {
put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory());
put(MigrationCompleteJob.KEY, new MigrationCompleteJob.Factory());
put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory());
put(UuidMigrationJob.KEY, new UuidMigrationJob.Factory());
// Dead jobs
put("PushContentReceiveJob", new FailingJob.Factory());

View File

@ -4,7 +4,6 @@ import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import com.google.android.mms.pdu_alt.CharacterSets;
import com.google.android.mms.pdu_alt.EncodedStringValue;
import com.google.android.mms.pdu_alt.PduBody;
@ -13,8 +12,6 @@ import com.google.android.mms.pdu_alt.RetrieveConf;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
@ -35,10 +32,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.DuplicateMessageException;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.LegacyMessageException;
import org.whispersystems.libsignal.NoSessionException;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;

View File

@ -14,7 +14,6 @@ import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.android.mms.pdu_alt.PduParser;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;

View File

@ -24,7 +24,6 @@ import com.klinker.android.send_message.Utils;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
@ -221,7 +220,6 @@ public final class MmsSendJob extends SendJob {
{
SendReq req = new SendReq();
String lineNumber = getMyNumber(context);
Address destination = message.getRecipient().requireAddress();
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
List<Attachment> scaledAttachments = message.getAttachments();
@ -231,18 +229,18 @@ public final class MmsSendJob extends SendJob {
req.setFrom(new EncodedStringValue(TextSecurePreferences.getLocalNumber(context)));
}
if (destination.isMmsGroup()) {
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(destination.toGroupString(), false);
if (message.getRecipient().isMmsGroup()) {
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireGroupId(), false);
for (Recipient member : members) {
if (message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST) {
req.addBcc(new EncodedStringValue(member.requireAddress().serialize()));
req.addBcc(new EncodedStringValue(member.requireSmsAddress()));
} else {
req.addTo(new EncodedStringValue(member.requireAddress().serialize()));
req.addTo(new EncodedStringValue(member.requireSmsAddress()));
}
}
} else {
req.addTo(new EncodedStringValue(destination.serialize()));
req.addTo(new EncodedStringValue(message.getRecipient().requireSmsAddress()));
}
req.setDate(System.currentTimeMillis() / 1000);

View File

@ -12,12 +12,14 @@ import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
@ -67,16 +69,16 @@ public class MultiDeviceBlockedUpdateJob extends BaseJob {
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) {
List<String> blockedIndividuals = new LinkedList<>();
List<byte[]> blockedGroups = new LinkedList<>();
List<SignalServiceAddress> blockedIndividuals = new LinkedList<>();
List<byte[]> blockedGroups = new LinkedList<>();
Recipient recipient;
while ((recipient = reader.getNext()) != null) {
if (recipient.isGroup()) {
blockedGroups.add(GroupUtil.getDecodedId(recipient.requireAddress().toGroupString()));
blockedGroups.add(GroupUtil.getDecodedId(recipient.requireGroupId()));
} else {
blockedIndividuals.add(recipient.requireAddress().serialize());
blockedIndividuals.add(RecipientUtil.toSignalServiceAddress(context, recipient));
}
}

View File

@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.jobs;
import android.Manifest;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
@ -14,7 +13,6 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@ -25,6 +23,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.guava.Optional;
@ -37,6 +36,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
@ -129,7 +129,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId());
Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord);
out.write(new DeviceContact(recipient.requireAddress().toPhoneString(),
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, recipient),
Optional.fromNullable(recipient.getName()),
getAvatar(recipient.getContactUri()),
Optional.fromNullable(recipient.getColor().serialize()),
@ -189,12 +189,12 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
boolean blocked = recipient.isBlocked();
Optional<Integer> expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
out.write(new DeviceContact(recipient.requireAddress().toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, recipient), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
}
if (ProfileKeyUtil.hasProfileKey(context)) {
Recipient self = Recipient.self();
out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context),
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, Recipient.self()),
Optional.absent(), Optional.absent(),
Optional.of(self.getColor().serialize()), Optional.absent(),
Optional.of(ProfileKeyUtil.getProfileKey(context)),
@ -300,8 +300,8 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
private Optional<VerifiedMessage> getVerifiedMessage(Recipient recipient, Optional<IdentityDatabase.IdentityRecord> identity) throws InvalidNumberException {
if (!identity.isPresent()) return Optional.absent();
String destination = recipient.requireAddress().toPhoneString();
IdentityKey identityKey = identity.get().getIdentityKey();
SignalServiceAddress destination = RecipientUtil.toSignalServiceAddress(context, recipient);
IdentityKey identityKey = identity.get().getIdentityKey();
VerifiedMessage.VerifiedState state;

View File

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
@ -23,6 +24,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStre
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.ByteArrayInputStream;
@ -82,10 +84,10 @@ public class MultiDeviceGroupUpdateJob extends BaseJob {
while ((record = reader.getNext()) != null) {
if (!record.isMms()) {
List<String> members = new LinkedList<>();
List<SignalServiceAddress> members = new LinkedList<>();
for (RecipientId member : record.getMembers()) {
members.add(Recipient.resolved(member).requireAddress().serialize());
members.add(RecipientUtil.toSignalServiceAddress(context, Recipient.resolved(member)));
}
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(record.getId(), record.isMms()));

View File

@ -11,6 +11,8 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -68,7 +70,7 @@ public class MultiDeviceProfileKeyUpdateJob extends BaseJob {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeviceContactsOutputStream out = new DeviceContactsOutputStream(baos);
out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context),
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, Recipient.self()),
Optional.absent(),
Optional.absent(),
Optional.absent(),

View File

@ -15,12 +15,14 @@ import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
@ -89,7 +91,7 @@ public class MultiDeviceReadUpdateJob extends BaseJob {
for (SerializableSyncMessageId messageId : messageIds) {
Recipient recipient = Recipient.resolved(RecipientId.from(messageId.recipientId));
readMessages.add(new ReadMessage(recipient.requireAddress().serialize(), messageId.timestamp));
readMessages.add(new ReadMessage(RecipientUtil.toSignalServiceAddress(context, recipient), messageId.timestamp));
}
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();

View File

@ -10,9 +10,9 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -22,6 +22,7 @@ import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
@ -99,9 +100,9 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob {
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
Recipient recipient = Recipient.resolved(destination);
Address canonicalDestination = recipient.requireAddress();
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
SignalServiceAddress verifiedAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
VerifiedMessage verifiedMessage = new VerifiedMessage(verifiedAddress, new IdentityKey(identityKey, 0), verifiedState, timestamp);
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage),
UnidentifiedAccessUtil.getAccessFor(context, recipient));

View File

@ -13,12 +13,15 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
@ -76,7 +79,7 @@ public class MultiDeviceViewOnceOpenJob extends BaseJob {
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
Recipient recipient = Recipient.resolved(RecipientId.from(messageId.recipientId));
ViewOnceOpenMessage openMessage = new ViewOnceOpenMessage(recipient.requireAddress().serialize(), messageId.timestamp);
ViewOnceOpenMessage openMessage = new ViewOnceOpenMessage(RecipientUtil.toSignalServiceAddress(context, recipient), messageId.timestamp);
messageSender.sendMessage(SignalServiceSyncMessage.forViewOnceOpen(openMessage), UnidentifiedAccessUtil.getAccessForSync(context));
}

View File

@ -14,7 +14,6 @@ import android.util.Pair;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import com.google.android.mms.APN;
import org.signal.libsignal.metadata.InvalidMetadataMessageException;
import org.signal.libsignal.metadata.InvalidMetadataVersionException;
@ -35,7 +34,6 @@ import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment;
import org.thoughtcrime.securesms.attachments.TombstoneAttachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
@ -226,7 +224,7 @@ public class PushDecryptJob extends BaseJob {
try {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
SignalServiceAddress localAddress = new SignalServiceAddress(Optional.of(TextSecurePreferences.getLocalUuid(context)), Optional.of(TextSecurePreferences.getLocalNumber(context)));
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, UnidentifiedAccessUtil.getCertificateValidator());
SignalServiceContent content = cipher.decrypt(envelope);
@ -290,7 +288,7 @@ public class PushDecryptJob extends BaseJob {
Log.w(TAG, "Got unrecognized message...");
}
resetRecipientToPush(Recipient.external(context, content.getSender()));
resetRecipientToPush(Recipient.externalPush(context, content.getSender()));
if (envelope.isPreKeySignalMessage()) {
ApplicationDependencies.getJobManager().add(new RefreshPreKeysJob());
@ -301,8 +299,7 @@ public class PushDecryptJob extends BaseJob {
} catch (ProtocolInvalidMessageException e) {
Log.w(TAG, e);
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
}
catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
} catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
Log.w(TAG, e);
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (StorageFailedException e) {
@ -340,7 +337,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_INCOMING_CALL);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp());
@ -356,7 +353,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_RESPONSE_MESSAGE);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
context.startService(intent);
@ -370,7 +367,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_ICE_MESSAGE);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP, message.getSdp());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_MID, message.getSdpMid());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_LINE_INDEX, message.getSdpMLineIndex());
@ -390,7 +387,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_REMOTE_HANGUP);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
context.startService(intent);
}
@ -402,7 +399,7 @@ public class PushDecryptJob extends BaseJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_REMOTE_BUSY);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
context.startService(intent);
}
@ -411,7 +408,7 @@ public class PushDecryptJob extends BaseJob {
@NonNull Optional<Long> smsMessageId)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.external(context, content.getSender()).getId(),
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(),
content.getSenderDevice(),
content.getTimestamp(),
"", Optional.absent(), 0,
@ -432,7 +429,7 @@ public class PushDecryptJob extends BaseJob {
if (threadId != null) {
SessionStore sessionStore = new TextSecureSessionStore(context);
sessionStore.deleteAllSessions(content.getSender());
sessionStore.deleteAllSessions(content.getSender().getIdentifier());
SecurityEvent.broadcastSecurityUpdateEvent(context);
MessageNotifier.updateNotification(context, threadId);
@ -450,7 +447,7 @@ public class PushDecryptJob extends BaseJob {
if (!recipient.isGroup()) {
SessionStore sessionStore = new TextSecureSessionStore(context);
sessionStore.deleteAllSessions(recipient.requireAddress().toPhoneString());
sessionStore.deleteAllSessions(recipient.requireServiceId());
SecurityEvent.broadcastSecurityUpdateEvent(context);
@ -482,7 +479,7 @@ public class PushDecryptJob extends BaseJob {
private void handleUnknownGroupMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceGroup group)
{
ApplicationDependencies.getJobManager().add(new RequestGroupInfoJob(Recipient.external(context, content.getSender()).getId(), group.getGroupId()));
ApplicationDependencies.getJobManager().add(new RequestGroupInfoJob(Recipient.externalPush(context, content.getSender()).getId(), group.getGroupId()));
}
private void handleExpirationUpdate(@NonNull SignalServiceContent content,
@ -514,7 +511,7 @@ public class PushDecryptJob extends BaseJob {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
}
} catch (MmsException e) {
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
throw new StorageFailedException(e, content.getSender().getIdentifier(), content.getSenderDevice());
}
}
@ -573,11 +570,7 @@ public class PushDecryptJob extends BaseJob {
}
if (message.getMessage().getProfileKey().isPresent()) {
Recipient recipient = null;
if (message.getDestination().isPresent()) recipient = Recipient.external(context, message.getDestination().get());
else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.external(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false));
Recipient recipient = getSyncMessageDestination(message);
if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getId(), true);
@ -591,7 +584,7 @@ public class PushDecryptJob extends BaseJob {
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
} catch (MmsException e) {
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
throw new StorageFailedException(e, content.getSender().getIdentifier(), content.getSenderDevice());
}
}
@ -622,8 +615,8 @@ public class PushDecryptJob extends BaseJob {
private void handleSynchronizeReadMessage(@NonNull List<ReadMessage> readMessages, long envelopeTimestamp)
{
for (ReadMessage readMessage : readMessages) {
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.external(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.external(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.externalPush(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.externalPush(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
for (Pair<Long, Long> expiringMessage : expiringText) {
ApplicationContext.getInstance(context)
@ -644,7 +637,7 @@ public class PushDecryptJob extends BaseJob {
}
private void handleSynchronizeViewOnceOpenMessage(@NonNull ViewOnceOpenMessage openMessage, long envelopeTimestamp) {
RecipientId author = Recipient.external(context, openMessage.getSender()).getId();
RecipientId author = Recipient.externalPush(context, openMessage.getSender()).getId();
long timestamp = openMessage.getTimestamp();
MessageRecord record = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(timestamp, author);
@ -674,7 +667,7 @@ public class PushDecryptJob extends BaseJob {
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Recipient.external(context, content.getSender()).getId(),
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Recipient.externalPush(context, content.getSender()).getId(),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, false,
message.isViewOnce(),
@ -707,7 +700,7 @@ public class PushDecryptJob extends BaseJob {
database.setTransactionSuccessful();
}
} catch (MmsException e) {
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
throw new StorageFailedException(e, content.getSender().getIdentifier(), content.getSenderDevice());
} finally {
database.endTransaction();
}
@ -778,12 +771,13 @@ public class PushDecryptJob extends BaseJob {
try {
long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null);
if (recipients.requireAddress().isGroup()) {
updateGroupReceiptStatus(message, messageId, recipients.requireAddress().toGroupString());
if (recipients.isGroup()) {
updateGroupReceiptStatus(message, messageId, recipients.requireGroupId());
} else {
database.markUnidentified(messageId, message.isUnidentified(recipients.requireServiceId()));
}
database.markAsSent(messageId, true);
database.markUnidentified(messageId, message.isUnidentified(recipients.requireAddress().serialize()));
List<DatabaseAttachment> allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId);
List<DatabaseAttachment> stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList();
@ -838,12 +832,12 @@ public class PushDecryptJob extends BaseJob {
return;
}
updateGroupReceiptStatus(message, record.getId(), recipient.requireAddress().toGroupString());
updateGroupReceiptStatus(message, record.getId(), recipient.requireGroupId());
}
private void updateGroupReceiptStatus(@NonNull SentTranscriptMessage message, long messageId, @NonNull String groupString) {
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
List<Recipient> messageRecipients = Stream.of(message.getRecipients()).map(address -> Recipient.external(context, address)).toList();
List<Recipient> messageRecipients = Stream.of(message.getRecipients()).map(address -> Recipient.externalPush(context, address)).toList();
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupString, false);
Map<RecipientId, Integer> localReceipts = Stream.of(receiptDatabase.getGroupReceiptInfo(messageId))
.collect(Collectors.toMap(GroupReceiptInfo::getRecipientId, GroupReceiptInfo::getStatus));
@ -858,7 +852,7 @@ public class PushDecryptJob extends BaseJob {
}
for (Recipient member : members) {
receiptDatabase.setUnidentified(member.getId(), messageId, message.isUnidentified(member.requireAddress().serialize()));
receiptDatabase.setUnidentified(member.getId(), messageId, message.isUnidentified(member.requireServiceId()));
}
}
@ -882,7 +876,7 @@ public class PushDecryptJob extends BaseJob {
} else {
notifyTypingStoppedFromIncomingMessage(recipient, content.getSender(), content.getSenderDevice());
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.external(context, content.getSender()).getId(),
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(),
content.getSenderDevice(),
message.getTimestamp(), body,
message.getGroupInfo(),
@ -915,7 +909,7 @@ public class PushDecryptJob extends BaseJob {
}
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
boolean isGroup = recipient.requireAddress().isGroup();
boolean isGroup = recipient.isGroup();
MessagingDatabase database;
long messageId;
@ -927,13 +921,13 @@ public class PushDecryptJob extends BaseJob {
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null);
database = DatabaseFactory.getMmsDatabase(context);
updateGroupReceiptStatus(message, messageId, recipient.requireAddress().toGroupString());
updateGroupReceiptStatus(message, messageId, recipient.requireGroupId());
} else {
OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis);
messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null);
database = DatabaseFactory.getSmsDatabase(context);
database.markUnidentified(messageId, message.isUnidentified(recipient.requireAddress().serialize()));
database.markUnidentified(messageId, message.isUnidentified(recipient.requireServiceId()));
}
database.markAsSent(messageId, true);
@ -1025,7 +1019,7 @@ public class PushDecryptJob extends BaseJob {
}
}
private void handleInvalidMessage(@NonNull String sender,
private void handleInvalidMessage(@NonNull SignalServiceAddress sender,
int senderDevice,
@NonNull Optional<SignalServiceGroup> group,
long timestamp,
@ -1034,7 +1028,7 @@ public class PushDecryptJob extends BaseJob {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp, group);
Optional<InsertResult> insertResult = insertPlaceholder(sender.getIdentifier(), senderDevice, timestamp, group);
if (insertResult.isPresent()) {
smsDatabase.markAsInvalidMessage(insertResult.get().getMessageId());
@ -1082,7 +1076,7 @@ public class PushDecryptJob extends BaseJob {
@NonNull SignalServiceDataMessage message)
{
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
Recipient recipient = Recipient.external(context, content.getSender());
Recipient recipient = Recipient.externalPush(context, content.getSender());
if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
database.setProfileKey(recipient.getId(), message.getProfileKey().get());
@ -1094,7 +1088,7 @@ public class PushDecryptJob extends BaseJob {
private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message)
{
ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(Recipient.external(context, content.getSender()).getId(), message.getTimestamp()));
ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(Recipient.externalPush(context, content.getSender()).getId(), message.getTimestamp()));
}
@SuppressLint("DefaultLocale")
@ -1104,7 +1098,7 @@ public class PushDecryptJob extends BaseJob {
for (long timestamp : message.getTimestamps()) {
Log.i(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp));
DatabaseFactory.getMmsSmsDatabase(context)
.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.external(context, content.getSender()).getId(), timestamp), System.currentTimeMillis());
.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.externalPush(context, content.getSender()).getId(), timestamp), System.currentTimeMillis());
}
}
@ -1117,7 +1111,7 @@ public class PushDecryptJob extends BaseJob {
Log.i(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp));
DatabaseFactory.getMmsSmsDatabase(context)
.incrementReadReceiptCount(new SyncMessageId(Recipient.external(context, content.getSender()).getId(), timestamp), content.getTimestamp());
.incrementReadReceiptCount(new SyncMessageId(Recipient.externalPush(context, content.getSender()).getId(), timestamp), content.getTimestamp());
}
}
}
@ -1129,7 +1123,7 @@ public class PushDecryptJob extends BaseJob {
return;
}
Recipient author = Recipient.external(context, content.getSender());
Recipient author = Recipient.externalPush(context, content.getSender());
long threadId;
@ -1184,7 +1178,7 @@ public class PushDecryptJob extends BaseJob {
return Optional.absent();
}
RecipientId author = Recipient.external(context, quote.get().getAuthor().getNumber()).getId();
RecipientId author = Recipient.externalPush(context, quote.get().getAuthor()).getId();
MessageRecord message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quote.get().getId(), author);
if (message != null) {
@ -1313,7 +1307,7 @@ public class PushDecryptJob extends BaseJob {
if (message.getMessage().getGroupInfo().isPresent()) {
return Recipient.external(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false));
} else {
return Recipient.external(context, message.getDestination().get());
return Recipient.externalPush(context, message.getDestination().get());
}
}
@ -1321,12 +1315,12 @@ public class PushDecryptJob extends BaseJob {
if (message.getGroupInfo().isPresent()) {
return Recipient.external(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false));
} else {
return Recipient.external(context, content.getSender());
return Recipient.externalPush(context, content.getSender());
}
}
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) {
Recipient author = Recipient.external(context, sender);
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull SignalServiceAddress sender, int device) {
Recipient author = Recipient.externalPush(context, sender);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(conversationRecipient);
if (threadId > 0) {
@ -1341,7 +1335,7 @@ public class PushDecryptJob extends BaseJob {
return true;
}
Recipient sender = Recipient.external(context, content.getSender());
Recipient sender = Recipient.externalPush(context, content.getSender());
if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get();

View File

@ -11,7 +11,6 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
@ -45,6 +44,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Qu
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.IOException;
@ -96,7 +96,7 @@ public class PushGroupSendJob extends PushSendJob {
throw new AssertionError("Not a group!");
}
if (!DatabaseFactory.getGroupDatabase(context).isActive(group.requireAddress().toGroupString())) {
if (!DatabaseFactory.getGroupDatabase(context).isActive(group.requireGroupId())) {
throw new MmsException("Inactive group!");
}
@ -152,12 +152,12 @@ public class PushGroupSendJob extends PushSendJob {
if (filterRecipient != null) target = Collections.singletonList(Recipient.resolved(filterRecipient).getId());
else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).toList();
else target = getGroupMessageRecipients(message.getRecipient().requireAddress().toGroupString(), messageId);
else target = getGroupMessageRecipients(message.getRecipient().requireGroupId(), messageId);
List<SendMessageResult> results = deliver(message, target);
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Recipient.external(context, result.getAddress().getNumber()).getId())).toList();
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Recipient.external(context, result.getAddress().getNumber()).getId(), result.getIdentityFailure().getIdentityKey())).toList();
Set<RecipientId> successIds = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> result.getAddress().getNumber()).map(a -> Recipient.external(context, a).getId()).collect(Collectors.toSet());
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Recipient.externalPush(context, result.getAddress()).getId())).toList();
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Recipient.externalPush(context, result.getAddress()).getId(), result.getIdentityFailure().getIdentityKey())).toList();
Set<RecipientId> successIds = Stream.of(results).filter(result -> result.getSuccess() != null).map(SendMessageResult::getAddress).map(a -> Recipient.externalPush(context, a).getId()).collect(Collectors.toSet());
List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
@ -181,7 +181,7 @@ public class PushGroupSendJob extends PushSendJob {
}
for (SendMessageResult success : successes) {
DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Recipient.external(context, success.getAddress().getNumber()).getId(),
DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Recipient.externalPush(context, success.getAddress()).getId(),
messageId,
success.getSuccess().isUnidentified());
}
@ -231,33 +231,36 @@ public class PushGroupSendJob extends PushSendJob {
rotateSenderCertificateIfNecessary();
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
String groupId = message.getRecipient().requireAddress().toGroupString();
String groupId = message.getRecipient().requireGroupId();
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
Optional<Quote> quote = getQuoteFor(message);
Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message);
List<SharedContact> sharedContacts = getSharedContactsFor(message);
List<Preview> previews = getPreviewsFor(message);
List<SignalServiceAddress> addresses = Stream.of(destinations).map(Recipient::resolved).map(Recipient::requireAddress).map(this::getPushAddress).toList();
List<SignalServiceAddress> addresses = Stream.of(destinations).map(Recipient::resolved).map(this::getPushAddress).toList();
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
boolean isRecipientUpdate = destinations.size() != DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId).size();
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses)
.map(address -> Recipient.external(context, address.getNumber()))
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations)
.map(Recipient::resolved)
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
.toList();
if (message.isGroup()) {
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
GroupContext groupContext = groupMessage.getGroupContext();
SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0);
SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE;
SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupContext.getName(), groupContext.getMembersList(), avatar);
SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis())
.withExpiration(message.getRecipient().getExpireMessages())
.asGroupMessage(group)
.build();
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
GroupContext groupContext = groupMessage.getGroupContext();
SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0);
SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE;
List<SignalServiceAddress> members = Stream.of(groupContext.getMembersList())
.map(m -> new SignalServiceAddress(UuidUtil.parseOrNull(m.getUuid()), m.getE164()))
.toList();
SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupContext.getName(), members, avatar);
SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis())
.withExpiration(message.getRecipient().getExpireMessages())
.asGroupMessage(group)
.build();
return messageSender.sendMessage(addresses, unidentifiedAccess, isRecipientUpdate, groupDataMessage);
} else {

View File

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -25,6 +26,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -92,10 +95,11 @@ public class PushGroupUpdateJob extends BaseJob {
.build();
}
List<String> members = new LinkedList<>();
List<SignalServiceAddress> members = new LinkedList<>();
for (RecipientId member : record.get().getMembers()) {
members.add(Recipient.resolved(member).requireAddress().serialize());
Recipient recipient = Recipient.resolved(member);
members.add(RecipientUtil.toSignalServiceAddress(context, recipient));
}
SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE)
@ -117,7 +121,7 @@ public class PushGroupUpdateJob extends BaseJob {
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
Recipient recipient = Recipient.resolved(source);
messageSender.sendMessage(new SignalServiceAddress(recipient.requireAddress().serialize()),
messageSender.sendMessage(RecipientUtil.toSignalServiceAddress(context, recipient),
UnidentifiedAccessUtil.getAccessFor(context, recipient),
message);
}

View File

@ -10,7 +10,6 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.MmsDatabase;
@ -29,6 +28,7 @@ import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -67,7 +67,7 @@ public class PushMediaSendJob extends PushSendJob {
@WorkerThread
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Recipient recipient) {
try {
if (!recipient.requireAddress().isPhone()) {
if (!recipient.hasServiceIdentifier()) {
throw new AssertionError();
}
@ -164,7 +164,7 @@ public class PushMediaSendJob extends PushSendJob {
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
} catch (UntrustedIdentityException uie) {
warn(TAG, "Failure", uie);
database.addMismatchedIdentity(messageId, Recipient.external(context, uie.getE164Number()).getId(), uie.getIdentityKey());
database.addMismatchedIdentity(messageId, Recipient.external(context, uie.getIdentifier()).getId(), uie.getIdentityKey());
database.markAsSentFailed(messageId);
}
}
@ -189,19 +189,11 @@ public class PushMediaSendJob extends PushSendJob {
throw new UndeliverableMessageException("No destination address.");
}
final Address destination = message.getRecipient().requireAddress();
if (!destination.isPhone()) {
if (destination.isEmail()) throw new UndeliverableMessageException("Not e164, is email");
if (destination.isGroup()) throw new UndeliverableMessageException("Not e164, is group");
throw new UndeliverableMessageException("Not e164, unknown");
}
try {
rotateSenderCertificateIfNecessary();
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
SignalServiceAddress address = getPushAddress(destination);
SignalServiceAddress address = getPushAddress(message.getRecipient());
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
List<SignalServiceAttachment> serviceAttachments = getAttachmentPointersFor(attachments);
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
@ -223,7 +215,7 @@ public class PushMediaSendJob extends PushSendJob {
.asExpirationUpdate(message.isExpirationUpdate())
.build();
if (address.getNumber().equals(TextSecurePreferences.getLocalNumber(context))) {
if (Util.equals(TextSecurePreferences.getLocalUuid(context), address.getUuid().orNull())) {
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, mediaMessage, syncAccess);

View File

@ -1,17 +1,6 @@
package org.thoughtcrime.securesms.jobs;
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
public abstract class PushReceivedJob extends BaseJob {

View File

@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.PartProgressEvent;
@ -33,6 +32,7 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil;
@ -106,9 +106,8 @@ public abstract class PushSendJob extends SendJob {
return Optional.of(ProfileKeyUtil.getProfileKey(context));
}
protected SignalServiceAddress getPushAddress(Address address) {
String relay = null;
return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay));
protected SignalServiceAddress getPushAddress(@NonNull Recipient recipient) {
return RecipientUtil.toSignalServiceAddress(context, recipient);
}
protected List<SignalServiceAttachment> getAttachmentsFor(List<Attachment> parts) {
@ -256,7 +255,9 @@ public abstract class PushSendJob extends SendJob {
}
}
return Optional.of(new SignalServiceDataMessage.Quote(quoteId, new SignalServiceAddress(Recipient.resolved(quoteAuthor).requireAddress().serialize()), quoteBody, quoteAttachments));
Recipient quoteAuthorRecipient = Recipient.resolved(quoteAuthor);
SignalServiceAddress quoteAddress = RecipientUtil.toSignalServiceAddress(context, quoteAuthorRecipient);
return Optional.of(new SignalServiceDataMessage.Quote(quoteId, quoteAddress, quoteBody, quoteAttachments));
}
protected Optional<SignalServiceDataMessage.Sticker> getStickerFor(OutgoingMediaMessage message) {
@ -329,13 +330,13 @@ public abstract class PushSendJob extends SendJob {
}
protected SignalServiceSyncMessage buildSelfSendSyncMessage(@NonNull Context context, @NonNull SignalServiceDataMessage message, Optional<UnidentifiedAccessPair> syncAccess) {
String localNumber = TextSecurePreferences.getLocalNumber(context);
SentTranscriptMessage transcript = new SentTranscriptMessage(localNumber,
message.getTimestamp(),
message,
message.getExpiresInSeconds(),
Collections.singletonMap(localNumber, syncAccess.isPresent()),
false);
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalUuid(context), TextSecurePreferences.getLocalNumber(context));
SentTranscriptMessage transcript = new SentTranscriptMessage(localAddress,
message.getTimestamp(),
message,
message.getExpiresInSeconds(),
Collections.singletonMap(localAddress, syncAccess.isPresent()),
false);
return SignalServiceSyncMessage.forSentTranscript(transcript);
}

View File

@ -7,7 +7,6 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessM
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
@ -21,6 +20,7 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -122,7 +122,7 @@ public class PushTextSendJob extends PushSendJob {
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
} catch (UntrustedIdentityException e) {
warn(TAG, "Failure", e);
database.addMismatchedIdentity(record.getId(), Recipient.external(context, e.getE164Number()).getId(), e.getIdentityKey());
database.addMismatchedIdentity(record.getId(), Recipient.external(context, e.getIdentifier()).getId(), e.getIdentityKey());
database.markAsSentFailed(record.getId());
database.markAsPush(record.getId());
}
@ -154,7 +154,7 @@ public class PushTextSendJob extends PushSendJob {
rotateSenderCertificateIfNecessary();
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().requireAddress());
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient());
Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient());
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, message.getIndividualRecipient());
@ -168,7 +168,7 @@ public class PushTextSendJob extends PushSendJob {
.asEndSessionMessage(message.isEndSession())
.build();
if (address.getNumber().equals(TextSecurePreferences.getLocalNumber(context))) {
if (Util.equals(TextSecurePreferences.getLocalUuid(context), address.getUuid().orNull())) {
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess);

View File

@ -51,7 +51,8 @@ public class RefreshUnidentifiedDeliveryAbilityJob extends BaseJob {
@Override
public void onRun() throws Exception {
byte[] profileKey = ProfileKeyUtil.getProfileKey(context);
SignalServiceProfile profile = retrieveProfile(TextSecurePreferences.getLocalNumber(context));
SignalServiceAddress address = new SignalServiceAddress(Optional.of(TextSecurePreferences.getLocalUuid(context)), Optional.of(TextSecurePreferences.getLocalNumber(context)));
SignalServiceProfile profile = retrieveProfile(address);
boolean enabled = profile.getUnidentifiedAccess() != null && isValidVerifier(profileKey, profile.getUnidentifiedAccess());
@ -68,19 +69,19 @@ public class RefreshUnidentifiedDeliveryAbilityJob extends BaseJob {
return exception instanceof PushNetworkException;
}
private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException {
private SignalServiceProfile retrieveProfile(@NonNull SignalServiceAddress address) throws IOException {
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
if (pipe != null) {
try {
return pipe.getProfile(new SignalServiceAddress(number), Optional.absent());
return pipe.getProfile(address, Optional.absent());
} catch (IOException e) {
Log.w(TAG, e);
}
}
return receiver.retrieveProfile(new SignalServiceAddress(number), Optional.absent());
return receiver.retrieveProfile(address, Optional.absent());
}
private boolean isValidVerifier(@NonNull byte[] profileKey, @NonNull String verifier) {

View File

@ -3,14 +3,15 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
@ -79,7 +80,7 @@ public class RequestGroupInfoJob extends BaseJob {
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
Recipient recipient = Recipient.resolved(source);
messageSender.sendMessage(new SignalServiceAddress(recipient.requireAddress().serialize()),
messageSender.sendMessage(RecipientUtil.toSignalServiceAddress(context, recipient),
UnidentifiedAccessUtil.getAccessFor(context, recipient),
message);
}

View File

@ -85,7 +85,7 @@ public class RetrieveProfileAvatarJob extends BaseJob {
}
if (TextUtils.isEmpty(profileAvatar)) {
Log.w(TAG, "Removing profile avatar (no url) for: " + recipient.requireAddress().serialize());
Log.w(TAG, "Removing profile avatar (no url) for: " + recipient.getId().serialize());
AvatarHelper.delete(context, recipient.getId());
database.setProfileAvatar(recipient.getId(), profileAvatar);
return;
@ -107,7 +107,7 @@ public class RetrieveProfileAvatarJob extends BaseJob {
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getId()));
} catch (PushNetworkException e) {
if (e.getCause() instanceof NonSuccessfulResponseCodeException) {
Log.w(TAG, "Removing profile avatar (no image available) for: " + recipient.requireAddress().serialize());
Log.w(TAG, "Removing profile avatar (no image available) for: " + recipient.getId().serialize());
AvatarHelper.delete(context, recipient.getId());
} else {
throw e;

View File

@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import org.thoughtcrime.securesms.ApplicationContext;
@ -16,6 +18,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.IdentityUtil;
@ -86,21 +89,21 @@ public class RetrieveProfileJob extends BaseJob {
public void onCanceled() {}
private void handleIndividualRecipient(Recipient recipient) throws IOException {
if (recipient.requireAddress().isPhone()) handlePhoneNumberRecipient(recipient);
else Log.w(TAG, "Skipping fetching profile of non-phone recipient");
if (recipient.hasServiceIdentifier()) handlePhoneNumberRecipient(recipient);
else Log.w(TAG, "Skipping fetching profile of non-Signal recipient");
}
private void handlePhoneNumberRecipient(Recipient recipient) throws IOException {
String number = recipient.requireAddress().toPhoneString();
SignalServiceAddress address = RecipientUtil.toSignalServiceAddress(context, recipient);
Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(recipient);
SignalServiceProfile profile;
try {
profile = retrieveProfile(number, unidentifiedAccess);
profile = retrieveProfile(address, unidentifiedAccess);
} catch (NonSuccessfulResponseCodeException e) {
if (unidentifiedAccess.isPresent()) {
profile = retrieveProfile(number, Optional.absent());
profile = retrieveProfile(address, Optional.absent());
} else {
throw e;
}
@ -109,18 +112,19 @@ public class RetrieveProfileJob extends BaseJob {
setIdentityKey(recipient, profile.getIdentityKey());
setProfileName(recipient, profile.getName());
setProfileAvatar(recipient, profile.getAvatar());
setProfileCapabilities(recipient, profile.getCapabilities());
setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess());
}
private void handleGroupRecipient(Recipient group) throws IOException {
List<Recipient> recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(group.requireAddress().toGroupString(), false);
List<Recipient> recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(group.requireGroupId(), false);
for (Recipient recipient : recipients) {
handleIndividualRecipient(recipient);
}
}
private SignalServiceProfile retrieveProfile(@NonNull String number, Optional<UnidentifiedAccess> unidentifiedAccess)
private SignalServiceProfile retrieveProfile(@NonNull SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess)
throws IOException
{
SignalServiceMessagePipe authPipe = IncomingMessageObserver.getPipe();
@ -130,14 +134,14 @@ public class RetrieveProfileJob extends BaseJob {
if (pipe != null) {
try {
return pipe.getProfile(new SignalServiceAddress(number), unidentifiedAccess);
return pipe.getProfile(address, unidentifiedAccess);
} catch (IOException e) {
Log.w(TAG, e);
}
}
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
return receiver.retrieveProfile(new SignalServiceAddress(number), unidentifiedAccess);
return receiver.retrieveProfile(address, unidentifiedAccess);
}
private void setIdentityKey(Recipient recipient, String identityKeyValue) {
@ -157,7 +161,7 @@ public class RetrieveProfileJob extends BaseJob {
return;
}
IdentityUtil.saveIdentity(context, recipient.requireAddress().toPhoneString(), identityKey);
IdentityUtil.saveIdentity(context, recipient.requireServiceId(), identityKey);
} catch (InvalidKeyException | IOException e) {
Log.w(TAG, e);
}
@ -225,6 +229,14 @@ public class RetrieveProfileJob extends BaseJob {
}
}
private void setProfileCapabilities(@NonNull Recipient recipient, @Nullable SignalServiceProfile.Capabilities capabilities) {
if (capabilities == null) {
return;
}
DatabaseFactory.getRecipientDatabase(context).setUuidSupported(recipient.getId(), capabilities.isUuid());
}
private Optional<UnidentifiedAccess> getUnidentifiedAccess(@NonNull Recipient recipient) {
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);

View File

@ -52,10 +52,12 @@ public class RotateCertificateJob extends BaseJob {
@Override
public void onRun() throws IOException {
synchronized (RotateCertificateJob.class) {
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
byte[] certificate = accountManager.getSenderCertificate();
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
byte[] certificate = accountManager.getSenderCertificate();
byte[] legacyCertificate = accountManager.getSenderCertificateLegacy();
TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
TextSecurePreferences.setUnidentifiedAccessCertificateLegacy(context, legacyCertificate);
}
}

View File

@ -5,7 +5,6 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;

View File

@ -4,7 +4,6 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
@ -12,6 +11,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
@ -76,7 +76,7 @@ public class SendDeliveryReceiptJob extends BaseJob {
public void onRun() throws IOException, UntrustedIdentityException {
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
Recipient recipient = Recipient.resolved(recipientId);
SignalServiceAddress remoteAddress = new SignalServiceAddress(recipient.requireAddress().serialize());
SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY,
Collections.singletonList(messageId),
timestamp);

View File

@ -13,7 +13,9 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
@ -87,7 +89,7 @@ public class SendReadReceiptJob extends BaseJob {
Recipient recipient = Recipient.resolved(recipientId);
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
SignalServiceAddress remoteAddress = new SignalServiceAddress(recipient.requireAddress().serialize());
SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
messageSender.sendReceipt(remoteAddress,

View File

@ -9,7 +9,6 @@ import androidx.annotation.NonNull;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
@ -118,7 +117,7 @@ public class SmsSendJob extends SendJob {
throw new UndeliverableMessageException("Trying to send a secure SMS?");
}
String recipient = message.getIndividualRecipient().requireAddress().serialize();
String recipient = message.getIndividualRecipient().requireSmsAddress();
// See issue #1516 for bug report, and discussion on commits related to #4833 for problems
// related to the original fix to #1516. This still may not be a correct fix if networks allow

View File

@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
@ -85,12 +86,14 @@ public class TypingSendJob extends BaseJob {
Optional<byte[]> groupId = Optional.absent();
if (recipient.isGroup()) {
recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireAddress().toGroupString(), false);
groupId = Optional.of(GroupUtil.getDecodedId(recipient.requireAddress().toGroupString()));
recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireGroupId(), false);
groupId = Optional.of(GroupUtil.getDecodedId(recipient.requireGroupId()));
}
recipients = Stream.of(recipients).map(Recipient::resolve).toList();
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
List<SignalServiceAddress> addresses = Stream.of(recipients).map(r -> new SignalServiceAddress(r.requireAddress().serialize())).toList();
List<SignalServiceAddress> addresses = Stream.of(recipients).map(r -> RecipientUtil.toSignalServiceAddress(context, r)).toList();
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(recipients).map(r -> UnidentifiedAccessUtil.getAccessFor(context, r)).toList();
SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis(), groupId);

View File

@ -131,7 +131,7 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity {
getSupportActionBar().setTitle(getString(R.string.LongMessageActivity_your_message));
} else {
Recipient recipient = message.get().getMessageRecord().getRecipient();
String name = Util.getFirstNonEmpty(recipient.getName(), recipient.getProfileName(), recipient.requireAddress().serialize()) ;
String name = Util.getFirstNonEmpty(recipient.getName(), recipient.getProfileName(), recipient.getE164().orNull(), recipient.getEmail().orNull()) ;
getSupportActionBar().setTitle(getString(R.string.LongMessageActivity_message_from_s, name));
}

View File

@ -704,9 +704,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
} else if (recipient.isLocalNumber()) {
composeText.setHint(getString(R.string.note_to_self), null);
} else {
String displayName = Optional.fromNullable(recipient.getName())
.or(Optional.fromNullable(recipient.getProfileName())
.or(recipient.requireAddress().serialize()));
String displayName = Util.getFirstNonEmpty(recipient.getName(), recipient.getProfileName(), recipient.getE164().orNull(), recipient.getEmail().orNull());
composeText.setHint(getString(R.string.MediaSendActivity_message_to_s, displayName), null);
}

View File

@ -38,7 +38,7 @@ public class ApplicationMigrations {
private static final int LEGACY_CANONICAL_VERSION = 455;
public static final int CURRENT_VERSION = 5;
public static final int CURRENT_VERSION = 6;
private static final class Version {
static final int LEGACY = 1;
@ -46,6 +46,7 @@ public class ApplicationMigrations {
static final int RECIPIENT_SEARCH = 3;
static final int RECIPIENT_CLEANUP = 4;
static final int AVATAR_MIGRATION = 5;
static final int UUIDS = 6;
}
/**
@ -178,6 +179,10 @@ public class ApplicationMigrations {
jobs.put(Version.AVATAR_MIGRATION, new AvatarMigrationJob());
}
if (lastSeenVersion < Version.UUIDS) {
jobs.put(Version.UUIDS, new UuidMigrationJob());
}
return jobs;
}

View File

@ -0,0 +1,92 @@
package org.thoughtcrime.securesms.migrations;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import java.io.IOException;
import java.util.UUID;
/**
* Couple migrations steps need to happen after we move to UUIDS.
* - We need to get our own UUID.
* - We need to fetch the new UUID sealed sender cert.
* - We need to do a directory sync so we can guarantee that all active users have UUIDs.
*/
public class UuidMigrationJob extends MigrationJob {
public static final String KEY = "UuidMigrationJob";
private static final String TAG = Log.tag(UuidMigrationJob.class);
UuidMigrationJob() {
this(new Parameters.Builder().addConstraint(NetworkConstraint.KEY).build());
}
private UuidMigrationJob(@NonNull Parameters parameters) {
super(parameters);
}
@Override
public @NonNull String getFactoryKey() {
return KEY;
}
@Override
boolean isUiBlocking() {
return false;
}
@Override
void performMigration() throws Exception {
if (!TextSecurePreferences.isPushRegistered(context)) {
Log.w(TAG, "Not registered! Skipping migration, as it wouldn't do anything.");
}
fetchOwnUuid(context);
rotateSealedSenderCerts(context);
}
@Override
boolean shouldRetry(@NonNull Exception e) {
return e instanceof IOException;
}
private static void fetchOwnUuid(@NonNull Context context) throws IOException {
RecipientId self = Recipient.self().getId();
UUID localUuid = ApplicationDependencies.getSignalServiceAccountManager().getOwnUuid();
DatabaseFactory.getRecipientDatabase(context).markRegistered(self, localUuid);
TextSecurePreferences.setLocalUuid(context, localUuid);
}
private static void rotateSealedSenderCerts(@NonNull Context context) throws IOException {
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
byte[] certificate = accountManager.getSenderCertificate();
byte[] legacyCertificate = accountManager.getSenderCertificateLegacy();
TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
TextSecurePreferences.setUnidentifiedAccessCertificateLegacy(context, legacyCertificate);
}
public static class Factory implements Job.Factory<UuidMigrationJob> {
@Override
public @NonNull UuidMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new UuidMigrationJob(parameters);
}
}
}

View File

@ -5,7 +5,6 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.GroupUtil;

View File

@ -7,7 +7,6 @@ import androidx.annotation.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;

View File

@ -5,7 +5,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.List;

View File

@ -25,7 +25,6 @@ import android.os.AsyncTask;
import android.os.Bundle;
import androidx.core.app.RemoteInput;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;

View File

@ -12,7 +12,6 @@ import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;

View File

@ -41,8 +41,8 @@ public class NotificationChannels {
private static final String TAG = NotificationChannels.class.getSimpleName();
private static class Version {
static final int MESSAGES_CATEGORY = 2;
static final int CALLS_PRIORITY_BUMP = 3;
static final int MESSAGES_CATEGORY = 2;
static final int CALLS_PRIORITY_BUMP = 3;
}
private static final int VERSION = 3;
@ -147,7 +147,7 @@ public class NotificationChannels {
VibrateState vibrateState = recipient.getMessageVibrate();
boolean vibrationEnabled = vibrateState == VibrateState.DEFAULT ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == VibrateState.ENABLED;
Uri messageRingtone = recipient.getMessageRingtone() != null ? recipient.getMessageRingtone() : getMessageRingtone(context);
String displayName = getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.requireAddress().serialize());
String displayName = getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getSmsAddress().or(""));
return createChannelFor(context, generateChannelIdFor(recipient), displayName, messageRingtone, vibrationEnabled);
}
@ -384,7 +384,7 @@ public class NotificationChannels {
}
NotificationChannel channel = new NotificationChannel(recipient.getNotificationChannel(),
getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.requireAddress().serialize()),
getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getSmsAddress().or("")),
NotificationManager.IMPORTANCE_HIGH);
channel.setGroup(CATEGORY_MESSAGES);
notificationManager.createNotificationChannel(channel);

View File

@ -17,6 +17,9 @@ import androidx.appcompat.app.AlertDialog;
import androidx.preference.CheckBoxPreference;
import androidx.preference.Preference;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.logging.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
@ -24,8 +27,6 @@ import org.thoughtcrime.securesms.LogSubmitActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
@ -185,7 +186,7 @@ public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
protected Integer doInBackground(Void... params) {
try {
Context context = getActivity();
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
try {
accountManager.setGcmId(Optional.<String>absent());

Some files were not shown because too many files have changed in this diff Show More