mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-23 02:10:44 +01:00
Improve and centralize e164 utils.
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.jobmanager.migrations
|
||||
|
||||
import org.thoughtcrime.securesms.jobmanager.JobMigration
|
||||
|
||||
/**
|
||||
* Used as a replacement for another JobMigration that is no longer necessary.
|
||||
*/
|
||||
class DeprecatedJobMigration(version: Int) : JobMigration(version) {
|
||||
override fun migrate(jobData: JobData): JobData = jobData
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package org.thoughtcrime.securesms.jobmanager.migrations;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobMigration;
|
||||
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.signal.core.util.Base64;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* We changed the format of the queue key for legacy PushProcessMessageJob
|
||||
* to have the recipient ID in it, so this migrates existing jobs to be in that format.
|
||||
*/
|
||||
public class PushProcessMessageQueueJobMigration extends JobMigration {
|
||||
|
||||
private static final String TAG = Log.tag(PushProcessMessageQueueJobMigration.class);
|
||||
|
||||
private final Context context;
|
||||
|
||||
public PushProcessMessageQueueJobMigration(@NonNull Context context) {
|
||||
super(6);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull JobData migrate(@NonNull JobData jobData) {
|
||||
if ("PushProcessJob".equals(jobData.getFactoryKey())) {
|
||||
Log.i(TAG, "Found a PushProcessMessageJob to migrate.");
|
||||
try {
|
||||
return migratePushProcessMessageJob(context, jobData);
|
||||
} catch (IOException | InvalidInputException e) {
|
||||
Log.w(TAG, "Failed to migrate message job.", e);
|
||||
return jobData;
|
||||
}
|
||||
}
|
||||
return jobData;
|
||||
}
|
||||
|
||||
private static @NonNull JobData migratePushProcessMessageJob(@NonNull Context context, @NonNull JobData jobData) throws IOException, InvalidInputException {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
String suffix = "";
|
||||
|
||||
if (data.getInt("message_state") == 0) {
|
||||
SignalServiceContentProto proto = SignalServiceContentProto.ADAPTER.decode(Base64.decode(data.getString("message_content")));
|
||||
|
||||
if (proto != null && proto.content != null && proto.content.dataMessage != null && proto.content.dataMessage.groupV2 != null) {
|
||||
Log.i(TAG, "Migrating a group message.");
|
||||
|
||||
GroupId groupId = GroupId.v2(new GroupMasterKey(proto.content.dataMessage.groupV2.masterKey.toByteArray()));
|
||||
Recipient recipient = Recipient.externalGroupExact(groupId);
|
||||
|
||||
suffix = recipient.getId().toQueueKey();
|
||||
} else if (proto != null && proto.metadata != null && proto.metadata.address != null) {
|
||||
Log.i(TAG, "Migrating an individual message.");
|
||||
ServiceId senderServiceId = ServiceId.parseOrThrow(proto.metadata.address.uuid);
|
||||
String senderE164 = proto.metadata.address.e164;
|
||||
SignalServiceAddress sender = new SignalServiceAddress(senderServiceId, Optional.ofNullable(senderE164));
|
||||
|
||||
suffix = RecipientId.from(sender).toQueueKey();
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "Migrating an exception message.");
|
||||
|
||||
String exceptionSender = data.getString("exception_sender");
|
||||
GroupId exceptionGroup = GroupId.parseNullableOrThrow(data.getStringOrDefault("exception_groupId", null));
|
||||
|
||||
if (exceptionGroup != null) {
|
||||
suffix = Recipient.externalGroupExact(exceptionGroup).getId().toQueueKey();
|
||||
} else if (exceptionSender != null) {
|
||||
suffix = Recipient.external(context, exceptionSender).getId().toQueueKey();
|
||||
}
|
||||
}
|
||||
|
||||
return jobData.withQueueKey("__PUSH_PROCESS_JOB__" + suffix);
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package org.thoughtcrime.securesms.jobmanager.migrations;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobMigration;
|
||||
|
||||
/**
|
||||
* Fixes things that went wrong in {@link RecipientIdJobMigration}. In particular, some jobs didn't
|
||||
* have some necessary data fields carried over. Thankfully they're relatively non-critical, so
|
||||
* we'll just swap them out with failing jobs if they're missing something.
|
||||
*/
|
||||
public class RecipientIdFollowUpJobMigration extends JobMigration {
|
||||
|
||||
public RecipientIdFollowUpJobMigration() {
|
||||
this(3);
|
||||
}
|
||||
|
||||
RecipientIdFollowUpJobMigration(int endVersion) {
|
||||
super(endVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull JobData migrate(@NonNull JobData jobData) {
|
||||
switch(jobData.getFactoryKey()) {
|
||||
case "RequestGroupInfoJob": return migrateRequestGroupInfoJob(jobData);
|
||||
case "SendDeliveryReceiptJob": return migrateSendDeliveryReceiptJob(jobData);
|
||||
default:
|
||||
return jobData;
|
||||
}
|
||||
}
|
||||
|
||||
private static @NonNull JobData migrateRequestGroupInfoJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
if (!data.hasString("source") || !data.hasString("group_id")) {
|
||||
return failingJobData();
|
||||
} else {
|
||||
return jobData;
|
||||
}
|
||||
}
|
||||
|
||||
private static @NonNull JobData migrateSendDeliveryReceiptJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
if (!data.hasString("recipient") ||
|
||||
!data.hasLong("message_id") ||
|
||||
!data.hasLong("timestamp"))
|
||||
{
|
||||
return failingJobData();
|
||||
} else {
|
||||
return jobData;
|
||||
}
|
||||
}
|
||||
|
||||
private static JobData failingJobData() {
|
||||
return JobData.FAILING_JOB_DATA;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.thoughtcrime.securesms.jobmanager.migrations;
|
||||
|
||||
/**
|
||||
* Unfortunately there was a bug in {@link RecipientIdFollowUpJobMigration} that requires it to be
|
||||
* run again.
|
||||
*/
|
||||
public class RecipientIdFollowUpJobMigration2 extends RecipientIdFollowUpJobMigration {
|
||||
public RecipientIdFollowUpJobMigration2() {
|
||||
super(4);
|
||||
}
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
package org.thoughtcrime.securesms.jobmanager.migrations;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobMigration;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class RecipientIdJobMigration extends JobMigration {
|
||||
|
||||
private final Application application;
|
||||
|
||||
public RecipientIdJobMigration(@NonNull Application application) {
|
||||
super(2);
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull JobData migrate(@NonNull JobData jobData) {
|
||||
switch(jobData.getFactoryKey()) {
|
||||
case "MultiDeviceContactUpdateJob": return migrateMultiDeviceContactUpdateJob(jobData);
|
||||
case "MultiDeviceRevealUpdateJob": return migrateMultiDeviceViewOnceOpenJob(jobData);
|
||||
case "RequestGroupInfoJob": return migrateRequestGroupInfoJob(jobData);
|
||||
case "SendDeliveryReceiptJob": return migrateSendDeliveryReceiptJob(jobData);
|
||||
case "MultiDeviceVerifiedUpdateJob": return migrateMultiDeviceVerifiedUpdateJob(jobData);
|
||||
case "RetrieveProfileJob": return migrateRetrieveProfileJob(jobData);
|
||||
case "PushGroupSendJob": return migratePushGroupSendJob(jobData);
|
||||
case "PushGroupUpdateJob": return migratePushGroupUpdateJob(jobData);
|
||||
case "DirectoryRefreshJob": return migrateDirectoryRefreshJob(jobData);
|
||||
case "RetrieveProfileAvatarJob": return migrateRetrieveProfileAvatarJob(jobData);
|
||||
case "MultiDeviceReadUpdateJob": return migrateMultiDeviceReadUpdateJob(jobData);
|
||||
case "PushTextSendJob": return migratePushTextSendJob(jobData);
|
||||
case "PushMediaSendJob": return migratePushMediaSendJob(jobData);
|
||||
case "SmsSendJob": return migrateSmsSendJob(jobData);
|
||||
default: return jobData;
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateMultiDeviceContactUpdateJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
String address = data.hasString("address") ? data.getString("address") : null;
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("recipient", address != null ? Recipient.external(application, address).getId().serialize() : null)
|
||||
.putBoolean("force_sync", data.getBoolean("force_sync"))
|
||||
.build();
|
||||
|
||||
return jobData.withData(updatedData.serialize());
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateMultiDeviceViewOnceOpenJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
try {
|
||||
String rawOld = data.getString("message_id");
|
||||
OldSerializableSyncMessageId old = JsonUtils.fromJson(rawOld, OldSerializableSyncMessageId.class);
|
||||
Recipient recipient = Recipient.external(application, old.sender);
|
||||
NewSerializableSyncMessageId updated = new NewSerializableSyncMessageId(recipient.getId().serialize(), old.timestamp);
|
||||
String rawUpdated = JsonUtils.toJson(updated);
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("message_id", rawUpdated).build();
|
||||
|
||||
return jobData.withData(updatedData.serialize());
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateRequestGroupInfoJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
String address = data.getString("source");
|
||||
Recipient recipient = Recipient.external(application, address);
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("source", recipient.getId().serialize())
|
||||
.putString("group_id", data.getString("group_id"))
|
||||
.build();
|
||||
|
||||
return jobData.withData(updatedData.serialize());
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateSendDeliveryReceiptJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
String address = data.getString("address");
|
||||
Recipient recipient = Recipient.external(application, address);
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("recipient", recipient.getId().serialize())
|
||||
.putLong("message_id", data.getLong("message_id"))
|
||||
.putLong("timestamp", data.getLong("timestamp"))
|
||||
.build();
|
||||
|
||||
return jobData.withData(updatedData.serialize());
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateMultiDeviceVerifiedUpdateJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
String address = data.getString("destination");
|
||||
Recipient recipient = Recipient.external(application, address);
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("destination", recipient.getId().serialize())
|
||||
.putString("identity_key", data.getString("identity_key"))
|
||||
.putInt("verified_status", data.getInt("verified_status"))
|
||||
.putLong("timestamp", data.getLong("timestamp"))
|
||||
.build();
|
||||
|
||||
return jobData.withData(updatedData.serialize());
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateRetrieveProfileJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
String address = data.getString("address");
|
||||
Recipient recipient = Recipient.external(application, address);
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("recipient", recipient.getId().serialize()).build();
|
||||
|
||||
return jobData.withData(updatedData.serialize());
|
||||
}
|
||||
|
||||
private @NonNull JobData migratePushGroupSendJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
// noinspection ConstantConditions
|
||||
Recipient queueRecipient = Recipient.external(application, jobData.getQueueKey());
|
||||
String address = data.hasString("filter_address") ? data.getString("filter_address") : null;
|
||||
RecipientId recipientId = address != null ? Recipient.external(application, address).getId() : null;
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("filter_recipient", recipientId != null ? recipientId.serialize() : null)
|
||||
.putLong("message_id", data.getLong("message_id"))
|
||||
.build();
|
||||
|
||||
return jobData.withQueueKey(queueRecipient.getId().toQueueKey())
|
||||
.withData(updatedData.serialize());
|
||||
}
|
||||
|
||||
private @NonNull JobData migratePushGroupUpdateJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
String address = data.getString("source");
|
||||
Recipient recipient = Recipient.external(application, address);
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("source", recipient.getId().serialize())
|
||||
.putString("group_id", data.getString("group_id"))
|
||||
.build();
|
||||
|
||||
return jobData.withData(updatedData.serialize());
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateDirectoryRefreshJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
String address = data.hasString("address") ? data.getString("address") : null;
|
||||
Recipient recipient = address != null ? Recipient.external(application, address) : null;
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("recipient", recipient != null ? recipient.getId().serialize() : null)
|
||||
.putBoolean("notify_of_new_users", data.getBoolean("notify_of_new_users"))
|
||||
.build();
|
||||
|
||||
return jobData.withData(updatedData.serialize());
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateRetrieveProfileAvatarJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
//noinspection ConstantConditions
|
||||
String queueAddress = jobData.getQueueKey().substring("RetrieveProfileAvatarJob".length());
|
||||
Recipient queueRecipient = Recipient.external(application, queueAddress);
|
||||
String address = data.getString("address");
|
||||
Recipient recipient = Recipient.external(application, address);
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putString("recipient", recipient.getId().serialize())
|
||||
.putString("profile_avatar", data.getString("profile_avatar"))
|
||||
.build();
|
||||
|
||||
return jobData.withQueueKey("RetrieveProfileAvatarJob::" + queueRecipient.getId().toQueueKey())
|
||||
.withData(updatedData.serialize());
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateMultiDeviceReadUpdateJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
try {
|
||||
String[] rawOld = data.getStringArray("message_ids");
|
||||
String[] rawUpdated = new String[rawOld.length];
|
||||
|
||||
for (int i = 0; i < rawOld.length; i++) {
|
||||
OldSerializableSyncMessageId old = JsonUtils.fromJson(rawOld[i], OldSerializableSyncMessageId.class);
|
||||
Recipient recipient = Recipient.external(application, old.sender);
|
||||
NewSerializableSyncMessageId updated = new NewSerializableSyncMessageId(recipient.getId().serialize(), old.timestamp);
|
||||
|
||||
rawUpdated[i] = JsonUtils.toJson(updated);
|
||||
}
|
||||
|
||||
JsonJobData updatedData = new JsonJobData.Builder().putStringArray("message_ids", rawUpdated).build();
|
||||
|
||||
return jobData.withData(updatedData.serialize());
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull JobData migratePushTextSendJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
//noinspection ConstantConditions
|
||||
Recipient recipient = Recipient.external(application, jobData.getQueueKey());
|
||||
return jobData.withQueueKey(recipient.getId().toQueueKey());
|
||||
}
|
||||
|
||||
private @NonNull JobData migratePushMediaSendJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
//noinspection ConstantConditions
|
||||
Recipient recipient = Recipient.external(application, jobData.getQueueKey());
|
||||
return jobData.withQueueKey(recipient.getId().toQueueKey());
|
||||
}
|
||||
|
||||
private @NonNull JobData migrateSmsSendJob(@NonNull JobData jobData) {
|
||||
JsonJobData data = JsonJobData.deserialize(jobData.getData());
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if (jobData.getQueueKey() != null) {
|
||||
Recipient recipient = Recipient.external(application, jobData.getQueueKey());
|
||||
return jobData.withQueueKey(recipient.getId().toQueueKey());
|
||||
} else {
|
||||
return jobData;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class OldSerializableSyncMessageId implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonProperty
|
||||
private final String sender;
|
||||
@JsonProperty
|
||||
private final long timestamp;
|
||||
|
||||
OldSerializableSyncMessageId(@JsonProperty("sender") String sender, @JsonProperty("timestamp") long timestamp) {
|
||||
this.sender = sender;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class NewSerializableSyncMessageId implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonProperty
|
||||
private final String recipientId;
|
||||
@JsonProperty
|
||||
private final long timestamp;
|
||||
|
||||
NewSerializableSyncMessageId(@JsonProperty("recipientId") String recipientId, @JsonProperty("timestamp") long timestamp) {
|
||||
this.recipientId = recipientId;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user