kopia lustrzana https://github.com/ryukoposting/Signal-Android
Migrate queued jobs during SMS migration.
rodzic
4d9dc42868
commit
c6f29fc950
|
@ -1093,7 +1093,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
|||
Runnable deleteForEveryone = () -> {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
for (MessageRecord message : messageRecords) {
|
||||
MessageSender.sendRemoteDelete(message.getId(), message.isMms());
|
||||
MessageSender.sendRemoteDelete(message.getId());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ import net.zetetic.database.sqlcipher.SQLiteDatabase
|
|||
import org.signal.core.util.SqlUtil
|
||||
import org.signal.core.util.Stopwatch
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
||||
object V168_SingleMessageTableMigration : SignalDatabaseMigration {
|
||||
private val TAG = Log.tag(V168_SingleMessageTableMigration::class.java)
|
||||
|
@ -140,6 +141,6 @@ object V168_SingleMessageTableMigration : SignalDatabaseMigration {
|
|||
|
||||
stopwatch.stop(TAG)
|
||||
|
||||
// TODO jobs?
|
||||
SignalStore.plaintext().smsMigrationIdOffset = nextMmsId
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,10 @@ public final class JobSpec {
|
|||
return new JobSpec(id, factoryKey, queueKey, createTime, updated, runAttempt, maxAttempts, lifespan, serializedData, serializedInputData, isRunning, memoryOnly);
|
||||
}
|
||||
|
||||
public @NonNull JobSpec withData(String updatedSerializedData) {
|
||||
return new JobSpec(id, factoryKey, queueKey, createTime, nextRunAttemptTime, runAttempt, maxAttempts, lifespan, updatedSerializedData, serializedInputData, isRunning, memoryOnly);
|
||||
}
|
||||
|
||||
public @NonNull String getId() {
|
||||
return id;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ import org.thoughtcrime.securesms.migrations.PniMigrationJob;
|
|||
import org.thoughtcrime.securesms.migrations.ProfileMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.ProfileSharingUpdateMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.RebuildMessageSearchIndexMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.UpdateSmsJobsMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.StickerAdditionMigrationJob;
|
||||
|
@ -233,6 +234,7 @@ public final class JobManagerFactories {
|
|||
put(StoryReadStateMigrationJob.KEY, new StoryReadStateMigrationJob.Factory());
|
||||
put(StoryViewedReceiptsStateMigrationJob.KEY, new StoryViewedReceiptsStateMigrationJob.Factory());
|
||||
put(TrimByLengthSettingsMigrationJob.KEY, new TrimByLengthSettingsMigrationJob.Factory());
|
||||
put(UpdateSmsJobsMigrationJob.KEY, new UpdateSmsJobsMigrationJob.Factory());
|
||||
put(UserNotificationMigrationJob.KEY, new UserNotificationMigrationJob.Factory());
|
||||
put(UuidMigrationJob.KEY, new UuidMigrationJob.Factory());
|
||||
|
||||
|
|
|
@ -45,22 +45,19 @@ public class RemoteDeleteSendJob extends BaseJob {
|
|||
private static final String TAG = Log.tag(RemoteDeleteSendJob.class);
|
||||
|
||||
private static final String KEY_MESSAGE_ID = "message_id";
|
||||
private static final String KEY_IS_MMS = "is_mms";
|
||||
private static final String KEY_RECIPIENTS = "recipients";
|
||||
private static final String KEY_INITIAL_RECIPIENT_COUNT = "initial_recipient_count";
|
||||
|
||||
private final long messageId;
|
||||
private final boolean isMms;
|
||||
private final List<RecipientId> recipients;
|
||||
private final int initialRecipientCount;
|
||||
|
||||
|
||||
@WorkerThread
|
||||
public static @NonNull JobManager.Chain create(long messageId, boolean isMms)
|
||||
public static @NonNull JobManager.Chain create(long messageId)
|
||||
throws NoSuchMessageException
|
||||
{
|
||||
MessageRecord message = isMms ? SignalDatabase.messages().getMessageRecord(messageId)
|
||||
: SignalDatabase.messages().getSmsMessage(messageId);
|
||||
MessageRecord message = SignalDatabase.messages().getMessageRecord(messageId);
|
||||
|
||||
Recipient conversationRecipient = SignalDatabase.threads().getRecipientForThreadId(message.getThreadId());
|
||||
|
||||
|
@ -82,7 +79,6 @@ public class RemoteDeleteSendJob extends BaseJob {
|
|||
recipients.remove(Recipient.self().getId());
|
||||
|
||||
RemoteDeleteSendJob sendJob = new RemoteDeleteSendJob(messageId,
|
||||
isMms,
|
||||
recipients,
|
||||
recipients.size(),
|
||||
new Parameters.Builder()
|
||||
|
@ -101,7 +97,6 @@ public class RemoteDeleteSendJob extends BaseJob {
|
|||
}
|
||||
|
||||
private RemoteDeleteSendJob(long messageId,
|
||||
boolean isMms,
|
||||
@NonNull List<RecipientId> recipients,
|
||||
int initialRecipientCount,
|
||||
@NonNull Parameters parameters)
|
||||
|
@ -109,7 +104,6 @@ public class RemoteDeleteSendJob extends BaseJob {
|
|||
super(parameters);
|
||||
|
||||
this.messageId = messageId;
|
||||
this.isMms = isMms;
|
||||
this.recipients = recipients;
|
||||
this.initialRecipientCount = initialRecipientCount;
|
||||
}
|
||||
|
@ -117,7 +111,6 @@ public class RemoteDeleteSendJob extends BaseJob {
|
|||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId)
|
||||
.putBoolean(KEY_IS_MMS, isMms)
|
||||
.putString(KEY_RECIPIENTS, RecipientId.toSerializedList(recipients))
|
||||
.putInt(KEY_INITIAL_RECIPIENT_COUNT, initialRecipientCount)
|
||||
.build();
|
||||
|
@ -134,18 +127,10 @@ public class RemoteDeleteSendJob extends BaseJob {
|
|||
throw new NotPushRegisteredException();
|
||||
}
|
||||
|
||||
MessageTable db;
|
||||
MessageRecord message;
|
||||
MessageTable db = SignalDatabase.messages();
|
||||
MessageRecord message = SignalDatabase.messages().getMessageRecord(messageId);
|
||||
|
||||
if (isMms) {
|
||||
db = SignalDatabase.messages();
|
||||
message = SignalDatabase.messages().getMessageRecord(messageId);
|
||||
} else {
|
||||
db = SignalDatabase.messages();
|
||||
message = SignalDatabase.messages().getSmsMessage(messageId);
|
||||
}
|
||||
|
||||
long targetSentTimestamp = message.getDateSent();
|
||||
long targetSentTimestamp = message.getDateSent();
|
||||
Recipient conversationRecipient = SignalDatabase.threads().getRecipientForThreadId(message.getThreadId());
|
||||
|
||||
if (conversationRecipient == null) {
|
||||
|
@ -182,7 +167,7 @@ public class RemoteDeleteSendJob extends BaseJob {
|
|||
|
||||
Log.i(TAG, "Completed now: " + sendResult.completed.size() + ", Skipped: " + totalSkips.size() + ", Remaining: " + recipients.size());
|
||||
|
||||
if (totalSkips.size() > 0 && isMms && message.getRecipient().isGroup()) {
|
||||
if (totalSkips.size() > 0 && message.getRecipient().isGroup()) {
|
||||
SignalDatabase.groupReceipts().setSkipped(totalSkips, messageId);
|
||||
}
|
||||
|
||||
|
@ -242,11 +227,10 @@ public class RemoteDeleteSendJob extends BaseJob {
|
|||
@Override
|
||||
public @NonNull RemoteDeleteSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
long messageId = data.getLong(KEY_MESSAGE_ID);
|
||||
boolean isMms = data.getBoolean(KEY_IS_MMS);
|
||||
List<RecipientId> recipients = RecipientId.fromSerializedList(data.getString(KEY_RECIPIENTS));
|
||||
int initialRecipientCount = data.getInt(KEY_INITIAL_RECIPIENT_COUNT);
|
||||
|
||||
return new RemoteDeleteSendJob(messageId, isMms, recipients, initialRecipientCount, parameters);
|
||||
return new RemoteDeleteSendJob(messageId, recipients, initialRecipientCount, parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package org.thoughtcrime.securesms.keyvalue
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
/**
|
||||
* There are some values that you can't, for whatever reason, store in the normal encrypted [KeyValueStore].
|
||||
* Usually, it's because the value your storing is _related to_ the database. Regardless, this is just a normal
|
||||
* shared-prefs-backed class. Do not put anything in here that you wouldn't be comfortable storing in plain text.
|
||||
*
|
||||
* A good rule of thumb might be: if you're not comforable logging it, then you shouldn't be comfortable putting
|
||||
* it in here.
|
||||
*/
|
||||
class PlainTextSharedPrefsDataStore(private val context: Context) {
|
||||
|
||||
companion object {
|
||||
const val SMS_MIGRATION_ID_OFFSET = "sms_migration_id_offset"
|
||||
}
|
||||
|
||||
private val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
/**
|
||||
* Stores the ID offset that was determined during the big migration that moved all SMS messages into the MMS table.
|
||||
*/
|
||||
var smsMigrationIdOffset: Long
|
||||
get() = sharedPrefs.getLong(SMS_MIGRATION_ID_OFFSET, -1)
|
||||
@SuppressLint("ApplySharedPref")
|
||||
set(value) {
|
||||
sharedPrefs.edit().putLong(SMS_MIGRATION_ID_OFFSET, value).commit()
|
||||
}
|
||||
}
|
|
@ -44,6 +44,8 @@ public final class SignalStore {
|
|||
private final ReleaseChannelValues releaseChannelValues;
|
||||
private final StoryValues storyValues;
|
||||
|
||||
private final PlainTextSharedPrefsDataStore plainTextValues;
|
||||
|
||||
private static volatile SignalStore instance;
|
||||
|
||||
private static @NonNull SignalStore getInstance() {
|
||||
|
@ -85,6 +87,7 @@ public final class SignalStore {
|
|||
this.notificationProfileValues = new NotificationProfileValues(store);
|
||||
this.releaseChannelValues = new ReleaseChannelValues(store);
|
||||
this.storyValues = new StoryValues(store);
|
||||
this.plainTextValues = new PlainTextSharedPrefsDataStore(ApplicationDependencies.getApplication());
|
||||
}
|
||||
|
||||
public static void onFirstEverAppLaunch() {
|
||||
|
@ -269,6 +272,10 @@ public final class SignalStore {
|
|||
return new SignalPreferenceDataStore(getStore());
|
||||
}
|
||||
|
||||
public static @NonNull PlainTextSharedPrefsDataStore plaintext() {
|
||||
return getInstance().plainTextValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures any pending writes are finished. Only intended to be called by
|
||||
* {@link SignalUncaughtExceptionHandler}.
|
||||
|
|
|
@ -84,7 +84,7 @@ class MediaPreviewRepository {
|
|||
|
||||
fun remoteDelete(attachment: DatabaseAttachment): Completable {
|
||||
return Completable.fromRunnable {
|
||||
MessageSender.sendRemoteDelete(attachment.mmsId, true)
|
||||
MessageSender.sendRemoteDelete(attachment.mmsId)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
|
|
|
@ -114,9 +114,10 @@ public class ApplicationMigrations {
|
|||
static final int THREAD_MESSAGE_SCHEMA_CHANGE = 70;
|
||||
static final int SMS_MMS_MERGE = 71;
|
||||
static final int REBUILD_MESSAGE_FTS_INDEX = 72;
|
||||
static final int UPDATE_SMS_JOBS = 73;
|
||||
}
|
||||
|
||||
public static final int CURRENT_VERSION = 72;
|
||||
public static final int CURRENT_VERSION = 73;
|
||||
|
||||
/**
|
||||
* This *must* be called after the {@link JobManager} has been instantiated, but *before* the call
|
||||
|
@ -506,6 +507,10 @@ public class ApplicationMigrations {
|
|||
jobs.put(Version.REBUILD_MESSAGE_FTS_INDEX, new RebuildMessageSearchIndexMigrationJob());
|
||||
}
|
||||
|
||||
if (lastSeenVersion < Version.UPDATE_SMS_JOBS) {
|
||||
jobs.put(Version.UPDATE_SMS_JOBS, new UpdateSmsJobsMigrationJob());
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package org.thoughtcrime.securesms.migrations
|
||||
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.Data
|
||||
import org.thoughtcrime.securesms.jobmanager.Data.Serializer
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
||||
/**
|
||||
* Updates the data in queued jobs to reflect the new ids SMS messages get assigned during the table merge migration.
|
||||
* Normally we'd do this in a JobManager migration, but unfortunately this migration requires that a database migration
|
||||
* happened already, but we don't want the database to be accessed until the [DatabaseMigrationJob] is run, otherwise
|
||||
* we won't show the progress update.
|
||||
*
|
||||
* This ends up being more straightforward regardless because by the time this application migration is being run, it must be the
|
||||
* case that the database migration is finished (since it's enqueued after the [DatabaseMigrationJob]), so we don't have to
|
||||
* do any weird wait-notify stuff to guarantee the offset is set.
|
||||
*/
|
||||
internal class UpdateSmsJobsMigrationJob(
|
||||
parameters: Parameters = Parameters.Builder().build()
|
||||
) : MigrationJob(parameters) {
|
||||
|
||||
companion object {
|
||||
val TAG = Log.tag(UpdateSmsJobsMigrationJob::class.java)
|
||||
const val KEY = "UpdateSmsJobsMigrationJob"
|
||||
}
|
||||
|
||||
override fun getFactoryKey(): String = KEY
|
||||
|
||||
override fun isUiBlocking(): Boolean = false
|
||||
|
||||
override fun performMigration() {
|
||||
val idOffset = SignalStore.plaintext().smsMigrationIdOffset
|
||||
check(idOffset >= 0) { "Invalid ID offset of $idOffset -- this shouldn't be possible!" }
|
||||
|
||||
ApplicationDependencies.getJobManager().update { jobSpec, serializer ->
|
||||
when (jobSpec.factoryKey) {
|
||||
"PushTextSendJob" -> jobSpec.updateAndSerialize(serializer, "message_id", null, idOffset)
|
||||
"ReactionSendJob" -> jobSpec.updateAndSerialize(serializer, "message_id", "is_mms", idOffset)
|
||||
"RemoteDeleteSendJob" -> jobSpec.updateAndSerialize(serializer, "message_id", "is_mms", idOffset)
|
||||
"SmsSendJob" -> jobSpec.updateAndSerialize(serializer, "message_id", null, idOffset)
|
||||
"SmsSentJob" -> jobSpec.updateAndSerialize(serializer, "message_id", null, idOffset)
|
||||
else -> jobSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JobSpec.updateAndSerialize(serializer: Serializer, idKey: String, isMmsKey: String?, offset: Long): JobSpec {
|
||||
val data = serializer.deserialize(this.serializedData)
|
||||
|
||||
if (isMmsKey != null && data.getBooleanOrDefault(isMmsKey, false)) {
|
||||
return this
|
||||
}
|
||||
|
||||
return if (data.hasLong(idKey)) {
|
||||
val currentValue: Long = data.getLong(idKey)
|
||||
val updatedValue: Long = currentValue + offset
|
||||
val updatedData: Data = data.buildUpon().putLong(idKey, updatedValue).build()
|
||||
|
||||
Log.d(TAG, "Updating job with factory ${this.factoryKey} from $currentValue to $updatedValue")
|
||||
this.withData(serializer.serialize(updatedData))
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldRetry(e: Exception): Boolean = false
|
||||
|
||||
class Factory : Job.Factory<UpdateSmsJobsMigrationJob> {
|
||||
override fun create(parameters: Parameters, data: Data): UpdateSmsJobsMigrationJob {
|
||||
return UpdateSmsJobsMigrationJob(parameters)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -466,13 +466,13 @@ public class MessageSender {
|
|||
}
|
||||
}
|
||||
|
||||
public static void sendRemoteDelete(long messageId, boolean isMms) {
|
||||
MessageTable db = isMms ? SignalDatabase.messages() : SignalDatabase.messages();
|
||||
public static void sendRemoteDelete(long messageId) {
|
||||
MessageTable db = SignalDatabase.messages();
|
||||
db.markAsRemoteDelete(messageId);
|
||||
db.markAsSending(messageId);
|
||||
|
||||
try {
|
||||
RemoteDeleteSendJob.create(messageId, isMms).enqueue();
|
||||
RemoteDeleteSendJob.create(messageId).enqueue();
|
||||
onMessageSent();
|
||||
} catch (NoSuchMessageException e) {
|
||||
Log.w(TAG, "[sendRemoteDelete] Could not find message! Ignoring.");
|
||||
|
|
|
@ -32,7 +32,7 @@ class PrivateStorySettingsRepository {
|
|||
val recipientId = SignalDatabase.recipients.getOrInsertFromDistributionListId(distributionListId)
|
||||
SignalDatabase.messages.getAllStoriesFor(recipientId, -1).use { reader ->
|
||||
for (record in reader) {
|
||||
MessageSender.sendRemoteDelete(record.id, record.isMms)
|
||||
MessageSender.sendRemoteDelete(record.id)
|
||||
}
|
||||
}
|
||||
}.subscribeOn(Schedulers.io())
|
||||
|
|
|
@ -32,7 +32,7 @@ class StoriesPrivacySettingsRepository {
|
|||
SignalDatabase.messages.getAllOutgoingStories(false, -1).use { reader ->
|
||||
reader.map { record -> record.id }
|
||||
}.forEach { messageId ->
|
||||
MessageSender.sendRemoteDelete(messageId, true)
|
||||
MessageSender.sendRemoteDelete(messageId)
|
||||
}
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ object DeleteDialog {
|
|||
private fun deleteForEveryone(messageRecords: Set<MessageRecord>, emitter: SingleEmitter<Boolean>) {
|
||||
SignalExecutors.BOUNDED.execute {
|
||||
messageRecords.forEach { message ->
|
||||
MessageSender.sendRemoteDelete(message.id, message.isMms)
|
||||
MessageSender.sendRemoteDelete(message.id)
|
||||
}
|
||||
|
||||
emitter.onSuccess(false)
|
||||
|
|
Ładowanie…
Reference in New Issue