Decrypt and process messages all in one transaction.

Giddy up
This commit is contained in:
Greyson Parrelli
2021-02-23 18:34:18 -05:00
committed by GitHub
parent d651716d99
commit 8950100bd7
21 changed files with 2523 additions and 2008 deletions

View File

@@ -5,6 +5,8 @@ import androidx.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.util.Base64;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -65,6 +67,11 @@ public class Data {
return strings.get(key);
}
public byte[] getStringAsBlob(@NonNull String key) {
throwIfAbsent(strings, key);
return Base64.decodeOrThrow(strings.get(key));
}
public String getStringOrDefault(@NonNull String key, String defaultValue) {
if (hasString(key)) return getString(key);
else return defaultValue;
@@ -349,6 +356,12 @@ public class Data {
return this;
}
public Builder putBlobAsString(@NonNull String key, @NonNull byte[] value) {
String serialized = Base64.encodeBytes(value);
strings.put(key, serialized);
return this;
}
public Data build() {
return new Data(strings,
stringArrays,

View File

@@ -316,6 +316,10 @@ class JobController {
return info.toString();
}
synchronized boolean areQueuesEmpty(@NonNull Set<String> queueKeys) {
return jobStorage.areQueuesEmpty(queueKeys);
}
@WorkerThread
private boolean chainExceedsMaximumInstances(@NonNull List<List<Job>> chain) {
if (chain.size() == 1 && chain.get(0).size() == 1) {

View File

@@ -42,7 +42,7 @@ public class JobManager implements ConstraintObserver.Notifier {
private static final String TAG = JobManager.class.getSimpleName();
public static final int CURRENT_VERSION = 7;
public static final int CURRENT_VERSION = 8;
private final Application application;
private final Configuration configuration;
@@ -331,6 +331,31 @@ public class JobManager implements ConstraintObserver.Notifier {
}
}
/**
* Can tell you if a queue is empty at the time of invocation. It is worth noting that the state
* of the queue could change immediately after this method returns due to a call on some other
* thread, and you should take that into consideration when using the result. If you want
* something to happen within a queue, the safest course of action will always be to create a
* job and place it in that queue.
*
* @return True if requested queue is empty at the time of invocation, otherwise false.
*/
@WorkerThread
public boolean isQueueEmpty(@NonNull String queueKey) {
return areQueuesEmpty(Collections.singleton(queueKey));
}
/**
* See {@link #isQueueEmpty(String)}
*
* @return True if *all* requested queues are empty at the time of invocation, otherwise false.
*/
@WorkerThread
public boolean areQueuesEmpty(@NonNull Set<String> queueKeys) {
waitUntilInitialized();
return jobController.areQueuesEmpty(queueKeys);
}
/**
* Pokes the system to take another pass at the job queue.
*/

View File

@@ -0,0 +1,70 @@
package org.thoughtcrime.securesms.jobmanager.migrations;
import android.content.Context;
import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.groups.BadGroupIdException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.JobMigration;
import org.thoughtcrime.securesms.jobs.FailingJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import java.io.IOException;
/**
* We removed the messageId property from the job data and replaced it with a serialized envelope,
* so we need to take jobs that referenced an ID and replace it with the envelope instead.
*/
public class PushDecryptMessageJobEnvelopeMigration extends JobMigration {
private static final String TAG = Log.tag(PushDecryptMessageJobEnvelopeMigration.class);
private final PushDatabase pushDatabase;
public PushDecryptMessageJobEnvelopeMigration(@NonNull Context context) {
super(8);
this.pushDatabase = DatabaseFactory.getPushDatabase(context);
}
@Override
protected @NonNull JobData migrate(@NonNull JobData jobData) {
if ("PushDecryptJob".equals(jobData.getFactoryKey())) {
Log.i(TAG, "Found a PushDecryptJob to migrate.");
return migratePushDecryptMessageJob(pushDatabase, jobData);
} else {
return jobData;
}
}
private static @NonNull JobData migratePushDecryptMessageJob(@NonNull PushDatabase pushDatabase, @NonNull JobData jobData) {
Data data = jobData.getData();
if (data.hasLong("message_id")) {
long messageId = data.getLong("message_id");
try {
SignalServiceEnvelope envelope = pushDatabase.get(messageId);
return jobData.withData(jobData.getData()
.buildUpon()
.putBlobAsString("envelope", envelope.serialize())
.build());
} catch (NoSuchMessageException e) {
Log.w(TAG, "Failed to find envelope in DB! Failing.");
return jobData.withFactoryKey(FailingJob.KEY);
}
} else {
Log.w(TAG, "No message_id property?");
return jobData;
}
}
}

View File

@@ -4,7 +4,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public interface JobStorage {
@@ -32,6 +34,9 @@ public interface JobStorage {
@WorkerThread
int getJobCountForFactoryAndQueue(@NonNull String factoryKey, @NonNull String queueKey);
@WorkerThread
boolean areQueuesEmpty(@NonNull Set<String> queueKeys);
@WorkerThread
void updateJobRunningState(@NonNull String id, boolean isRunning);