kopia lustrzana https://github.com/ryukoposting/Signal-Android
Automatically recover from bad encrypted messages.
rodzic
adea15df10
commit
728f1707b6
|
@ -65,6 +65,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
|
|||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||
{
|
||||
public static final String LAUNCH_TO_BACKUPS_FRAGMENT = "launch.to.backups.fragment";
|
||||
public static final String LAUNCH_TO_HELP_FRAGMENT = "launch.to.help.fragment";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = ApplicationPreferencesActivity.class.getSimpleName();
|
||||
|
@ -104,6 +105,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
|
|||
initFragment(android.R.id.content, new NotificationsPreferenceFragment());
|
||||
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_BACKUPS_FRAGMENT, false)) {
|
||||
initFragment(android.R.id.content, new BackupsPreferenceFragment());
|
||||
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_HELP_FRAGMENT, false)) {
|
||||
initFragment(android.R.id.content, new HelpFragment());
|
||||
} else if (icicle == null) {
|
||||
initFragment(android.R.id.content, new ApplicationPreferenceFragment());
|
||||
} else {
|
||||
|
|
|
@ -61,6 +61,7 @@ public interface BindableConversationItem extends Unbindable {
|
|||
void onVoiceNotePlay(@NonNull Uri uri, long messageId, double position);
|
||||
void onVoiceNoteSeekTo(@NonNull Uri uri, double position);
|
||||
void onGroupMigrationLearnMoreClicked(@NonNull GroupMigrationMembershipChange membershipChange);
|
||||
void onDecryptionFailedLearnMoreClicked();
|
||||
void onJoinGroupCallClicked();
|
||||
void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId);
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ import org.signal.core.util.StreamUtil;
|
|||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
@ -1415,6 +1416,23 @@ public class ConversationFragment extends LoggingFragment {
|
|||
GroupsV1MigrationInfoBottomSheetDialogFragment.show(requireFragmentManager(), membershipChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDecryptionFailedLearnMoreClicked() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setView(R.layout.decryption_failed_dialog)
|
||||
.setPositiveButton(android.R.string.ok, (d, w) -> {
|
||||
d.dismiss();
|
||||
})
|
||||
.setNeutralButton(R.string.ConversationFragment_contact_us, (d, w) -> {
|
||||
Intent intent = new Intent(requireContext(), ApplicationPreferencesActivity.class);
|
||||
intent.putExtra(ApplicationPreferencesActivity.LAUNCH_TO_HELP_FRAGMENT, true);
|
||||
|
||||
startActivity(intent);
|
||||
d.dismiss();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoinGroupCallClicked() {
|
||||
CommunicationActions.startVideoCall(requireActivity(), recipient.get());
|
||||
|
|
|
@ -206,6 +206,16 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
eventListener.onGroupMigrationLearnMoreClicked(conversationMessage.getMessageRecord().getGroupV1MigrationMembershipChanges());
|
||||
}
|
||||
});
|
||||
} else if (conversationMessage.getMessageRecord().isFailedDecryptionType() &&
|
||||
(!nextMessageRecord.isPresent() || !nextMessageRecord.get().isFailedDecryptionType()))
|
||||
{
|
||||
actionButton.setText(R.string.ConversationUpdateItem_learn_more);
|
||||
actionButton.setVisibility(VISIBLE);
|
||||
actionButton.setOnClickListener(v -> {
|
||||
if (batchSelected.isEmpty() && eventListener != null) {
|
||||
eventListener.onDecryptionFailedLearnMoreClicked();
|
||||
}
|
||||
});
|
||||
} else if (conversationMessage.getMessageRecord().isGroupCall()) {
|
||||
UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true);
|
||||
Collection<UUID> uuids = updateDescription.getMentioned();
|
||||
|
|
|
@ -434,7 +434,8 @@ public final class ConversationListItem extends ConstraintLayout
|
|||
} else if (SmsDatabase.Types.isKeyExchangeType(thread.getType())) {
|
||||
return emphasisAdded(context, context.getString(R.string.ConversationListItem_key_exchange_message), defaultTint);
|
||||
} else if (SmsDatabase.Types.isFailedDecryptType(thread.getType())) {
|
||||
return emphasisAdded(context, context.getString(R.string.MessageDisplayHelper_bad_encrypted_message), defaultTint);
|
||||
UpdateDescription description = UpdateDescription.staticDescription(context.getString(R.string.ThreadRecord_chat_session_refreshed), R.drawable.ic_refresh_16);
|
||||
return emphasisAdded(context, description, defaultTint);
|
||||
} else if (SmsDatabase.Types.isNoRemoteSessionType(thread.getType())) {
|
||||
return emphasisAdded(context, context.getString(R.string.MessageDisplayHelper_message_encrypted_for_non_existing_session), defaultTint);
|
||||
} else if (SmsDatabase.Types.isEndSessionType(thread.getType())) {
|
||||
|
|
|
@ -29,4 +29,7 @@ public class SessionUtil {
|
|||
new TextSecureSessionStore(context).archiveAllSessions();
|
||||
}
|
||||
|
||||
public static void archiveSession(Context context, RecipientId recipientId, int deviceId) {
|
||||
new TextSecureSessionStore(context).archiveSession(recipientId, deviceId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,6 +103,16 @@ public class TextSecureSessionStore implements SessionStore {
|
|||
}
|
||||
}
|
||||
|
||||
public void archiveSession(@NonNull RecipientId recipientId, int deviceId) {
|
||||
synchronized (FILE_LOCK) {
|
||||
SessionRecord session = DatabaseFactory.getSessionDatabase(context).load(recipientId, deviceId);
|
||||
if (session != null) {
|
||||
session.archiveCurrentState();
|
||||
DatabaseFactory.getSessionDatabase(context).store(recipientId, deviceId, session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) {
|
||||
synchronized (FILE_LOCK) {
|
||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||
|
|
|
@ -149,6 +149,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
|||
public abstract Optional<InsertResult> insertMessageInbox(IncomingMediaMessage retrieved, String contentLocation, long threadId) throws MmsException;
|
||||
public abstract Pair<Long, Long> insertMessageInbox(@NonNull NotificationInd notification, int subscriptionId);
|
||||
public abstract Optional<InsertResult> insertSecureDecryptedMessageInbox(IncomingMediaMessage retrieved, long threadId) throws MmsException;
|
||||
public abstract @NonNull InsertResult insertDecryptionFailedMessage(@NonNull RecipientId recipientId, long senderDeviceId, long sentTimestamp);
|
||||
public abstract long insertMessageOutbox(long threadId, OutgoingTextMessage message, boolean forceSms, long date, InsertListener insertListener);
|
||||
public abstract long insertMessageOutbox(@NonNull OutgoingMediaMessage message, long threadId, boolean forceSms, @Nullable SmsDatabase.InsertListener insertListener) throws MmsException;
|
||||
public abstract long insertMessageOutbox(@NonNull OutgoingMediaMessage message, long threadId, boolean forceSms, int defaultReceiptStatus, @Nullable SmsDatabase.InsertListener insertListener) throws MmsException;
|
||||
|
|
|
@ -1383,6 +1383,11 @@ public class MmsDatabase extends MessageDatabase {
|
|||
return new Pair<>(messageId, threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull InsertResult insertDecryptionFailedMessage(@NonNull RecipientId recipientId, long senderDeviceId, long sentTimestamp) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markIncomingNotificationReceived(long threadId) {
|
||||
notifyConversationListeners(threadId);
|
||||
|
|
|
@ -1038,7 +1038,7 @@ public class SmsDatabase extends MessageDatabase {
|
|||
if (groupRecipient == null) threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
||||
|
||||
ContentValues values = new ContentValues(6);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(RECIPIENT_ID, message.getSender().serialize());
|
||||
values.put(ADDRESS_DEVICE_ID, message.getSenderDeviceId());
|
||||
values.put(DATE_RECEIVED, System.currentTimeMillis());
|
||||
|
@ -1093,6 +1093,36 @@ public class SmsDatabase extends MessageDatabase {
|
|||
return insertMessageInbox(message, Types.BASE_INBOX_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull InsertResult insertDecryptionFailedMessage(@NonNull RecipientId recipientId, long senderDeviceId, long sentTimestamp) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.resolved(recipientId));
|
||||
long type = Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT;
|
||||
|
||||
type = type & (Types.TOTAL_MASK - Types.ENCRYPTION_MASK) | Types.ENCRYPTION_REMOTE_FAILED_BIT;
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(RECIPIENT_ID, recipientId.serialize());
|
||||
values.put(ADDRESS_DEVICE_ID, senderDeviceId);
|
||||
values.put(DATE_RECEIVED, System.currentTimeMillis());
|
||||
values.put(DATE_SENT, sentTimestamp);
|
||||
values.put(DATE_SERVER, -1);
|
||||
values.put(READ, 0);
|
||||
values.put(TYPE, type);
|
||||
values.put(THREAD_ID, threadId);
|
||||
|
||||
long messageId = db.insert(TABLE_NAME, null, values);
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
|
||||
notifyConversationListeners(threadId);
|
||||
|
||||
ApplicationDependencies.getJobManager().add(new TrimThreadJob(threadId));
|
||||
|
||||
return new InsertResult(messageId, threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long insertMessageOutbox(long threadId, OutgoingTextMessage message,
|
||||
boolean forceSms, long date, InsertListener insertListener)
|
||||
|
|
|
@ -191,6 +191,8 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
else return fromRecipient(getIndividualRecipient(), r-> context.getString(R.string.SmsMessageRecord_secure_session_reset_s, r.getDisplayName(context)), R.drawable.ic_update_info_16);
|
||||
} else if (isGroupV1MigrationEvent()) {
|
||||
return getGroupMigrationEventDescription(context);
|
||||
} else if (isFailedDecryptionType()) {
|
||||
return staticUpdateDescription(context.getString(R.string.MessageRecord_chat_session_refreshed), R.drawable.ic_refresh_16);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -436,7 +438,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
public boolean isUpdate() {
|
||||
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
|
||||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() ||
|
||||
isProfileChange() || isGroupV1MigrationEvent();
|
||||
isProfileChange() || isGroupV1MigrationEvent() || isFailedDecryptionType();
|
||||
}
|
||||
|
||||
public boolean isMediaPending() {
|
||||
|
@ -471,6 +473,10 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
return isFailed() && ((getRecipient().isPushGroup() && hasNetworkFailures()) || !isIdentityMismatchFailure());
|
||||
}
|
||||
|
||||
public boolean isFailedDecryptionType() {
|
||||
return MmsSmsColumns.Types.isFailedDecryptType(type);
|
||||
}
|
||||
|
||||
protected static SpannableString emphasisAdded(String sequence) {
|
||||
SpannableString spannable = new SpannableString(sequence);
|
||||
spannable.setSpan(new RelativeSizeSpan(0.9f), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
|
|
@ -66,7 +66,7 @@ public class SmsMessageRecord extends MessageRecord {
|
|||
@Override
|
||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||
if (SmsDatabase.Types.isFailedDecryptType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_bad_encrypted_message));
|
||||
return emphasisAdded(context.getString(R.string.MessageRecord_chat_session_refreshed));
|
||||
} else if (isCorruptedKeyExchange()) {
|
||||
return emphasisAdded(context.getString(R.string.SmsMessageRecord_received_corrupted_key_exchange_message));
|
||||
} else if (isInvalidVersionKeyExchange()) {
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraint;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* - Archives the session associated with the specified device
|
||||
* - Inserts an error message in the conversation
|
||||
* - Sends a new, empty message to trigger a fresh session with the specified device
|
||||
*
|
||||
* This will only be run when all decryptions have finished, and there can only be one enqueued
|
||||
* per websocket drain cycle.
|
||||
*/
|
||||
public class AutomaticSessionResetJob extends BaseJob {
|
||||
|
||||
private static final String TAG = Log.tag(AutomaticSessionResetJob.class);
|
||||
|
||||
public static final String KEY = "AutomaticSessionResetJob";
|
||||
|
||||
private static final String KEY_RECIPIENT_ID = "recipient_id";
|
||||
private static final String KEY_DEVICE_ID = "device_id";
|
||||
private static final String KEY_SENT_TIMESTAMP = "sent_timestamp";
|
||||
|
||||
private final RecipientId recipientId;
|
||||
private final int deviceId;
|
||||
private final long sentTimestamp;
|
||||
|
||||
public AutomaticSessionResetJob(@NonNull RecipientId recipientId, int deviceId, long sentTimestamp) {
|
||||
this(new Parameters.Builder()
|
||||
.setQueue(PushProcessMessageJob.getQueueName(recipientId))
|
||||
.addConstraint(DecryptionsDrainedConstraint.KEY)
|
||||
.setMaxInstancesForQueue(1)
|
||||
.build(),
|
||||
recipientId,
|
||||
deviceId,
|
||||
sentTimestamp);
|
||||
}
|
||||
|
||||
private AutomaticSessionResetJob(@NonNull Parameters parameters,
|
||||
@NonNull RecipientId recipientId,
|
||||
int deviceId,
|
||||
long sentTimestamp)
|
||||
{
|
||||
super(parameters);
|
||||
this.recipientId = recipientId;
|
||||
this.deviceId = deviceId;
|
||||
this.sentTimestamp = sentTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder().putString(KEY_RECIPIENT_ID, recipientId.serialize())
|
||||
.putInt(KEY_DEVICE_ID, deviceId)
|
||||
.putLong(KEY_SENT_TIMESTAMP, sentTimestamp)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRun() throws Exception {
|
||||
SessionUtil.archiveSession(context, recipientId, deviceId);
|
||||
insertLocalMessage();
|
||||
sendNullMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onShouldRetry(@NonNull Exception e) {
|
||||
return e instanceof RetryLaterException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
}
|
||||
|
||||
private void insertLocalMessage() {
|
||||
MessageDatabase.InsertResult result = DatabaseFactory.getSmsDatabase(context).insertDecryptionFailedMessage(recipientId, deviceId, sentTimestamp);
|
||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, result.getThreadId());
|
||||
}
|
||||
|
||||
private void sendNullMessage() throws IOException {
|
||||
Recipient recipient = Recipient.resolved(recipientId);
|
||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||
SignalServiceAddress address = RecipientUtil.toSignalServiceAddress(context, recipient);
|
||||
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
|
||||
|
||||
try {
|
||||
messageSender.sendNullMessage(address, unidentifiedAccess);
|
||||
} catch (UntrustedIdentityException e) {
|
||||
Log.w(TAG, "Unable to send null message.");
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<AutomaticSessionResetJob> {
|
||||
@Override
|
||||
public @NonNull AutomaticSessionResetJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new AutomaticSessionResetJob(parameters,
|
||||
RecipientId.from(data.getString(KEY_RECIPIENT_ID)),
|
||||
data.getInt(KEY_DEVICE_ID),
|
||||
data.getLong(KEY_SENT_TIMESTAMP));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -65,6 +65,7 @@ public final class JobManagerFactories {
|
|||
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
|
||||
put(AttachmentMarkUploadedJob.KEY, new AttachmentMarkUploadedJob.Factory());
|
||||
put(AttachmentCompressionJob.KEY, new AttachmentCompressionJob.Factory());
|
||||
put(AutomaticSessionResetJob.KEY, new AutomaticSessionResetJob.Factory());
|
||||
put(AvatarGroupsV1DownloadJob.KEY, new AvatarGroupsV1DownloadJob.Factory());
|
||||
put(AvatarGroupsV2DownloadJob.KEY, new AvatarGroupsV2DownloadJob.Factory());
|
||||
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
|||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
@ -179,22 +180,11 @@ public final class PushDecryptMessageJob extends BaseJob {
|
|||
smsMessageId,
|
||||
envelope.getTimestamp()));
|
||||
|
||||
} catch (ProtocolInvalidMessageException | ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
|
||||
} catch (ProtocolInvalidMessageException | ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException | ProtocolNoSessionException e) {
|
||||
Log.w(TAG, String.valueOf(envelope.getTimestamp()), e);
|
||||
return Collections.singletonList(new PushProcessMessageJob(PushProcessMessageJob.MessageState.CORRUPT_MESSAGE,
|
||||
toExceptionMetadata(e),
|
||||
messageId,
|
||||
smsMessageId,
|
||||
envelope.getTimestamp()));
|
||||
|
||||
} catch (ProtocolNoSessionException e) {
|
||||
Log.w(TAG, String.valueOf(envelope.getTimestamp()), e);
|
||||
return Collections.singletonList(new PushProcessMessageJob(PushProcessMessageJob.MessageState.NO_SESSION,
|
||||
toExceptionMetadata(e),
|
||||
messageId,
|
||||
smsMessageId,
|
||||
envelope.getTimestamp()));
|
||||
|
||||
return Collections.singletonList(new AutomaticSessionResetJob(Recipient.external(context, e.getSender()).getId(),
|
||||
e.getSenderDevice(),
|
||||
envelope.getTimestamp()));
|
||||
} catch (ProtocolLegacyMessageException e) {
|
||||
Log.w(TAG, String.valueOf(envelope.getTimestamp()), e);
|
||||
return Collections.singletonList(new PushProcessMessageJob(PushProcessMessageJob.MessageState.LEGACY_MESSAGE,
|
||||
|
|
|
@ -483,16 +483,6 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
handleInvalidVersionMessage(e.sender, e.senderDevice, timestamp, smsMessageId);
|
||||
break;
|
||||
|
||||
case CORRUPT_MESSAGE:
|
||||
warn(TAG, String.valueOf(timestamp), "Handling corrupt message.");
|
||||
handleCorruptMessage(e.sender, e.senderDevice, timestamp, smsMessageId);
|
||||
break;
|
||||
|
||||
case NO_SESSION:
|
||||
warn(TAG, String.valueOf(timestamp), "Handling no session.");
|
||||
handleNoSessionMessage(e.sender, e.senderDevice, timestamp, smsMessageId);
|
||||
break;
|
||||
|
||||
case LEGACY_MESSAGE:
|
||||
warn(TAG, String.valueOf(timestamp), "Handling legacy message.");
|
||||
handleLegacyMessage(e.sender, e.senderDevice, timestamp, smsMessageId);
|
||||
|
@ -1474,23 +1464,6 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleNoSessionMessage(@NonNull String sender, int senderDevice, long timestamp,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
|
||||
if (!smsMessageId.isPresent()) {
|
||||
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
smsDatabase.markAsNoSession(insertResult.get().getMessageId());
|
||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
} else {
|
||||
smsDatabase.markAsNoSession(smsMessageId.get());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUnsupportedDataMessage(@NonNull String sender,
|
||||
int senderDevice,
|
||||
@NonNull Optional<GroupId> groupId,
|
||||
|
@ -2049,8 +2022,6 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
public enum MessageState {
|
||||
DECRYPTED_OK,
|
||||
INVALID_VERSION,
|
||||
CORRUPT_MESSAGE,
|
||||
NO_SESSION,
|
||||
LEGACY_MESSAGE,
|
||||
DUPLICATE_MESSAGE,
|
||||
UNSUPPORTED_DATA_MESSAGE
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="200dp"
|
||||
android:height="110dp"
|
||||
android:viewportWidth="200"
|
||||
android:viewportHeight="110">
|
||||
<path
|
||||
android:pathData="M39.3,15.1L12.9,15C9.1,15 6,18.1 6,21.9L5.9,88C5.9,91.8 9,94.9 12.8,94.9L39.1,95C42.9,95 46,91.9 46,88.1L46.2,22C46.2,18.2 43.1,15.1 39.3,15.1ZM42.8,88.1C42.8,90.1 41.1,91.8 39.1,91.8L12.7,91.7C10.7,91.7 9,90 9,88L9.1,21.9C9.1,19.9 10.8,18.2 12.8,18.2L39.2,18.3C41.2,18.3 42.9,20 42.9,22L42.8,88.1Z"
|
||||
android:fillColor="#C6C6C6"/>
|
||||
<path
|
||||
android:pathData="M187.3,15.1L160.9,15C157.1,15 154,18.1 154,21.9L153.9,88C153.9,91.8 157,94.9 160.8,94.9L187.2,95C191,95 194.1,91.9 194.1,88.1L194.2,22C194.2,18.2 191.1,15.1 187.3,15.1ZM190.8,88.1C190.8,90.1 189.1,91.8 187.1,91.8L160.7,91.7C158.7,91.7 157,90 157,88L157.1,21.9C157.1,19.9 158.8,18.2 160.8,18.2L187.2,18.3C189.2,18.3 190.9,20 190.9,22L190.8,88.1Z"
|
||||
android:fillColor="#C6C6C6"/>
|
||||
<path
|
||||
android:pathData="M126,56m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#848484"/>
|
||||
<path
|
||||
android:pathData="M136,56m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#848484"/>
|
||||
<path
|
||||
android:pathData="M146,56m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#848484"/>
|
||||
<path
|
||||
android:pathData="M54,56m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#848484"/>
|
||||
<path
|
||||
android:pathData="M64,56m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#848484"/>
|
||||
<path
|
||||
android:pathData="M74,56m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#848484"/>
|
||||
<path
|
||||
android:pathData="M100,56m-18,0a18,18 0,1 1,36 0a18,18 0,1 1,-36 0"
|
||||
android:fillColor="#2C6BED"/>
|
||||
<path
|
||||
android:pathData="M109.38,56C109.393,58.503 108.413,60.909 106.655,62.69C104.896,64.471 102.503,65.481 100,65.5C98.443,65.479 96.916,65.076 95.551,64.327C94.187,63.577 93.028,62.504 92.175,61.201C91.323,59.898 90.804,58.406 90.664,56.855C90.524,55.305 90.767,53.744 91.372,52.31C91.977,50.875 92.925,49.611 94.133,48.629C95.341,47.647 96.772,46.977 98.3,46.678C99.828,46.379 101.405,46.46 102.895,46.913C104.384,47.367 105.739,48.18 106.84,49.28L107.84,50.28L107.49,48.98V46.56H109V52.5H103V51H105.39L106.81,51.38L105.81,50.38C104.695,49.252 103.27,48.481 101.716,48.164C100.162,47.848 98.549,48.001 97.082,48.603C95.615,49.205 94.36,50.23 93.476,51.547C92.593,52.864 92.121,54.414 92.12,56C92.12,57.035 92.324,58.06 92.72,59.015C93.116,59.972 93.696,60.84 94.428,61.572C95.16,62.304 96.028,62.884 96.984,63.28C97.94,63.676 98.965,63.88 100,63.88C101.035,63.88 102.059,63.676 103.016,63.28C103.972,62.884 104.84,62.304 105.572,61.572C106.304,60.84 106.884,59.972 107.28,59.015C107.676,58.06 107.88,57.035 107.88,56H109.38Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M13.46,8A5.46,5.46 0,1 1,2.55 8,5.54 5.54,0 0,1 12,4.06l0.44,0.44 -1.13,-0.33L10.17,4.17v1.5h4.49L14.66,1.18h-1.5L13.16,2.3l0.31,1.09L13.08,3a7,7 0,0 0,-12 5,7 7,0 0,0 7,7 7,7 0,0 0,7 -7ZM13.91,4.94Z"/>
|
||||
</vector>
|
|
@ -28,7 +28,7 @@
|
|||
style="@style/Signal.Widget.Button.Small.Primary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="gone"
|
||||
tools:text="Learn more"
|
||||
tools:visibility="visible" />
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:srcCompat="@drawable/chat_session_refresh_banner" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/DecryptionFailedDialog_chat_session_refreshed"
|
||||
style="@style/Signal.Text.Headline"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:textSize="20sp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/DecryptionFailedDialog_signal_uses_end_to_end_encryption"
|
||||
style="@style/Signal.Text.Body"/>
|
||||
|
||||
|
||||
</LinearLayout>
|
|
@ -356,6 +356,7 @@
|
|||
<string name="ConversationFragment__tap_to_review">Tap to review</string>
|
||||
<string name="ConversationFragment__review_requests_carefully">Review requests carefully</string>
|
||||
<string name="ConversationFragment__signal_found_another_contact_with_the_same_name">Signal found another contact with the same name.</string>
|
||||
<string name="ConversationFragment_contact_us">Contact us</string>
|
||||
|
||||
<!-- ConversationListActivity -->
|
||||
<string name="ConversationListActivity_there_is_no_browser_installed_on_your_device">There is no browser installed on your device.</string>
|
||||
|
@ -454,6 +455,10 @@
|
|||
<string name="DateUtils_today">Today</string>
|
||||
<string name="DateUtils_yesterday">Yesterday</string>
|
||||
|
||||
<!-- DecryptionFailedDialog -->
|
||||
<string name="DecryptionFailedDialog_chat_session_refreshed">Chat session refreshed</string>
|
||||
<string name="DecryptionFailedDialog_signal_uses_end_to_end_encryption">Signal uses end-to-end encryption and it may need to refresh your chat session sometimes. This doesn\'t affect your chat\'s security, but you may have missed a message from this contact, and you can ask them to resend it.</string>
|
||||
|
||||
<!-- DeliveryStatus -->
|
||||
<string name="DeliveryStatus_sending">Sending</string>
|
||||
<string name="DeliveryStatus_sent">Sent</string>
|
||||
|
@ -1080,6 +1085,7 @@
|
|||
<string name="MessageRecord_disappearing_message_time_set_to_s">The disappearing message timer has been set to %1$s.</string>
|
||||
<string name="MessageRecord_this_group_was_updated_to_a_new_group">This group was updated to a New Group.</string>
|
||||
<string name="MessageRecord_you_couldnt_be_added_to_the_new_group_and_have_been_invited_to_join">You couldn\'t be added to the New Group and have been invited to join.</string>
|
||||
<string name="MessageRecord_chat_session_refreshed">Chat session refreshed</string>
|
||||
<plurals name="MessageRecord_members_couldnt_be_added_to_the_new_group_and_have_been_invited">
|
||||
<item quantity="one">A member couldn\'t be added to the New Group and has been invited to join.</item>
|
||||
<item quantity="other">%1$s members couldn\'t be added to the New Group and have been invited to join.</item>
|
||||
|
@ -1660,6 +1666,7 @@
|
|||
<string name="ThreadRecord_contact">Contact</string>
|
||||
<string name="ThreadRecord_file">File</string>
|
||||
<string name="ThreadRecord_video">Video</string>
|
||||
<string name="ThreadRecord_chat_session_refreshed">Chat session refreshed</string>
|
||||
|
||||
<!-- UpdateApkReadyListener -->
|
||||
<string name="UpdateApkReadyListener_Signal_update">Signal update</string>
|
||||
|
@ -1711,7 +1718,6 @@
|
|||
<string name="AudioView_duration" translatable="false">%1$d:%2$02d</string>
|
||||
|
||||
<!-- MessageDisplayHelper -->
|
||||
<string name="MessageDisplayHelper_bad_encrypted_message">Bad encrypted message</string>
|
||||
<string name="MessageDisplayHelper_message_encrypted_for_non_existing_session">Message encrypted for non-existing session</string>
|
||||
|
||||
<!-- MmsMessageRecord -->
|
||||
|
|
|
@ -275,6 +275,7 @@ public class SignalServiceMessageSender {
|
|||
sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false, null);
|
||||
}
|
||||
|
||||
// TODO [greyson][session] Delete this when we delete the button
|
||||
if (message.isEndSession()) {
|
||||
if (recipient.getUuid().isPresent()) {
|
||||
store.deleteAllSessions(recipient.getUuid().get().toString());
|
||||
|
@ -481,7 +482,6 @@ public class SignalServiceMessageSender {
|
|||
attachment.getUploadTimestamp());
|
||||
}
|
||||
|
||||
|
||||
private void sendMessage(VerifiedMessage message, Optional<UnidentifiedAccessPair> unidentifiedAccess)
|
||||
throws IOException, UntrustedIdentityException
|
||||
{
|
||||
|
@ -507,6 +507,26 @@ public class SignalServiceMessageSender {
|
|||
}
|
||||
}
|
||||
|
||||
public SendMessageResult sendNullMessage(SignalServiceAddress address, Optional<UnidentifiedAccessPair> unidentifiedAccess)
|
||||
throws UntrustedIdentityException, IOException
|
||||
{
|
||||
byte[] nullMessageBody = DataMessage.newBuilder()
|
||||
.setBody(Base64.encodeBytes(Util.getRandomLengthBytes(140)))
|
||||
.build()
|
||||
.toByteArray();
|
||||
|
||||
NullMessage nullMessage = NullMessage.newBuilder()
|
||||
.setPadding(ByteString.copyFrom(nullMessageBody))
|
||||
.build();
|
||||
|
||||
byte[] content = Content.newBuilder()
|
||||
.setNullMessage(nullMessage)
|
||||
.build()
|
||||
.toByteArray();
|
||||
|
||||
return sendMessage(address, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), content, false, null);
|
||||
}
|
||||
|
||||
private byte[] createTypingContent(SignalServiceTypingMessage message) {
|
||||
Content.Builder container = Content.newBuilder();
|
||||
TypingMessage.Builder builder = TypingMessage.newBuilder();
|
||||
|
|
Ładowanie…
Reference in New Issue