Schedule jobs with WorkManager.

Should help solve most of our pressing targetSdk=26 migration issues.
This commit is contained in:
Greyson Parrelli 2018-08-09 10:15:43 -04:00
parent d10a44f8eb
commit 87e6aa48bb
55 changed files with 1442 additions and 1192 deletions

View File

@ -541,8 +541,6 @@
<receiver android:name=".service.ExpirationListener" />
<receiver android:name=".jobmanager.requirements.BackoffReceiver" />
<provider android:name=".providers.PartProvider"
android:grantUriPermissions="true"
android:exported="false"

View File

@ -57,18 +57,21 @@ repositories {
}
dependencies {
compile 'com.android.support:appcompat-v7:27.0.2'
compile 'com.android.support:recyclerview-v7:27.0.2'
compile 'com.android.support:design:27.0.2'
compile 'com.android.support:support-v13:27.0.2'
compile 'com.android.support:cardview-v7:27.0.2'
compile 'com.android.support:preference-v7:27.0.2'
compile 'com.android.support:preference-v14:27.0.2'
compile 'com.android.support:gridlayout-v7:27.0.2'
compile 'com.android.support:multidex:1.0.2'
compile 'com.android.support:exifinterface:27.0.2'
def supportVersion = '28.0.0'
compile "com.android.support:appcompat-v7:$supportVersion"
compile "com.android.support:recyclerview-v7:$supportVersion"
compile "com.android.support:design:$supportVersion"
compile "com.android.support:support-v13:$supportVersion"
compile "com.android.support:cardview-v7:$supportVersion"
compile "com.android.support:preference-v7:$supportVersion"
compile "com.android.support:preference-v14:$supportVersion"
compile "com.android.support:gridlayout-v7:$supportVersion"
compile "com.android.support:exifinterface:$supportVersion"
compile 'com.android.support:multidex:1.0.3'
compile 'android.arch.lifecycle:extensions:1.1.1'
compile 'android.arch.lifecycle:common-java8:1.1.1'
compile 'android.arch.work:work-runtime:1.0.0-alpha09'
compile 'com.google.android.gms:play-services-gcm:9.6.1'
compile 'com.google.android.gms:play-services-maps:9.6.1'
@ -134,8 +137,8 @@ dependencies {
testCompile 'org.powermock:powermock-module-junit4-rule:1.6.1'
testCompile 'org.powermock:powermock-classloading-xstream:1.6.1'
androidTestCompile 'com.android.support:multidex:1.0.2'
androidTestCompile 'com.android.support:multidex-instrumentation:1.0.2'
androidTestCompile 'com.android.support:multidex:1.0.3'
androidTestCompile 'com.android.support:multidex-instrumentation:1.0.3'
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
androidTestCompile ('org.assertj:assertj-core:1.7.1') {
@ -149,21 +152,21 @@ dependencies {
dependencyVerification {
verify = [
'com.android.support:design:fa5c27a705310e95a8f4099c98777132ed901a0d69178942306bb34cd76f0d57',
'com.android.support:preference-v14:1fcf40bd560dc21ce55b6a292d1326d28f52dc5e4f2a64eef2f38dd95777b645',
'com.android.support:preference-v7:d9f32ddc92f8d6a0bd86a18f4fcaff805fa021245e395b6e7dd382907443ee53',
'com.android.support:design:7874ad1904eedc74aa41cffffb7f759d8990056f3bbbc9264911651c67c42f5f',
'com.android.support:preference-v14:8133c6e19233fa51e036a341e6d3f4adeead3375cebf777efced0fe154c3267e',
'com.android.support:preference-v7:75eabe936d1fc3b178450a554c4d433466036f2be6d6dccdf971eac9590fdbf5',
'com.pnikosis:materialish-progress:d71d80e00717a096784482aee21001a9d299fec3833e4ebd87739ed36cf77c54',
'pl.tajchert:waitingdots:2835d49e0787dbcb606c5a60021ced66578503b1e9fddcd7a5ef0cd5f095ba2c',
'mobi.upod:time-duration-picker:db469ce0f48dd96b892eac424ed76870e54bf00fe0a28cdcddfbe5f2a226a0e1',
'com.codewaves.stickyheadergrid:stickyheadergrid:5b4aa6a52a957cfd55f60f4220c11c0c371385a3cb9786cae03c260dcdef5794',
'com.android.support:appcompat-v7:b2825e8b47f665d3362d8481c8d147d1af9230d16f23a2b94f6ccbc53c68cec1',
'com.android.support:appcompat-v7:a3a8e5230359746ed91801579b5fbe4668e3b1c4e6a14c7d67c8f58cb0311752',
'com.melnykov:floatingactionbutton:15d58d4fac0f7a288d0e5301bbaf501a146f5b3f5921277811bf99bd3b397263',
'com.android.support:recyclerview-v7:3eb953930f10941f2b0447ec123a9b03d2746a42a99c523e82c3ece3308ca70b',
'com.android.support:support-v13:6f2848811ceef2e32cc98da02a045d65f7e7447a6bd7198b9ec10aa14d7ad55c',
'com.android.support:cardview-v7:57f867a3c8f33e2d4dc0a03e2dfa03cad6267a908179f04a725a68ea9f0b8ccf',
'com.android.support:gridlayout-v7:227b5fdffa20f53bd562503aab6d2293d52cf64b5a6ab1116d2150f87bff9e88',
'com.android.support:multidex:7cd48755c7cfdb6dd2d21cbb02236ec390f6ac91cde87eb62f475b259ab5301d',
'com.android.support:exifinterface:0e7cd526c4468895cd8549def46b3d33c8bcfb1ae4830569898d8c7326b15bb2',
'com.android.support:recyclerview-v7:eb296414c1f6d4c7b522f69fe50588ea85297855db0e7806c24eb4f75409587d',
'com.android.support:support-v13:491f940c5d6d2ec7678fa2f14bd4bbbe8bf776e2c776d04bf0e5c2175975be43',
'com.android.support:cardview-v7:bc9e6b0e06ce1205f1db34f0e6193019613d19cfeb54cdccea722340d1c60f26',
'com.android.support:gridlayout-v7:5029529f7db66f8773426bf7318645f0840fc50d74f66355cd60c5e58d2da087',
'com.android.support:exifinterface:bbf44e519edd6333a24a3285aa21fd00181b920b81ca8aa89a8899f03ab4d6b0',
'android.arch.work:work-runtime:eda29b2cad202dee05a2e5aafe0a37c93ba9cde8f7cc0d0c8926a9f1a9498a8f',
'android.arch.lifecycle:extensions:429426b2feec2245ffc5e75b3b5309bedb36159cf06dc71843ae43526ac289b6',
'android.arch.lifecycle:common-java8:7078b5c8ccb94203df9cc2a463c69cf0021596e6cf966d78fbfd697aaafe0630',
'com.google.android.gms:play-services-gcm:312e61253a236f2d9b750b9c04fc92fd190d23b0b2755c99de6ce4a28b259dae',
@ -199,24 +202,44 @@ dependencyVerification {
'com.google.android.gms:play-services-base:0ca636a8fc9a5af45e607cdcd61783bf5d561cbbb0f862021ce69606eee5ad49',
'com.google.android.gms:play-services-tasks:69ec265168e601d0203d04cd42e34bb019b2f029aa1e16fabd38a5153eea2086',
'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d',
'com.android.support:support-v4:1b2b37169fcccfef5e563d273749e3792decdce9818bc17932403a2363f537b4',
'com.android.support:support-fragment:e4358388022a2205777575a7251fe357334658e4123d5d6e3b082f5899d9b011',
'com.android.support:animated-vector-drawable:5b117a2c13a898c2a3c84c480d64edcfac2ef720aa9b742c29249fac774ffc48',
'com.android.support:support-core-ui:2284072511a95d504c074de80c82cd33724c6d2754117833b98ba3a09994163e',
'com.android.support:transition:1a7db0453c1467fc8fd815e6d50ca6bb475a7a9ba6b5f3b307329688a7c62a68',
'com.android.support:support-v4:8b9031381c678d628c9e47b566ae1d161e1c9710f7855c759beeac7596cecf30',
'com.android.support:support-fragment:3772fc738ada86824ba1a4b3f197c3dbd67b7ddcfe2c9db1de95ef2e3487a915',
'com.android.support:animated-vector-drawable:271ecbc906cda8dcd9e655ba0473129c3408a4189c806f616c378e6fd18fb3b7',
'com.android.support:support-core-ui:bbc7f65fc95649464733af373361532ab5f9f3b749c3badaa2bbf27e574b6c6f',
'com.android.support:transition:45d09fc51284c17bbab300f5122512ac7d7348a6d23bda2051648bbe76cc9aa5',
'com.android.support:viewpager:013c4c53058758ec104dbae970be58159f75dfe342ba8b937d15ff5282e35ffc',
'com.android.support:coordinatorlayout:9dfacd80423dc979048fbaed83c0ee543c46259feb2417377e79a656888d3892',
'com.android.support:drawerlayout:8f6809afae4793550c37461c9810e954ae6a23dbb4d23e5333bf18148df1150a',
'com.android.support:slidingpanelayout:d1d234f66a1b36a9aee9b94fa6c66f97128c0828078c8e889e9037ec898cd600',
'com.android.support:customview:98db03845f994e08248bf701c1ff0ccaa12e70f94251ec9272900f0f694e072b',
'com.android.support:swiperefreshlayout:a3b41f7f6730866b49865e86e49f988d4858699765f534300fb2ff5f9325e712',
'com.android.support:asynclayoutinflater:115bde87721f7334579b0c735f60dd7c98af1bb7f34010c5b0553b95dc351aa2',
'android.arch.persistence.room:runtime:c21810eaafce370f1c9df1365393f55f962370a0d8b0b38b4771052c7021b737',
'com.android.support:support-core-utils:c81e1e98ca3cb2edae002c69cf35b22aec364b8cb2f1042c97e206eb5790ac41',
'com.android.support:loader:920b85efd72dc33e915b0f88a883fe73b88483c6df8751a741e17611f2460341',
'com.android.support:support-vector-drawable:f658986d968172bccfed28578471c96050780fe5e133861e4d331069cc373f4d',
'com.android.support:support-media-compat:266eff9605f515013eee1ebdbd8818a9270696dc807f34bbcc5fc11fb61a22c7',
'com.android.support:support-compat:e17e3b01dbea3f9ea1c86943292f903ca93d2231c6242e456e0b6a9c5817118a',
'com.android.support:versionedparcelable:60eb1cb08f71b65c3f6123135e03ebeb5930b5e126e1e5b2ac91b386908c9d02',
'com.android.support:collections:93c258c8a09f531a267653829742c0f8f6da0e348b11cb8655b0855628f2d4f0',
'android.arch.lifecycle:livedata:50ab0490c1ff1a7cfb4e554032998b080888946d0dd424f39900efc4a1bcd750',
'android.arch.lifecycle:livedata-core:d6fdd8b985d6178d7ea2f16986a24e83f1bee936b74d43167c69e08d3cc12c50',
'android.arch.core:runtime:c3215aa5873311b3f88a6f4e4a3c25ad89971bc127de8c3e1291c57f93a05c39',
'com.android.support:support-core-utils:b69c6e1e7731b876b910fc7100bcadf40a57f27b32ca26b91400995542112c96',
'com.android.support:support-vector-drawable:bf4f4fcbf58b1380616581224e6487c230bfdb3434ec353d4adaa4b1f4865cfa',
'com.android.support:support-media-compat:6dd9327ee9aa467cab479aad97df375072b2b6ba61eadffdaa5a88de3843c457',
'com.android.support:support-compat:ed4d25d91a0b13d8b9def1c0de69ed03d7fb89d50fb37eb0e9b63b0cf7a42357',
'android.arch.lifecycle:runtime:c4e4be66c1b2f0abec593571454e1de14013f7e0f96bf2a9f212931a48cae550',
'android.arch.core:common:3a616a32f433e9e23f556b38575c31b013613d3ae85206263b7625fe1f4c151a',
'android.arch.lifecycle:common:8d378e88ebd5189e09eef623414812c868fd90aa519d6160e2311fb8b81cff56',
'android.arch.lifecycle:viewmodel:7de29cfaba77d6b5d5be234c57f6812d0150d087e63941af22ba1d1f8e2bc96a',
'com.github.bumptech.glide:gifdecoder:59ccf3bb0cec11dab4b857382cbe0b171111b6fc62bf141adce4e1180889af15',
'com.android.support:support-annotations:af05330d997eb92a066534dbe0a3ea24347d26d7001221092113ae02a8f233da',
'com.android.support:interpolator:7bc7ee86a0db39a4b51956f3e89842d2bd962118d57d779eb6ed6b34ba0677ea',
'com.android.support:cursoradapter:87feffe742b8d62ca8a9833abe564838bf6a672e31c7ad1306ec4006adf90d21',
'android.arch.persistence.room:common:7cf36bcd5f59ddc4876f887e36511bfd7b111f1eb717c0e9b6e2bcc710305ae6',
'android.arch.persistence:db-framework:bd665448330acb90a6f551a87b0ba69169da2b8ec168b92f387997339cc14311',
'android.arch.persistence:db:504e8c4307bfd53084924776ba3d49fed11b6f76d82dd80d5121c2d907fdfef6',
'android.arch.core:runtime:c3215aa5873311b3f88a6f4e4a3c25ad89971bc127de8c3e1291c57f93a05c39',
'android.arch.core:common:3a616a32f433e9e23f556b38575c31b013613d3ae85206263b7625fe1f4c151a',
'android.arch.lifecycle:viewmodel:7de29cfaba77d6b5d5be234c57f6812d0150d087e63941af22ba1d1f8e2bc96a',
'com.android.support:documentfile:47cdcd3e9302b7b064923f05487a5c03babbd9bbda4726b71e97791fab5d4779',
'com.android.support:localbroadcastmanager:d287c823af5fdde72c099fcfc5f630efe9687af7a914343ae6fd92de32c8a806',
'com.android.support:print:4be8a812d73e4a80e35b91ceae127def3f0bb9726bf3bc439aa0cc81503f5728',
'com.android.support:support-annotations:5d5b9414f02d3fa0ee7526b8d5ddae0da67c8ecc8c4d63ffa6cf91488a93b927',
'androidx.concurrent:futures:1f63078c41efd29d20ee3444fba93c6cdfaeeb862c6d3b6166ff8debd37d471a',
'org.whispersystems:signal-protocol-android:5b8acded7f2a40178eb90ab8e8cbfec89d170d91b3ff5e78487d1098df6185a1',
'org.whispersystems:signal-service-java:4db9adf763071756cfd93fe48a40850f684ca02f2dea59601841abba7715c5c1',
'com.github.bumptech.glide:disklrucache:c1b1b6f5bbd01e2fcdc9d7f60913c8d338bdb65ed4a93bfa02b56f19daaade4b',
@ -226,6 +249,8 @@ dependencyVerification {
'com.klinkerapps:logger:177e325259a8b111ad6745ec10db5861723c99f402222b80629f576f49408541',
'com.google.android:flexbox:a9989fd13ae2ee42765dfc515fe362edf4f326e74925d02a10369df8092a4935',
'org.jsoup:jsoup:abeaf34795a4de70f72aed6de5966d2955ec7eb348eeb813324f23c999575473',
'com.google.guava:listenablefuture:e4ad7607e5c0477c6f890ef26a49cb8d1bb4dffb650bab4502afee64644e3069',
'androidx.annotation:annotation:04f22f257944ce223701d5aa1bdc36fb7f4594e87b539044045cd161d965468e',
'org.whispersystems:curve25519-android:82595394422b957d4a5b5f1b27b75ba25cf6dc4db4d312418ca38cd6fff279ca',
'org.whispersystems:signal-protocol-java:5152c2b01a25147967d6bf82e540f947901bdfa79260be3eb3e96b03f787d6b5',
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
@ -245,8 +270,8 @@ dependencyVerification {
android {
flavorDimensions "none"
compileSdkVersion 27
buildToolsVersion '27.0.1'
compileSdkVersion 28
buildToolsVersion '27.0.3'
useLibrary 'org.apache.http.legacy'
dexOptions {
@ -258,7 +283,7 @@ android {
versionName "4.26.2"
minSdkVersion 14
targetSdkVersion 25
targetSdkVersion 26
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
@ -292,6 +317,7 @@ android {
exclude 'asm-license.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/proguard/androidx-annotations.pro'
}
signingConfigs {
@ -319,6 +345,7 @@ android {
'proguard-retrolambda.pro',
'proguard-okhttp.pro',
'proguard-ez-vcard.pro',
'proguard-workmanager.pro',
'proguard.cfg'
testProguardFiles 'proguard-automation.pro',
'proguard.cfg'

1
proguard-workmanager.pro Normal file
View File

@ -0,0 +1 @@
-dontwarn sun.misc.Unsafe

View File

@ -17,8 +17,6 @@
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.arch.lifecycle.DefaultLifecycleObserver;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.ProcessLifecycleOwner;
@ -27,7 +25,6 @@ import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.multidex.MultiDexApplication;
import android.support.v4.app.NotificationManagerCompat;
import com.google.android.gms.security.ProviderInstaller;
@ -37,14 +34,9 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.dependencies.DependencyInjector;
import org.thoughtcrime.securesms.jobmanager.persistence.JavaJobSerializer;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirementProvider;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirementProvider;
import org.thoughtcrime.securesms.jobs.requirements.ServiceRequirementProvider;
import org.thoughtcrime.securesms.jobs.requirements.SqlCipherMigrationRequirementProvider;
import org.thoughtcrime.securesms.logging.AndroidLogger;
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
import org.thoughtcrime.securesms.logging.Log;
@ -58,6 +50,7 @@ import org.thoughtcrime.securesms.service.LocalBackupListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.PeerConnectionFactory.InitializationOptions;
import org.webrtc.voiceengine.WebRtcAudioManager;
@ -68,6 +61,7 @@ import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import androidx.work.WorkManager;
import dagger.ObjectGraph;
/**
@ -86,6 +80,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
private JobManager jobManager;
private ObjectGraph objectGraph;
private PersistentLogger persistentLogger;
private boolean initialized;
private volatile boolean isAppVisible;
@ -95,20 +90,33 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
@Override
public void onCreate() {
super.onCreate();
initializeRandomNumberFix();
initializeLogging();
initializeCrashHandling();
initializeDependencyInjection();
initializeJobManager();
initializeExpiringMessageManager();
initializeGcmCheck();
initializeSignedPreKeyCheck();
initializePeriodicTasks();
initializeCircumvention();
initializeWebRtc();
NotificationChannels.create(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
synchronized (this) {
super.onCreate();
initializeRandomNumberFix();
initializeLogging();
initializeCrashHandling();
initializeDependencyInjection();
initializeJobManager();
initializeExpiringMessageManager();
initializeGcmCheck();
initializeSignedPreKeyCheck();
initializePeriodicTasks();
initializeCircumvention();
initializeWebRtc();
NotificationChannels.create(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
initialized = true;
notifyAll();
}
}
public void ensureInitialized() {
synchronized (this) {
while (!initialized) {
Util.wait(this, 0);
}
}
}
@Override
@ -164,16 +172,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
}
private void initializeJobManager() {
this.jobManager = JobManager.newBuilder(this)
.withName("TextSecureJobs")
.withDependencyInjector(this)
.withJobSerializer(new JavaJobSerializer())
.withRequirementProviders(new MasterSecretRequirementProvider(this),
new ServiceRequirementProvider(this),
new NetworkRequirementProvider(this),
new SqlCipherMigrationRequirementProvider())
.withConsumerThreads(5)
.build();
this.jobManager = new JobManager(WorkManager.getInstance());
}
private void initializeDependencyInjection() {

View File

@ -25,6 +25,11 @@ import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.preference.PreferenceManager;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.persistence.JavaJobSerializer;
import org.thoughtcrime.securesms.jobmanager.persistence.PersistentStorage;
import org.thoughtcrime.securesms.logging.Log;
import android.view.View;
import android.widget.ProgressBar;
@ -85,6 +90,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
public static final int FULL_TEXT_SEARCH = 358;
public static final int BAD_IMPORT_CLEANUP = 373;
public static final int IMAGE_CACHE_CLEANUP = 406;
public static final int WORKMANAGER_MIGRATION = 408;
private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
@ -108,6 +114,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
add(FULL_TEXT_SEARCH);
add(BAD_IMPORT_CLEANUP);
add(IMAGE_CACHE_CLEANUP);
add(WORKMANAGER_MIGRATION);
}};
private MasterSecret masterSecret;
@ -318,6 +325,18 @@ public class DatabaseUpgradeActivity extends BaseActivity {
}
}
if (params[0] < WORKMANAGER_MIGRATION) {
Log.i(TAG, "Beginning migration of existing jobs to WorkManager");
JobManager jobManager = ApplicationContext.getInstance(getApplicationContext()).getJobManager();
PersistentStorage storage = new PersistentStorage(getApplicationContext(), "TextSecureJobs", new JavaJobSerializer());
for (Job job : storage.getAllUnencrypted()) {
jobManager.add(job);
Log.i(TAG, "Migrated job with class '" + job.getClass().getSimpleName() + "' to run on new JobManager.");
}
}
return null;
}

View File

@ -254,7 +254,7 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView, Camer
@Override
protected boolean drawChild(@NonNull Canvas canvas, @NonNull View child, long drawingTime) {
boolean result;
final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);
final int save = canvas.save();
canvas.getClipBounds(drawChildrenRect);
if (child == coverView) {

View File

@ -1,138 +1,139 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.jobmanager;
import android.os.PowerManager;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.jobmanager.requirements.Requirement;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.jobmanager.dependencies.ContextDependent;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobs.requirements.SqlCipherMigrationRequirement;
import org.thoughtcrime.securesms.logging.Log;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
/**
* An abstract class representing a unit of work that can be scheduled with
* the JobManager. This should be extended to implement tasks.
*/
public abstract class Job implements Serializable {
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
private final JobParameters parameters;
public abstract class Job extends Worker implements Serializable {
private transient long persistentId;
private transient int runIteration;
private transient long lastRunTime;
private transient PowerManager.WakeLock wakeLock;
private static final long serialVersionUID = -4658540468214421276L;
public Job(JobParameters parameters) {
this.parameters = parameters;
}
private static final String TAG = Job.class.getSimpleName();
public List<Requirement> getRequirements() {
return parameters.getRequirements();
}
static final String KEY_RETRY_COUNT = "Job_retry_count";
static final String KEY_RETRY_UNTIL = "Job_retry_until";
static final String KEY_SUBMIT_TIME = "Job_submit_time";
static final String KEY_REQUIRES_MASTER_SECRET = "Job_requires_master_secret";
static final String KEY_REQUIRES_SQLCIPHER = "Job_requires_sqlcipher";
public boolean isRequirementsMet() {
for (Requirement requirement : parameters.getRequirements()) {
if (!requirement.isPresent(this)) return false;
}
private JobParameters parameters;
return true;
}
public String getGroupId() {
return parameters.getGroupId();
}
public boolean isPersistent() {
return parameters.isPersistent();
}
public EncryptionKeys getEncryptionKeys() {
return parameters.getEncryptionKeys();
}
public void setEncryptionKeys(EncryptionKeys keys) {
parameters.setEncryptionKeys(keys);
}
public int getRetryCount() {
return parameters.getRetryCount();
}
public long getRetryUntil() {
return parameters.getRetryUntil();
}
public long getLastRunTime() {
return lastRunTime;
}
public void resetRunStats() {
runIteration = 0;
lastRunTime = 0;
}
public void setPersistentId(long persistentId) {
this.persistentId = persistentId;
}
public long getPersistentId() {
return persistentId;
}
public int getRunIteration() {
return runIteration;
}
public boolean needsWakeLock() {
return parameters.needsWakeLock();
}
public long getWakeLockTimeout() {
return parameters.getWakeLockTimeout();
}
public void setWakeLock(PowerManager.WakeLock wakeLock) {
this.wakeLock = wakeLock;
}
public PowerManager.WakeLock getWakeLock() {
return this.wakeLock;
}
public void onRetry() {
runIteration++;
lastRunTime = System.currentTimeMillis();
for (Requirement requirement : parameters.getRequirements()) {
requirement.onRetry(this);
}
public Job(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
/**
* Called after a job has been added to the JobManager queue. If it's a persistent job,
* the state has been persisted to disk before this method is called.
* Invoked when a job is first created in our own codebase.
*/
public abstract void onAdded();
protected Job(@Nullable JobParameters parameters) {
this.parameters = parameters;
}
@NonNull
@Override
public Result doWork() {
Data data = getInputData();
log("doWork()" + logSuffix());
ApplicationContext.getInstance(getApplicationContext()).ensureInitialized();
ApplicationContext.getInstance(getApplicationContext()).injectDependencies(this);
if (this instanceof ContextDependent) {
((ContextDependent)this).setContext(getApplicationContext());
}
initialize(new SafeData(data));
try {
if (withinRetryLimits(data)) {
if (requirementsMet(data)) {
onRun();
log("Successfully completed." + logSuffix());
return Result.SUCCESS;
} else {
log("Retrying due to unmet requirements." + logSuffix());
return retry();
}
} else {
warn("Failing after hitting the retry limit." + logSuffix());
return cancel();
}
} catch (Exception e) {
if (onShouldRetry(e)) {
log("Retrying after a retryable exception." + logSuffix());
return retry();
}
warn("Failing due to an exception." + logSuffix(), e);
return cancel();
}
}
@Override
public void onStopped(boolean cancelled) {
if (cancelled) {
warn("onStopped() with cancellation signal." + logSuffix());
onCanceled();
}
}
final void onSubmit(UUID id) {
log(id, "onSubmit()");
onAdded();
}
/**
* Called after a run has finished and we've determined a retry is required, but before the next
* attempt is run.
*/
protected void onRetry() { }
/**
* Called after a job has been added to the JobManager queue. Invoked off the main thread, so its
* safe to do longer-running work. However, work should finish relatively quickly, as it will
* block the submission of future tasks.
*/
protected void onAdded() { }
/**
* All instance state needs to be persisted in the provided {@link Data.Builder} so that it can
* be restored in {@link #initialize(SafeData)}.
* @param dataBuilder The builder where you put your state.
* @return The result of {@code dataBuilder.build()}.
*/
protected abstract @NonNull Data serialize(@NonNull Data.Builder dataBuilder);
/**
* Restore all of your instance state from the provided {@link Data}. It should contain all of
* the data put in during {@link #serialize(Data.Builder)}.
* @param data Where your data is stored.
*/
protected abstract void initialize(@NonNull SafeData data);
/**
* Called to actually execute the job.
* @throws Exception
*/
protected abstract void onRun() throws Exception;
public abstract void onRun() throws Exception;
/**
* Called if a job fails to run (onShouldRetry returned false, or the number of retries exceeded
* the job's configured retry count.
*/
protected abstract void onCanceled();
/**
* If onRun() throws an exception, this method will be called to determine whether the
@ -141,14 +142,69 @@ public abstract class Job implements Serializable {
* @param exception The exception onRun() threw.
* @return true if onRun() should be called again, false otherwise.
*/
public abstract boolean onShouldRetry(Exception exception);
protected abstract boolean onShouldRetry(Exception exception);
/**
* Called if a job fails to run (onShouldRetry returned false, or the number of retries exceeded
* the job's configured retry count.
*/
public abstract void onCanceled();
@Nullable JobParameters getJobParameters() {
return parameters;
}
private Result retry() {
onRetry();
return Result.RETRY;
}
private Result cancel() {
onCanceled();
return Result.SUCCESS;
}
private boolean requirementsMet(Data data) {
boolean met = true;
if (data.getBoolean(KEY_REQUIRES_MASTER_SECRET, false)) {
met &= new MasterSecretRequirement(getApplicationContext()).isPresent();
}
if (data.getBoolean(KEY_REQUIRES_SQLCIPHER, false)) {
met &= new SqlCipherMigrationRequirement(getApplicationContext()).isPresent();
}
return met;
}
private boolean withinRetryLimits(Data data) {
int retryCount = data.getInt(KEY_RETRY_COUNT, 0);
long retryUntil = data.getLong(KEY_RETRY_UNTIL, 0);
if (retryCount > 0) {
return getRunAttemptCount() <= retryCount;
}
return System.currentTimeMillis() < retryUntil;
}
private void log(@NonNull String message) {
log(getId(), message);
}
private void log(@NonNull UUID id, @NonNull String message) {
Log.i(TAG, buildLog(id, message));
}
private void warn(@NonNull String message) {
warn(message, null);
}
private void warn(@NonNull String message, @Nullable Throwable t) {
Log.w(TAG, buildLog(getId(), message), t);
}
private String buildLog(@NonNull UUID id, @NonNull String message) {
return "[" + id + "] " + getClass().getSimpleName() + " :: " + message;
}
private String logSuffix() {
long timeSinceSubmission = System.currentTimeMillis() - getInputData().getLong(KEY_SUBMIT_TIME, 0);
return " (Time since submission: " + timeSinceSubmission + " ms, Run attempt: " + getRunAttemptCount() + ")";
}
}

View File

@ -1,100 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.jobmanager;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.jobmanager.persistence.PersistentStorage;
class JobConsumer extends Thread {
private static final String TAG = JobConsumer.class.getSimpleName();
enum JobResult {
SUCCESS,
FAILURE,
DEFERRED
}
private final JobQueue jobQueue;
private final PersistentStorage persistentStorage;
public JobConsumer(String name, JobQueue jobQueue, PersistentStorage persistentStorage) {
super(name);
this.jobQueue = jobQueue;
this.persistentStorage = persistentStorage;
}
@Override
public void run() {
while (true) {
Job job = jobQueue.getNext();
JobResult result = runJob(job);
if (result == JobResult.DEFERRED) {
jobQueue.push(job);
} else {
if (result == JobResult.FAILURE) {
job.onCanceled();
}
if (job.isPersistent()) {
persistentStorage.remove(job.getPersistentId());
}
if (job.getWakeLock() != null && job.getWakeLockTimeout() == 0) {
job.getWakeLock().release();
}
if (job.getGroupId() != null) {
jobQueue.setGroupIdAvailable(job.getGroupId());
}
}
}
}
private JobResult runJob(Job job) {
while (canRetry(job)) {
try {
job.onRun();
return JobResult.SUCCESS;
} catch (Exception exception) {
Log.w(TAG, exception);
if (exception instanceof RuntimeException) {
throw (RuntimeException)exception;
} else if (!job.onShouldRetry(exception)) {
return JobResult.FAILURE;
}
job.onRetry();
if (!job.isRequirementsMet()) {
return JobResult.DEFERRED;
}
}
}
return JobResult.FAILURE;
}
private boolean canRetry(@NonNull Job job) {
if (job.getRetryCount() > 0) {
return job.getRunIteration() < job.getRetryCount();
}
return System.currentTimeMillis() < job.getRetryUntil();
}
}

View File

@ -1,252 +1,69 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.jobmanager;
import android.content.Context;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.dependencies.AggregateDependencyInjector;
import org.thoughtcrime.securesms.jobmanager.dependencies.DependencyInjector;
import org.thoughtcrime.securesms.jobmanager.persistence.JobSerializer;
import org.thoughtcrime.securesms.jobmanager.persistence.PersistentStorage;
import org.thoughtcrime.securesms.jobmanager.requirements.RequirementListener;
import org.thoughtcrime.securesms.jobmanager.requirements.RequirementProvider;
import org.thoughtcrime.securesms.logging.Log;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;
/**
* A JobManager allows you to enqueue {@link org.thoughtcrime.securesms.jobmanager.Job} tasks
* that are executed once a Job's {@link org.thoughtcrime.securesms.jobmanager.requirements.Requirement}s
* are met.
*/
public class JobManager implements RequirementListener {
import androidx.work.BackoffPolicy;
import androidx.work.Constraints;
import androidx.work.Data;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
private final JobQueue jobQueue = new JobQueue();
private final Executor eventExecutor = Executors.newSingleThreadExecutor();
private final AtomicBoolean hasLoadedEncrypted = new AtomicBoolean(false);
public class JobManager {
private final Context context;
private final PersistentStorage persistentStorage;
private final List<RequirementProvider> requirementProviders;
private final AggregateDependencyInjector dependencyInjector;
private static final Constraints NETWORK_CONSTRAINT = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
private JobManager(Context context, String name,
List<RequirementProvider> requirementProviders,
DependencyInjector dependencyInjector,
JobSerializer jobSerializer, int consumers)
{
this.context = context;
this.dependencyInjector = new AggregateDependencyInjector(dependencyInjector);
this.persistentStorage = new PersistentStorage(context, name, jobSerializer, this.dependencyInjector);
this.requirementProviders = requirementProviders;
private final Executor executor = Executors.newSingleThreadExecutor();
eventExecutor.execute(new LoadTask(null));
private final WorkManager workManager;
if (requirementProviders != null && !requirementProviders.isEmpty()) {
for (RequirementProvider provider : requirementProviders) {
provider.setListener(this);
public JobManager(@NonNull WorkManager workManager) {
this.workManager = workManager;
}
public void add(Job job) {
executor.execute(() -> {
workManager.synchronous().pruneWorkSync();
JobParameters jobParameters = job.getJobParameters();
if (jobParameters == null) {
throw new IllegalStateException("Jobs must have JobParameters at this stage.");
}
}
for (int i=0;i<consumers;i++) {
new JobConsumer("JobConsumer-" + i, jobQueue, persistentStorage).start();
}
}
Data.Builder dataBuilder = new Data.Builder().putInt(Job.KEY_RETRY_COUNT, jobParameters.getRetryCount())
.putLong(Job.KEY_RETRY_UNTIL, jobParameters.getRetryUntil())
.putLong(Job.KEY_SUBMIT_TIME, System.currentTimeMillis())
.putBoolean(Job.KEY_REQUIRES_MASTER_SECRET, jobParameters.requiresMasterSecret())
.putBoolean(Job.KEY_REQUIRES_SQLCIPHER, jobParameters.requiresSqlCipher());
Data data = job.serialize(dataBuilder);
/**
* @param context An Android {@link android.content.Context}.
* @return a {@link org.thoughtcrime.securesms.jobmanager.JobManager.Builder} used to construct a JobManager.
*/
public static Builder newBuilder(Context context) {
return new Builder(context);
}
OneTimeWorkRequest.Builder requestBuilder = new OneTimeWorkRequest.Builder(job.getClass())
.setInputData(data)
.setBackoffCriteria(BackoffPolicy.LINEAR, OneTimeWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS);
public void setEncryptionKeys(EncryptionKeys keys) {
if (hasLoadedEncrypted.compareAndSet(false, true)) {
eventExecutor.execute(new LoadTask(keys));
}
}
if (jobParameters.requiresNetwork()) {
requestBuilder.setConstraints(NETWORK_CONSTRAINT);
}
/**
* Queue a {@link org.thoughtcrime.securesms.jobmanager.Job} to be executed.
*
* @param job The Job to be executed.
*/
public void add(final Job job) {
if (job.needsWakeLock()) {
job.setWakeLock(acquireWakeLock(context, job.toString(), job.getWakeLockTimeout()));
}
OneTimeWorkRequest request = requestBuilder.build();
eventExecutor.execute(new Runnable() {
@Override
public void run() {
try {
if (job.isPersistent()) {
persistentStorage.store(job);
}
job.onSubmit(request.getId());
dependencyInjector.injectDependencies(context, job);
job.onAdded();
jobQueue.add(job);
} catch (IOException e) {
Log.w("JobManager", e);
job.onCanceled();
}
String groupId = jobParameters.getGroupId();
if (groupId != null) {
ExistingWorkPolicy policy = jobParameters.shouldIgnoreDuplicates() ? ExistingWorkPolicy.KEEP : ExistingWorkPolicy.APPEND;
workManager.beginUniqueWork(groupId, policy, request).enqueue();
} else {
workManager.beginWith(request).enqueue();
}
});
}
@Override
public void onRequirementStatusChanged() {
eventExecutor.execute(new Runnable() {
@Override
public void run() {
jobQueue.onRequirementStatusChanged();
}
});
}
private PowerManager.WakeLock acquireWakeLock(Context context, String name, long timeout) {
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
if (timeout == 0) wakeLock.acquire();
else wakeLock.acquire(timeout);
return wakeLock;
}
private class LoadTask implements Runnable {
private final EncryptionKeys keys;
public LoadTask(EncryptionKeys keys) {
this.keys = keys;
}
@Override
public void run() {
List<Job> pendingJobs;
if (keys == null) pendingJobs = persistentStorage.getAllUnencrypted();
else pendingJobs = persistentStorage.getAllEncrypted(keys);
jobQueue.addAll(pendingJobs);
}
}
public static class Builder {
private final Context context;
private String name;
private List<RequirementProvider> requirementProviders;
private DependencyInjector dependencyInjector;
private JobSerializer jobSerializer;
private int consumerThreads;
Builder(Context context) {
this.context = context;
this.consumerThreads = 5;
}
/**
* A name for the {@link org.thoughtcrime.securesms.jobmanager.JobManager}. This is a required parameter,
* and is linked to the durable queue used by persistent jobs.
*
* @param name The name for the JobManager to build.
* @return The builder.
*/
public Builder withName(String name) {
this.name = name;
return this;
}
/**
* The {@link org.thoughtcrime.securesms.jobmanager.requirements.RequirementProvider}s to register with this
* JobManager. Optional. Each {@link org.thoughtcrime.securesms.jobmanager.requirements.Requirement} an
* enqueued Job depends on should have a matching RequirementProvider registered here.
*
* @param requirementProviders The RequirementProviders
* @return The builder.
*/
public Builder withRequirementProviders(RequirementProvider... requirementProviders) {
this.requirementProviders = Arrays.asList(requirementProviders);
return this;
}
/**
* The {@link org.thoughtcrime.securesms.jobmanager.dependencies.DependencyInjector} to use for injecting
* dependencies into {@link Job}s. Optional. Injection occurs just before a Job's onAdded() callback, or
* after deserializing a persistent job.
*
* @param dependencyInjector The injector to use.
* @return The builder.
*/
public Builder withDependencyInjector(DependencyInjector dependencyInjector) {
this.dependencyInjector = dependencyInjector;
return this;
}
/**
* The {@link org.thoughtcrime.securesms.jobmanager.persistence.JobSerializer} to use for persistent Jobs.
* Required if persistent Jobs are used.
*
* @param jobSerializer The serializer to use.
* @return The builder.
*/
public Builder withJobSerializer(JobSerializer jobSerializer) {
this.jobSerializer = jobSerializer;
return this;
}
/**
* Set the number of threads dedicated to consuming Jobs from the queue and executing them.
*
* @param consumerThreads The number of threads.
* @return The builder.
*/
public Builder withConsumerThreads(int consumerThreads) {
this.consumerThreads = consumerThreads;
return this;
}
/**
* @return A constructed JobManager.
*/
public JobManager build() {
if (name == null) {
throw new IllegalArgumentException("You must specify a name!");
}
if (requirementProviders == null) {
requirementProviders = new LinkedList<>();
}
return new JobManager(context, name, requirementProviders,
dependencyInjector, jobSerializer,
consumerThreads);
}
}
}

View File

@ -16,12 +16,16 @@
*/
package org.thoughtcrime.securesms.jobmanager;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkBackoffRequirement;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobmanager.requirements.Requirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobs.requirements.NetworkOrServiceRequirement;
import org.thoughtcrime.securesms.jobs.requirements.SqlCipherMigrationRequirement;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* The set of parameters that describe a {@link org.thoughtcrime.securesms.jobmanager.Job}.
@ -30,46 +34,86 @@ public class JobParameters implements Serializable {
private static final long serialVersionUID = 4880456378402584584L;
private transient EncryptionKeys encryptionKeys;
private final List<Requirement> requirements;
private final boolean isPersistent;
private final boolean requiresNetwork;
private final boolean requiresMasterSecret;
private final boolean requiresSqlCipher;
private final int retryCount;
private final long retryUntil;
private final String groupId;
private final boolean wakeLock;
private final long wakeLockTimeout;
private final boolean ignoreDuplicates;
private JobParameters(List<Requirement> requirements,
boolean isPersistent, String groupId,
EncryptionKeys encryptionKeys,
int retryCount, long retryUntil, boolean wakeLock,
long wakeLockTimeout)
private JobParameters(String groupId,
boolean ignoreDuplicates,
boolean requiresNetwork,
boolean requiresMasterSecret,
boolean requiresSqlCipher,
int retryCount,
long retryUntil)
{
this.requirements = requirements;
this.isPersistent = isPersistent;
this.groupId = groupId;
this.encryptionKeys = encryptionKeys;
this.retryCount = retryCount;
this.retryUntil = retryUntil;
this.wakeLock = wakeLock;
this.wakeLockTimeout = wakeLockTimeout;
this.groupId = groupId;
this.ignoreDuplicates = ignoreDuplicates;
this.requirements = Collections.emptyList();
this.requiresNetwork = requiresNetwork;
this.requiresMasterSecret = requiresMasterSecret;
this.requiresSqlCipher = requiresSqlCipher;
this.retryCount = retryCount;
this.retryUntil = retryUntil;
}
public List<Requirement> getRequirements() {
return requirements;
public boolean shouldIgnoreDuplicates() {
return ignoreDuplicates;
}
public boolean isPersistent() {
return isPersistent;
public boolean requiresNetwork() {
return requiresNetwork || hasNetworkRequirement(requirements);
}
public EncryptionKeys getEncryptionKeys() {
return encryptionKeys;
public boolean requiresMasterSecret() {
return requiresMasterSecret || hasMasterSecretRequirement(requirements);
}
public void setEncryptionKeys(EncryptionKeys encryptionKeys) {
this.encryptionKeys = encryptionKeys;
public boolean requiresSqlCipher() {
return requiresSqlCipher || hasSqlCipherRequirement(requirements);
}
private boolean hasNetworkRequirement(List<Requirement> requirements) {
if (requirements == null || requirements.size() == 0) return false;
for (Requirement requirement : requirements) {
if (requirement instanceof NetworkRequirement ||
requirement instanceof NetworkOrServiceRequirement ||
requirement instanceof NetworkBackoffRequirement)
{
return true;
}
}
return false;
}
private boolean hasMasterSecretRequirement(List<Requirement> requirements) {
if (requirements == null || requirements.size() == 0) return false;
for (Requirement requirement : requirements) {
if (requirement instanceof MasterSecretRequirement) {
return true;
}
}
return false;
}
private boolean hasSqlCipherRequirement(List<Requirement> requirements) {
if (requirements == null || requirements.size() == 0) return false;
for (Requirement requirement : requirements) {
if (requirement instanceof SqlCipherMigrationRequirement) {
return true;
}
}
return false;
}
public int getRetryCount() {
@ -91,52 +135,29 @@ public class JobParameters implements Serializable {
return groupId;
}
public boolean needsWakeLock() {
return wakeLock;
}
public long getWakeLockTimeout() {
return wakeLockTimeout;
}
public static class Builder {
private List<Requirement> requirements = new LinkedList<>();
private boolean isPersistent = false;
private EncryptionKeys encryptionKeys = null;
private int retryCount = 100;
private long retryDuration = 0;
private String groupId = null;
private boolean wakeLock = false;
private long wakeLockTimeout = 0;
private int retryCount = 100;
private long retryDuration = 0;
private String groupId = null;
private boolean ignoreDuplicates = false;
private boolean requiresNetwork = false;
private boolean requiresSqlCipher = false;
private boolean requiresMasterSecret = false;
/**
* Specify a {@link org.thoughtcrime.securesms.jobmanager.requirements.Requirement }that must be met
* before the Job is executed. May be called multiple times to register multiple requirements.
* @param requirement The Requirement that must be met.
* @return the builder.
*/
public Builder withRequirement(Requirement requirement) {
this.requirements.add(requirement);
public Builder withNetworkRequirement() {
requiresNetwork = true;
return this;
}
/**
* Specify that the Job should be durably persisted to disk, so that it remains in the queue
* across application restarts.
* @return The builder.
*/
public Builder withPersistence() {
this.isPersistent = true;
@Deprecated
public Builder withMasterSecretRequirement() {
requiresMasterSecret = true;
return this;
}
/**
* Specify that the job should use encryption when durably persisted to disk.
* @param encryptionKeys The keys to encrypt the serialized job with before persisting.
* @return the builder.
*/
public Builder withEncryption(EncryptionKeys encryptionKeys) {
this.encryptionKeys = encryptionKeys;
@Deprecated
public Builder withSqlCipherRequirement() {
requiresSqlCipher = true;
return this;
}
@ -153,6 +174,11 @@ public class JobParameters implements Serializable {
return this;
}
/**
* Specify for how long we should keep retrying this job. Ignored if retryCount is set.
* @param duration The duration (in ms) for how long we should keep retrying this job for.
* @return the builder
*/
public Builder withRetryDuration(long duration) {
this.retryDuration = duration;
this.retryCount = 0;
@ -172,35 +198,25 @@ public class JobParameters implements Serializable {
}
/**
* Specify whether this job should hold a wake lock.
* If true, only one job with this groupId can be active at a time. If a job with the same
* groupId is already running, then subsequent jobs will be ignored silently. Only has an effect
* if a groupId has been specified via {@link #withGroupId(String)}.
* <p />
* Defaults to false.
*
* @param needsWakeLock If set, this job will acquire a wakelock on add(), and hold it until
* run() completes, or cancel().
* @param timeout Specify a timeout for the wakelock. A timeout of
* 0 will result in no timeout.
*
* @return the builder.
* @param ignoreDuplicates Whether to ignore duplicates.
* @return the builder
*/
public Builder withWakeLock(boolean needsWakeLock, long timeout, TimeUnit unit) {
this.wakeLock = needsWakeLock;
this.wakeLockTimeout = unit.toMillis(timeout);
public Builder withDuplicatesIgnored(boolean ignoreDuplicates) {
this.ignoreDuplicates = ignoreDuplicates;
return this;
}
/**
* Specify whether this job should hold a wake lock.
*
* @return the builder.
*/
public Builder withWakeLock(boolean needsWakeLock) {
return withWakeLock(needsWakeLock, 0, TimeUnit.MILLISECONDS);
}
/**
* @return the JobParameters instance that describes a Job.
*/
public JobParameters create() {
return new JobParameters(requirements, isPersistent, groupId, encryptionKeys, retryCount, System.currentTimeMillis() + retryDuration, wakeLock, wakeLockTimeout);
return new JobParameters(groupId, ignoreDuplicates, requiresNetwork, requiresMasterSecret, requiresSqlCipher, retryCount, System.currentTimeMillis() + retryDuration);
}
}
}

View File

@ -1,118 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.jobmanager;
import android.support.annotation.NonNull;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
class JobQueue {
private final Map<String, Job> activeGroupIds = new HashMap<>();
private final LinkedList<Job> jobQueue = new LinkedList<>();
synchronized void onRequirementStatusChanged() {
notifyAll();
}
synchronized void add(Job job) {
jobQueue.add(job);
processJobAddition(job);
notifyAll();
}
synchronized void addAll(List<Job> jobs) {
jobQueue.addAll(jobs);
for (Job job : jobs) {
processJobAddition(job);
}
notifyAll();
}
private void processJobAddition(@NonNull Job job) {
if (isJobActive(job) && isGroupIdAvailable(job)) {
setGroupIdUnavailable(job);
} else if (!isGroupIdAvailable(job)) {
Job blockingJob = activeGroupIds.get(job.getGroupId());
blockingJob.resetRunStats();
}
}
synchronized void push(Job job) {
jobQueue.addFirst(job);
}
synchronized Job getNext() {
try {
Job nextAvailableJob;
while ((nextAvailableJob = getNextAvailableJob()) == null) {
wait();
}
return nextAvailableJob;
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
synchronized void setGroupIdAvailable(String groupId) {
if (groupId != null) {
activeGroupIds.remove(groupId);
notifyAll();
}
}
private Job getNextAvailableJob() {
if (jobQueue.isEmpty()) return null;
ListIterator<Job> iterator = jobQueue.listIterator();
while (iterator.hasNext()) {
Job job = iterator.next();
if (job.isRequirementsMet() && isGroupIdAvailable(job)) {
iterator.remove();
setGroupIdUnavailable(job);
return job;
}
}
return null;
}
private boolean isJobActive(@NonNull Job job) {
return job.getRetryUntil() > 0 && job.getRunIteration() > 0;
}
private boolean isGroupIdAvailable(@NonNull Job requester) {
String groupId = requester.getGroupId();
return groupId == null || !activeGroupIds.containsKey(groupId) || activeGroupIds.get(groupId).equals(requester);
}
private void setGroupIdUnavailable(@NonNull Job job) {
String groupId = job.getGroupId();
if (groupId != null) {
activeGroupIds.put(groupId, job);
}
}
}

View File

@ -0,0 +1,80 @@
package org.thoughtcrime.securesms.jobmanager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import androidx.work.Data;
/**
* A wrapper around {@link Data} that does its best to throw an exception whenever a key isn't
* present in the {@link Data} object.
*/
public class SafeData {
private final static int INVALID_INT = Integer.MIN_VALUE;
private final static long INVALID_LONG = Long.MIN_VALUE;
private final Data data;
public SafeData(@NonNull Data data) {
this.data = data;
}
public int getInt(@NonNull String key) {
int value = data.getInt(key, INVALID_INT);
if (value == INVALID_INT) {
throw new IllegalStateException("Missing key: " + key);
}
return value;
}
public long getLong(@NonNull String key) {
long value = data.getLong(key, INVALID_LONG);
if (value == INVALID_LONG) {
throw new IllegalStateException("Missing key: " + key);
}
return value;
}
public @NonNull String getString(@NonNull String key) {
String value = data.getString(key);
if (value == null) {
throw new IllegalStateException("Missing key: " + key);
}
return value;
}
public @Nullable String getNullableString(@NonNull String key) {
return data.getString(key);
}
public @NonNull String[] getStringArray(@NonNull String key) {
String[] value = data.getStringArray(key);
if (value == null) {
throw new IllegalStateException("Missing key: " + key);
}
return value;
}
public @NonNull long[] getLongArray(@NonNull String key) {
long[] value = data.getLongArray(key);
if (value == null) {
throw new IllegalStateException("Missing key: " + key);
}
return value;
}
public boolean getBoolean(@NonNull String key, boolean defaultValue) {
return data.getBoolean(key, defaultValue);
}
}

View File

@ -1,36 +0,0 @@
package org.thoughtcrime.securesms.jobmanager.dependencies;
import android.content.Context;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.requirements.Requirement;
public class AggregateDependencyInjector {
private final DependencyInjector dependencyInjector;
public AggregateDependencyInjector(DependencyInjector dependencyInjector) {
this.dependencyInjector = dependencyInjector;
}
public void injectDependencies(Context context, Job job) {
if (job instanceof ContextDependent) {
((ContextDependent)job).setContext(context);
}
for (Requirement requirement : job.getRequirements()) {
if (requirement instanceof ContextDependent) {
((ContextDependent)requirement).setContext(context);
}
}
if (dependencyInjector != null) {
dependencyInjector.injectDependencies(job);
for (Requirement requirement : job.getRequirements()) {
dependencyInjector.injectDependencies(requirement);
}
}
}
}

View File

@ -16,7 +16,6 @@
*/
package org.thoughtcrime.securesms.jobmanager.persistence;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
@ -24,7 +23,6 @@ import android.database.sqlite.SQLiteOpenHelper;
import org.thoughtcrime.securesms.jobmanager.EncryptionKeys;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.dependencies.AggregateDependencyInjector;
import org.thoughtcrime.securesms.logging.Log;
import java.io.IOException;
@ -43,38 +41,18 @@ public class PersistentStorage {
private static final String DATABASE_CREATE = String.format("CREATE TABLE %s (%s INTEGER PRIMARY KEY, %s TEXT NOT NULL, %s INTEGER DEFAULT 0);",
TABLE_NAME, ID, ITEM, ENCRYPTED);
private final Context context;
private final DatabaseHelper databaseHelper;
private final JobSerializer jobSerializer;
private final AggregateDependencyInjector dependencyInjector;
private final DatabaseHelper databaseHelper;
private final JobSerializer jobSerializer;
public PersistentStorage(Context context, String name,
JobSerializer serializer,
AggregateDependencyInjector dependencyInjector)
{
public PersistentStorage(Context context, String name, JobSerializer serializer) {
this.databaseHelper = new DatabaseHelper(context, "_jobqueue-" + name);
this.context = context;
this.jobSerializer = serializer;
this.dependencyInjector = dependencyInjector;
}
public void store(Job job) throws IOException {
ContentValues contentValues = new ContentValues();
contentValues.put(ITEM, jobSerializer.serialize(job));
contentValues.put(ENCRYPTED, job.getEncryptionKeys() != null);
long id = databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
job.setPersistentId(id);
}
public List<Job> getAllUnencrypted() {
return getJobs(null, ENCRYPTED + " = 0");
}
public List<Job> getAllEncrypted(EncryptionKeys keys) {
return getJobs(keys, ENCRYPTED + " = 1");
}
private List<Job> getJobs(EncryptionKeys keys, String where) {
List<Job> results = new LinkedList<>();
SQLiteDatabase database = databaseHelper.getReadableDatabase();
@ -90,11 +68,6 @@ public class PersistentStorage {
try{
Job job = jobSerializer.deserialize(keys, encrypted, item);
job.setPersistentId(id);
job.setEncryptionKeys(keys);
dependencyInjector.injectDependencies(context, job);
results.add(job);
} catch (IOException e) {
Log.w("PersistentStore", e);

View File

@ -1,35 +0,0 @@
package org.thoughtcrime.securesms.jobmanager.requirements;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.logging.Log;
import java.util.UUID;
public class BackoffReceiver extends BroadcastReceiver {
private static final String TAG = BackoffReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Received an alarm to retry a job with ID: " + intent.getAction());
ApplicationContext.getInstance(context).getJobManager().onRequirementStatusChanged();
}
public static void setUniqueAlarm(@NonNull Context context, long time) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, BackoffReceiver.class);
intent.setAction(BuildConfig.APPLICATION_ID + UUID.randomUUID().toString());
alarmManager.set(AlarmManager.RTC_WAKEUP, time, PendingIntent.getBroadcast(context, 0, intent, 0));
Log.i(TAG, "Set an alarm to retry a job in " + (time - System.currentTimeMillis()) + " ms with ID: " + intent.getAction());
}
}

View File

@ -31,15 +31,6 @@ public class NetworkBackoffRequirement implements Requirement, ContextDependent
@Override
public void onRetry(@NonNull Job job) {
Log.i(TAG, "onRetry()");
if (!(new NetworkRequirement(context).isPresent())) {
Log.i(TAG, "No network. Resetting run stats.");
job.resetRunStats();
return;
}
BackoffReceiver.setUniqueAlarm(context, NetworkBackoffRequirement.calculateNextRunTime(job));
}
@Override
@ -48,9 +39,6 @@ public class NetworkBackoffRequirement implements Requirement, ContextDependent
}
private static long calculateNextRunTime(@NonNull Job job) {
long targetTime = job.getLastRunTime() + (long) (Math.pow(2, job.getRunIteration() - 1) * 1000);
long furthestTime = System.currentTimeMillis() + MAX_WAIT;
return Math.min(targetTime, Math.min(furthestTime, job.getRetryUntil()));
return 0;
}
}

View File

@ -1,8 +1,11 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.greenrobot.eventbus.EventBus;
@ -15,8 +18,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.AttachmentUtil;
@ -36,24 +37,34 @@ import java.io.InputStream;
import javax.inject.Inject;
import androidx.work.Data;
public class AttachmentDownloadJob extends MasterSecretJob implements InjectableType {
private static final long serialVersionUID = 2L;
private static final int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024;
private static final String TAG = AttachmentDownloadJob.class.getSimpleName();
private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_PART_ROW_ID = "part_row_id";
private static final String KEY_PAR_UNIQUE_ID = "part_unique_id";
private static final String KEY_MANUAL = "part_manual";
@Inject transient SignalServiceMessageReceiver messageReceiver;
private final long messageId;
private final long partRowId;
private final long partUniqueId;
private final boolean manual;
private long messageId;
private long partRowId;
private long partUniqueId;
private boolean manual;
public AttachmentDownloadJob() {
super(null, null);
}
public AttachmentDownloadJob(Context context, long messageId, AttachmentId attachmentId, boolean manual) {
super(context, JobParameters.newBuilder()
.withGroupId(AttachmentDownloadJob.class.getCanonicalName())
.withRequirement(new MasterSecretRequirement(context))
.withRequirement(new NetworkRequirement(context))
.withPersistence()
.withMasterSecretRequirement()
.withNetworkRequirement()
.create());
this.messageId = messageId;
@ -62,6 +73,23 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
this.manual = manual;
}
@Override
protected void initialize(@NonNull SafeData data) {
messageId = data.getLong(KEY_MESSAGE_ID);
partRowId = data.getLong(KEY_PART_ROW_ID);
partUniqueId = data.getLong(KEY_PAR_UNIQUE_ID);
manual = data.getBoolean(KEY_MANUAL, false);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId)
.putLong(KEY_PART_ROW_ID, partRowId)
.putLong(KEY_PAR_UNIQUE_ID, partUniqueId)
.putBoolean(KEY_MANUAL, manual)
.build();
}
@Override
public void onAdded() {
Log.i(TAG, "onAdded() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual);

View File

@ -1,86 +0,0 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.whispersystems.libsignal.InvalidMessageException;
import java.io.IOException;
import java.util.Arrays;
public class AttachmentFileNameJob extends MasterSecretJob {
private static final long serialVersionUID = 1L;
private final long attachmentRowId;
private final long attachmentUniqueId;
private final String encryptedFileName;
public AttachmentFileNameJob(@NonNull Context context, @NonNull AsymmetricMasterSecret asymmetricMasterSecret,
@NonNull DatabaseAttachment attachment, @NonNull IncomingMediaMessage message)
{
super(context, new JobParameters.Builder().withPersistence()
.withRequirement(new MasterSecretRequirement(context))
.create());
this.attachmentRowId = attachment.getAttachmentId().getRowId();
this.attachmentUniqueId = attachment.getAttachmentId().getUniqueId();
this.encryptedFileName = getEncryptedFileName(asymmetricMasterSecret, attachment, message);
}
@Override
public void onRun(MasterSecret masterSecret) throws IOException, InvalidMessageException {
if (encryptedFileName == null) return;
AttachmentId attachmentId = new AttachmentId(attachmentRowId, attachmentUniqueId);
String plaintextFileName = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret)).decryptBody(encryptedFileName);
DatabaseFactory.getAttachmentDatabase(context).updateAttachmentFileName(attachmentId, plaintextFileName);
}
@Override
public boolean onShouldRetryThrowable(Exception exception) {
return false;
}
@Override
public void onAdded() {
}
@Override
public void onCanceled() {
}
private @Nullable String getEncryptedFileName(@NonNull AsymmetricMasterSecret asymmetricMasterSecret,
@NonNull DatabaseAttachment attachment,
@NonNull IncomingMediaMessage mediaMessage)
{
for (Attachment messageAttachment : mediaMessage.getAttachments()) {
if (mediaMessage.getAttachments().size() == 1 ||
(messageAttachment.getDigest() != null && Arrays.equals(messageAttachment.getDigest(), attachment.getDigest())))
{
if (messageAttachment.getFileName() == null) return null;
else return new AsymmetricMasterCipher(asymmetricMasterSecret).encryptBody(messageAttachment.getFileName());
}
}
return null;
}
}

View File

@ -10,8 +10,7 @@ import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
@ -30,6 +29,8 @@ import java.io.InputStream;
import javax.inject.Inject;
import androidx.work.Data;
public class AvatarDownloadJob extends MasterSecretJob implements InjectableType {
private static final int MAX_AVATAR_SIZE = 20 * 1024 * 1024;
@ -37,22 +38,38 @@ public class AvatarDownloadJob extends MasterSecretJob implements InjectableType
private static final String TAG = AvatarDownloadJob.class.getSimpleName();
private static final String KEY_GROUP_ID = "group_id";
@Inject transient SignalServiceMessageReceiver receiver;
private final byte[] groupId;
private byte[] groupId;
public AvatarDownloadJob() {
super(null, null);
}
public AvatarDownloadJob(Context context, @NonNull byte[] groupId) {
super(context, JobParameters.newBuilder()
.withRequirement(new MasterSecretRequirement(context))
.withRequirement(new NetworkRequirement(context))
.withPersistence()
.withMasterSecretRequirement()
.withNetworkRequirement()
.create());
this.groupId = groupId;
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
try {
groupId = GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID));
} catch (IOException e) {
throw new AssertionError(e);
}
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false)).build();
}
@Override
public void onRun(MasterSecret masterSecret) throws IOException {

View File

@ -1,13 +1,15 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.whispersystems.libsignal.InvalidKeyIdException;
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
import org.whispersystems.libsignal.state.SignedPreKeyStore;
@ -24,6 +26,8 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import androidx.work.Data;
import static org.thoughtcrime.securesms.dependencies.AxolotlStorageModule.SignedPreKeyStoreFactory;
public class CleanPreKeysJob extends MasterSecretJob implements InjectableType {
@ -35,17 +39,25 @@ public class CleanPreKeysJob extends MasterSecretJob implements InjectableType {
@Inject transient SignalServiceAccountManager accountManager;
@Inject transient SignedPreKeyStoreFactory signedPreKeyStoreFactory;
public CleanPreKeysJob() {
super(null, null);
}
public CleanPreKeysJob(Context context) {
super(context, JobParameters.newBuilder()
.withGroupId(CleanPreKeysJob.class.getSimpleName())
.withRequirement(new MasterSecretRequirement(context))
.withMasterSecretRequirement()
.withRetryCount(5)
.create());
}
@Override
public void onAdded() {
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override

View File

@ -1,14 +1,14 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKeyPair;
@ -20,6 +20,8 @@ import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
public class CreateSignedPreKeyJob extends MasterSecretJob implements InjectableType {
private static final long serialVersionUID = 1L;
@ -28,17 +30,26 @@ public class CreateSignedPreKeyJob extends MasterSecretJob implements Injectable
@Inject transient SignalServiceAccountManager accountManager;
public CreateSignedPreKeyJob() {
super(null, null);
}
public CreateSignedPreKeyJob(Context context) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withRequirement(new NetworkRequirement(context))
.withRequirement(new MasterSecretRequirement(context))
.withNetworkRequirement()
.withMasterSecretRequirement()
.withGroupId(CreateSignedPreKeyJob.class.getSimpleName())
.create());
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun(MasterSecret masterSecret) throws IOException {

View File

@ -1,26 +1,36 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
import androidx.work.Data;
public class DirectoryRefreshJob extends ContextJob {
private static final String TAG = DirectoryRefreshJob.class.getSimpleName();
private static final String KEY_ADDRESS = "address";
private static final String KEY_NOTIFY_OF_NEW_USERS = "notify_of_new_users";
@Nullable private transient Recipient recipient;
private transient boolean notifyOfNewUsers;
public DirectoryRefreshJob() {
super(null, null);
}
public DirectoryRefreshJob(@NonNull Context context, boolean notifyOfNewUsers) {
this(context, null, notifyOfNewUsers);
}
@ -31,7 +41,7 @@ public class DirectoryRefreshJob extends ContextJob {
{
super(context, JobParameters.newBuilder()
.withGroupId(DirectoryRefreshJob.class.getSimpleName())
.withRequirement(new NetworkRequirement(context))
.withNetworkRequirement()
.create());
this.recipient = recipient;
@ -39,23 +49,29 @@ public class DirectoryRefreshJob extends ContextJob {
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
String serializedAddress = data.getNullableString(KEY_ADDRESS);
Address address = serializedAddress != null ? Address.fromSerialized(serializedAddress) : null;
recipient = address != null ? Recipient.from(context, address, true) : null;
notifyOfNewUsers = data.getBoolean(KEY_NOTIFY_OF_NEW_USERS, false);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_ADDRESS, recipient != null ? recipient.getAddress().serialize() : null)
.putBoolean(KEY_NOTIFY_OF_NEW_USERS, notifyOfNewUsers)
.build();
}
@Override
public void onRun() throws IOException {
Log.i(TAG, "DirectoryRefreshJob.onRun()");
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Directory Refresh");
try {
wakeLock.acquire();
if (recipient == null) {
DirectoryHelper.refreshDirectory(context, notifyOfNewUsers);
} else {
DirectoryHelper.refreshDirectoryFor(context, recipient);
}
} finally {
if (wakeLock.isHeld()) wakeLock.release();
if (recipient == null) {
DirectoryHelper.refreshDirectory(context, notifyOfNewUsers);
} else {
DirectoryHelper.refreshDirectoryFor(context, recipient);
}
}

View File

@ -21,7 +21,10 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import com.google.android.gms.common.ConnectionResult;
@ -32,7 +35,6 @@ import org.thoughtcrime.securesms.PlayServicesProblemActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
@ -41,6 +43,8 @@ import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulRespons
import javax.inject.Inject;
import androidx.work.Data;
public class GcmRefreshJob extends ContextJob implements InjectableType {
private static final String TAG = GcmRefreshJob.class.getSimpleName();
@ -49,15 +53,27 @@ public class GcmRefreshJob extends ContextJob implements InjectableType {
@Inject transient SignalServiceAccountManager textSecureAccountManager;
public GcmRefreshJob() {
super(null, null);
}
public GcmRefreshJob(Context context) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withGroupId(GcmRefreshJob.class.getSimpleName())
.withDuplicatesIgnored(true)
.withNetworkRequirement()
.withRetryCount(1)
.create());
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun() throws Exception {

View File

@ -4,6 +4,8 @@ package org.thoughtcrime.securesms.jobs;
import android.Manifest;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.R;
@ -24,21 +26,32 @@ import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import androidx.work.Data;
public class LocalBackupJob extends ContextJob {
private static final String TAG = LocalBackupJob.class.getSimpleName();
public LocalBackupJob() {
super(null, null);
}
public LocalBackupJob(@NonNull Context context) {
super(context, JobParameters.newBuilder()
.withGroupId("__LOCAL_BACKUP__")
.withWakeLock(true, 10, TimeUnit.SECONDS)
.withDuplicatesIgnored(true)
.create());
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun() throws NoExternalStorageException, IOException {

View File

@ -2,7 +2,10 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import com.google.android.mms.pdu_alt.CharacterSets;
@ -20,8 +23,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.ApnUnavailableException;
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
@ -45,7 +46,8 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import androidx.work.Data;
public class MmsDownloadJob extends MasterSecretJob {
@ -53,17 +55,23 @@ public class MmsDownloadJob extends MasterSecretJob {
private static final String TAG = MmsDownloadJob.class.getSimpleName();
private final long messageId;
private final long threadId;
private final boolean automatic;
private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_THREAD_ID = "thread_id";
private static final String KEY_AUTOMATIC = "automatic";
private long messageId;
private long threadId;
private boolean automatic;
public MmsDownloadJob() {
super(null, null);
}
public MmsDownloadJob(Context context, long messageId, long threadId, boolean automatic) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withRequirement(new MasterSecretRequirement(context))
.withRequirement(new NetworkRequirement(context))
.withMasterSecretRequirement()
.withMasterSecretRequirement()
.withGroupId("mms-operation")
.withWakeLock(true, 30, TimeUnit.SECONDS)
.create());
this.messageId = messageId;
@ -71,6 +79,21 @@ public class MmsDownloadJob extends MasterSecretJob {
this.automatic = automatic;
}
@Override
protected void initialize(@NonNull SafeData data) {
messageId = data.getLong(KEY_MESSAGE_ID);
threadId = data.getLong(KEY_THREAD_ID);
automatic = data.getBoolean(KEY_AUTOMATIC, false);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId)
.putLong(KEY_THREAD_ID, threadId)
.putBoolean(KEY_AUTOMATIC, automatic)
.build();
}
@Override
public void onAdded() {
if (automatic && KeyCachingService.getMasterSecret(context) == null) {

View File

@ -1,7 +1,11 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.google.android.mms.pdu_alt.GenericPdu;
@ -15,29 +19,51 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import androidx.work.Data;
public class MmsReceiveJob extends ContextJob {
private static final long serialVersionUID = 1L;
private static final String TAG = MmsReceiveJob.class.getSimpleName();
private final byte[] data;
private final int subscriptionId;
private static final String KEY_DATA = "data";
private static final String KEY_SUBSCRIPTION_ID = "subscription_id";
private byte[] data;
private int subscriptionId;
public MmsReceiveJob() {
super(null, null);
}
public MmsReceiveJob(Context context, byte[] data, int subscriptionId) {
super(context, JobParameters.newBuilder()
.withWakeLock(true)
.withPersistence().create());
super(context, JobParameters.newBuilder().create());
this.data = data;
this.subscriptionId = subscriptionId;
}
@Override
public void onAdded() {
protected void initialize(@NonNull SafeData data) {
try {
this.data = Base64.decode(data.getString(KEY_DATA));
} catch (IOException e) {
throw new AssertionError(e);
}
subscriptionId = data.getInt(KEY_SUBSCRIPTION_ID);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_DATA, Base64.encodeBytes(data))
.putInt(KEY_SUBSCRIPTION_ID, subscriptionId)
.build();
}
@Override

View File

@ -1,7 +1,10 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import android.webkit.MimeTypeMap;
@ -27,8 +30,6 @@ import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException;
@ -49,28 +50,41 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import androidx.work.Data;
public class MmsSendJob extends SendJob {
private static final long serialVersionUID = 0L;
private static final String TAG = MmsSendJob.class.getSimpleName();
private final long messageId;
private static final String KEY_MESSAGE_ID = "message_id";
private long messageId;
public MmsSendJob() {
super(null, null);
}
public MmsSendJob(Context context, long messageId) {
super(context, JobParameters.newBuilder()
.withGroupId("mms-operation")
.withRequirement(new NetworkRequirement(context))
.withRequirement(new MasterSecretRequirement(context))
.withPersistence()
.withNetworkRequirement()
.withMasterSecretRequirement()
.withRetryCount(15)
.create());
this.messageId = messageId;
}
@Override
public void onAdded() {
Log.i(TAG, "onAdded() messageId: " + messageId);
protected void initialize(@NonNull SafeData data) {
messageId = data.getLong(KEY_MESSAGE_ID);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId).build();
}
@Override

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -8,8 +9,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientReader;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -24,6 +24,8 @@ import java.util.List;
import javax.inject.Inject;
import androidx.work.Data;
public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements InjectableType {
private static final long serialVersionUID = 1L;
@ -32,15 +34,27 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
@Inject transient SignalServiceMessageSender messageSender;
public MultiDeviceBlockedUpdateJob() {
super(null, null);
}
public MultiDeviceBlockedUpdateJob(Context context) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withRequirement(new MasterSecretRequirement(context))
.withNetworkRequirement()
.withMasterSecretRequirement()
.withGroupId(MultiDeviceBlockedUpdateJob.class.getSimpleName())
.withPersistence()
.create());
}
@Override
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun(MasterSecret masterSecret)
throws IOException, UntrustedIdentityException
@ -71,11 +85,6 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
return false;
}
@Override
public void onAdded() {
}
@Override
public void onCanceled() {

View File

@ -20,8 +20,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -50,6 +49,8 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import androidx.work.Data;
public class MultiDeviceContactUpdateJob extends MasterSecretJob implements InjectableType {
private static final long serialVersionUID = 2L;
@ -58,12 +59,19 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
private static final long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6);
private static final String KEY_ADDRESS = "address";
private static final String KEY_FORCE_SYNC = "force_sync";
@Inject transient SignalServiceMessageSender messageSender;
private final @Nullable String address;
private @Nullable String address;
private boolean forceSync;
public MultiDeviceContactUpdateJob() {
super(null, null);
}
public MultiDeviceContactUpdateJob(@NonNull Context context) {
this(context, false);
}
@ -78,10 +86,9 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address, boolean forceSync) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withRequirement(new MasterSecretRequirement(context))
.withNetworkRequirement()
.withMasterSecretRequirement()
.withGroupId(MultiDeviceContactUpdateJob.class.getSimpleName())
.withPersistence()
.create());
this.forceSync = forceSync;
@ -90,6 +97,19 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
else this.address = null;
}
@Override
protected void initialize(@NonNull SafeData data) {
address = data.getNullableString(KEY_ADDRESS);
forceSync = data.getBoolean(KEY_FORCE_SYNC, false);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_ADDRESS, address)
.putBoolean(KEY_FORCE_SYNC, forceSync)
.build();
}
@Override
public void onRun(MasterSecret masterSecret)
throws IOException, UntrustedIdentityException, NetworkException
@ -201,11 +221,6 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
return false;
}
@Override
public void onAdded() {
}
@Override
public void onCanceled() {

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.crypto.MasterSecret;
@ -8,12 +9,11 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@ -34,6 +34,8 @@ import java.util.List;
import javax.inject.Inject;
import androidx.work.Data;
public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements InjectableType {
private static final long serialVersionUID = 1L;
@ -41,15 +43,27 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
@Inject transient SignalServiceMessageSender messageSender;
public MultiDeviceGroupUpdateJob() {
super(null, null);
}
public MultiDeviceGroupUpdateJob(Context context) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withRequirement(new MasterSecretRequirement(context))
.withNetworkRequirement()
.withMasterSecretRequirement()
.withGroupId(MultiDeviceGroupUpdateJob.class.getSimpleName())
.withPersistence()
.create());
}
@Override
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun(MasterSecret masterSecret) throws Exception {
File contactDataFile = createTempFile("multidevice-contact-update");
@ -102,11 +116,6 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
return false;
}
@Override
public void onAdded() {
}
@Override
public void onCanceled() {

View File

@ -2,13 +2,15 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -27,6 +29,8 @@ import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
public class MultiDeviceProfileKeyUpdateJob extends MasterSecretJob implements InjectableType {
private static final long serialVersionUID = 1L;
@ -34,14 +38,26 @@ public class MultiDeviceProfileKeyUpdateJob extends MasterSecretJob implements I
@Inject transient SignalServiceMessageSender messageSender;
public MultiDeviceProfileKeyUpdateJob() {
super(null, null);
}
public MultiDeviceProfileKeyUpdateJob(Context context) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withPersistence()
.withNetworkRequirement()
.withGroupId(MultiDeviceProfileKeyUpdateJob.class.getSimpleName())
.create());
}
@Override
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun(MasterSecret masterSecret) throws IOException, UntrustedIdentityException {
if (!TextSecurePreferences.isMultiDevice(getContext())) {
@ -79,11 +95,6 @@ public class MultiDeviceProfileKeyUpdateJob extends MasterSecretJob implements I
return false;
}
@Override
public void onAdded() {
}
@Override
public void onCanceled() {
Log.w(TAG, "Profile key sync failed!");

View File

@ -2,10 +2,11 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -18,28 +19,42 @@ import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
public class MultiDeviceReadReceiptUpdateJob extends ContextJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = MultiDeviceReadReceiptUpdateJob.class.getSimpleName();
private static final String KEY_ENABLED = "enabled";
@Inject transient SignalServiceMessageSender messageSender;
private final boolean enabled;
private boolean enabled;
public MultiDeviceReadReceiptUpdateJob() {
super(null, null);
}
public MultiDeviceReadReceiptUpdateJob(Context context, boolean enabled) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withGroupId("__MULTI_DEVICE_READ_RECEIPT_UPDATE_JOB__")
.withRequirement(new NetworkRequirement(context))
.withNetworkRequirement()
.create());
this.enabled = enabled;
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
enabled = data.getBoolean(KEY_ENABLED, false);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putBoolean(KEY_ENABLED, enabled).build();
}
@Override
public void onRun() throws IOException, UntrustedIdentityException {

View File

@ -1,14 +1,18 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
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;
@ -18,25 +22,33 @@ import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javax.inject.Inject;
import androidx.work.Data;
public class MultiDeviceReadUpdateJob extends MasterSecretJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = MultiDeviceReadUpdateJob.class.getSimpleName();
private final List<SerializableSyncMessageId> messageIds;
private static final String KEY_MESSAGE_IDS = "message_ids";
private List<SerializableSyncMessageId> messageIds;
@Inject transient SignalServiceMessageSender messageSender;
public MultiDeviceReadUpdateJob() {
super(null, null);
}
public MultiDeviceReadUpdateJob(Context context, List<SyncMessageId> messageIds) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withRequirement(new MasterSecretRequirement(context))
.withPersistence()
.withNetworkRequirement()
.withMasterSecretRequirement()
.create());
this.messageIds = new LinkedList<>();
@ -46,6 +58,34 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
}
}
@Override
protected void initialize(@NonNull SafeData data) {
String[] ids = data.getStringArray(KEY_MESSAGE_IDS);
messageIds = new ArrayList<>(ids.length);
for (String id : ids) {
try {
messageIds.add(JsonUtils.fromJson(id, SerializableSyncMessageId.class));
} catch (IOException e) {
throw new AssertionError(e);
}
}
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
String[] ids = new String[messageIds.size()];
for (int i = 0; i < ids.length; i++) {
try {
ids[i] = JsonUtils.toJson(messageIds.get(i));
} catch (IOException e) {
throw new AssertionError(e);
}
}
return dataBuilder.putStringArray(KEY_MESSAGE_IDS, ids).build();
}
@Override
public void onRun(MasterSecret masterSecret) throws IOException, UntrustedIdentityException {
@ -68,11 +108,6 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
return exception instanceof PushNetworkException;
}
@Override
public void onAdded() {
}
@Override
public void onCanceled() {
@ -82,10 +117,13 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
private static final long serialVersionUID = 1L;
@JsonProperty
private final String sender;
@JsonProperty
private final long timestamp;
private SerializableSyncMessageId(String sender, long timestamp) {
private SerializableSyncMessageId(@JsonProperty("sender") String sender, @JsonProperty("timestamp") long timestamp) {
this.sender = sender;
this.timestamp = timestamp;
}

View File

@ -2,13 +2,16 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
@ -22,24 +25,34 @@ import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
public class MultiDeviceVerifiedUpdateJob extends ContextJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = MultiDeviceVerifiedUpdateJob.class.getSimpleName();
private static final String KEY_DESTINATION = "destination";
private static final String KEY_IDENTITY_KEY = "identity_key";
private static final String KEY_VERIFIED_STATUS = "verified_status";
private static final String KEY_TIMESTAMP = "timestamp";
@Inject
transient SignalServiceMessageSender messageSender;
private final String destination;
private final byte[] identityKey;
private final VerifiedStatus verifiedStatus;
private final long timestamp;
private String destination;
private byte[] identityKey;
private VerifiedStatus verifiedStatus;
private long timestamp;
public MultiDeviceVerifiedUpdateJob() {
super(null, null);
}
public MultiDeviceVerifiedUpdateJob(Context context, Address destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withPersistence()
.withNetworkRequirement()
.withGroupId("__MULTI_DEVICE_VERIFIED_UPDATE__")
.create());
@ -49,6 +62,28 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
this.timestamp = System.currentTimeMillis();
}
@Override
protected void initialize(@NonNull SafeData data) {
destination = data.getString(KEY_DESTINATION);
verifiedStatus = VerifiedStatus.forState(data.getInt(KEY_VERIFIED_STATUS));
timestamp = data.getLong(KEY_TIMESTAMP);
try {
identityKey = Base64.decode(data.getString(KEY_IDENTITY_KEY));
} catch (IOException e) {
throw new AssertionError(e);
}
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_DESTINATION, destination)
.putString(KEY_IDENTITY_KEY, Base64.encodeBytes(identityKey))
.putInt(KEY_VERIFIED_STATUS, verifiedStatus.toInt())
.putLong(KEY_TIMESTAMP, timestamp)
.build();
}
@Override
public void onRun() throws IOException, UntrustedIdentityException {
try {
@ -90,11 +125,6 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
return exception instanceof PushNetworkException;
}
@Override
public void onAdded() {
}
@Override
public void onCanceled() {

View File

@ -1,6 +1,9 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
@ -10,12 +13,20 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import java.io.IOException;
import androidx.work.Data;
public class PushContentReceiveJob extends PushReceivedJob {
private static final long serialVersionUID = 5685475456901715638L;
private static final String TAG = PushContentReceiveJob.class.getSimpleName();
private final String data;
private static final String KEY_DATA = "data";
private String data;
public PushContentReceiveJob() {
super(null, null);
}
public PushContentReceiveJob(Context context) {
super(context, JobParameters.newBuilder().create());
@ -23,16 +34,19 @@ public class PushContentReceiveJob extends PushReceivedJob {
}
public PushContentReceiveJob(Context context, String data) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withWakeLock(true)
.create());
super(context, JobParameters.newBuilder().create());
this.data = data;
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
this.data = data.getNullableString(KEY_DATA);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_DATA, data).build();
}
@Override
public void onRun() {

View File

@ -7,6 +7,8 @@ import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import android.util.Pair;
@ -98,7 +100,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import androidx.work.Data;
public class PushDecryptJob extends ContextJob {
@ -106,8 +109,15 @@ public class PushDecryptJob extends ContextJob {
public static final String TAG = PushDecryptJob.class.getSimpleName();
private final long messageId;
private final long smsMessageId;
private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_SMS_MESSAGE_ID = "sms_message_id";
private long messageId;
private long smsMessageId;
public PushDecryptJob() {
super(null, null);
}
public PushDecryptJob(Context context, long pushMessageId) {
this(context, pushMessageId, -1);
@ -115,16 +125,24 @@ public class PushDecryptJob extends ContextJob {
public PushDecryptJob(Context context, long pushMessageId, long smsMessageId) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withGroupId("__PUSH_DECRYPT_JOB__")
.withWakeLock(true, 5, TimeUnit.SECONDS)
.create());
this.messageId = pushMessageId;
this.smsMessageId = smsMessageId;
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
messageId = data.getLong(KEY_MESSAGE_ID);
smsMessageId = data.getLong(KEY_SMS_MESSAGE_ID);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId)
.putLong(KEY_SMS_MESSAGE_ID, smsMessageId)
.build();
}
@Override
public void onRun() throws NoSuchMessageException {

View File

@ -16,8 +16,7 @@ import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkBackoffRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException;
@ -48,6 +47,8 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import androidx.work.Data;
public class PushGroupSendJob extends PushSendJob implements InjectableType {
private static final long serialVersionUID = 1L;
@ -56,16 +57,22 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
@Inject transient SignalServiceMessageSender messageSender;
private final long messageId;
private final long filterRecipientId; // Deprecated
private final String filterAddress;
private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_FILTER_ADDRESS = "filter_address";
private long messageId;
private long filterRecipientId; // Deprecated
private String filterAddress;
public PushGroupSendJob() {
super(null, null);
}
public PushGroupSendJob(Context context, long messageId, @NonNull Address destination, @Nullable Address filterAddress) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withGroupId(destination.toGroupString())
.withRequirement(new MasterSecretRequirement(context))
.withRequirement(new NetworkBackoffRequirement(context))
.withMasterSecretRequirement()
.withNetworkRequirement()
.withRetryDuration(TimeUnit.DAYS.toMillis(1))
.create());
@ -75,8 +82,16 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
}
@Override
public void onAdded() {
Log.i(TAG, "onAdded() messageId: " + messageId);
protected void initialize(@NonNull SafeData data) {
messageId = data.getLong(KEY_MESSAGE_ID);
filterAddress = data.getNullableString(KEY_FILTER_ADDRESS);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId)
.putString(KEY_FILTER_ADDRESS, filterAddress)
.build();
}
@Override

View File

@ -2,6 +2,9 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.database.Address;
@ -10,7 +13,6 @@ import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
@ -28,26 +30,34 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import androidx.work.Data;
public class PushGroupUpdateJob extends ContextJob implements InjectableType {
private static final String TAG = PushGroupUpdateJob.class.getSimpleName();
private static final long serialVersionUID = 0L;
private static final String KEY_SOURCE = "source";
private static final String KEY_GROUP_ID = "group_id";
@Inject transient SignalServiceMessageSender messageSender;
private final String source;
private final byte[] groupId;
private String source;
private byte[] groupId;
public PushGroupUpdateJob() {
super(null, null);
}
public PushGroupUpdateJob(Context context, String source, byte[] groupId) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withRequirement(new NetworkRequirement(context))
.withRetryCount(50)
.withNetworkRequirement()
.withRetryDuration(TimeUnit.DAYS.toMillis(1))
.create());
this.source = source;
@ -55,7 +65,21 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType {
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
source = data.getString(KEY_SOURCE);
try {
groupId = GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID));
} catch (IOException e) {
throw new AssertionError(e);
}
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_SOURCE, source)
.putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false))
.build();
}
@Override
public void onRun() throws IOException, UntrustedIdentityException {

View File

@ -1,6 +1,9 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
@ -32,24 +35,37 @@ import java.util.List;
import javax.inject.Inject;
import androidx.work.Data;
public class PushMediaSendJob extends PushSendJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = PushMediaSendJob.class.getSimpleName();
private static final String KEY_MESSAGE_ID = "message_id";
@Inject transient SignalServiceMessageSender messageSender;
private final long messageId;
private long messageId;
public PushMediaSendJob() {
super(null, null);
}
public PushMediaSendJob(Context context, long messageId, Address destination) {
super(context, constructParameters(context, destination));
super(context, constructParameters(destination));
this.messageId = messageId;
}
@Override
public void onAdded() {
Log.i(TAG, "onAdded() messageId: " + messageId);
protected void initialize(@NonNull SafeData data) {
messageId = data.getLong(KEY_MESSAGE_ID);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId).build();
}
@Override

View File

@ -1,35 +1,47 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import androidx.work.Data;
public class PushNotificationReceiveJob extends PushReceivedJob implements InjectableType {
private static final String TAG = PushNotificationReceiveJob.class.getSimpleName();
@Inject transient SignalServiceMessageReceiver receiver;
public PushNotificationReceiveJob() {
super(null, null);
}
public PushNotificationReceiveJob(Context context) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withNetworkRequirement()
.withGroupId("__notification_received")
.withWakeLock(true, 30, TimeUnit.SECONDS).create());
.create());
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun() throws IOException {

View File

@ -15,8 +15,6 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkBackoffRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
@ -49,12 +47,11 @@ public abstract class PushSendJob extends SendJob {
super(context, parameters);
}
protected static JobParameters constructParameters(Context context, Address destination) {
protected static JobParameters constructParameters(Address destination) {
JobParameters.Builder builder = JobParameters.newBuilder();
builder.withPersistence();
builder.withGroupId(destination.serialize());
builder.withRequirement(new MasterSecretRequirement(context));
builder.withRequirement(new NetworkBackoffRequirement(context));
builder.withMasterSecretRequirement();
builder.withNetworkRequirement();
builder.withRetryDuration(TimeUnit.DAYS.toMillis(1));
return builder.create();
@ -80,7 +77,7 @@ public abstract class PushSendJob extends SendJob {
super.onRetry();
Log.i(TAG, "onRetry()");
if (getRunIteration() > 1) {
if (getRunAttemptCount() > 1) {
Log.i(TAG, "Scheduling service outage detection job.");
ApplicationContext.getInstance(context).getJobManager().add(new ServiceOutageDetectionJob(context));
}

View File

@ -1,6 +1,9 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
@ -26,21 +29,39 @@ import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
public class PushTextSendJob extends PushSendJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = PushTextSendJob.class.getSimpleName();
private static final String KEY_MESSAGE_ID = "message_id";
@Inject transient SignalServiceMessageSender messageSender;
private final long messageId;
private long messageId;
public PushTextSendJob() {
super(null, null);
}
public PushTextSendJob(Context context, long messageId, Address destination) {
super(context, constructParameters(context, destination));
super(context, constructParameters(destination));
this.messageId = messageId;
}
@Override
protected void initialize(@NonNull SafeData data) {
messageId = data.getLong(KEY_MESSAGE_ID);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId).build();
}
@Override
public void onAdded() {
Log.i(TAG, "onAdded() messageId: " + messageId);

View File

@ -1,20 +1,23 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import androidx.work.Data;
public class RefreshAttributesJob extends ContextJob implements InjectableType {
public static final long serialVersionUID = 1L;
@ -23,17 +26,25 @@ public class RefreshAttributesJob extends ContextJob implements InjectableType {
@Inject transient SignalServiceAccountManager signalAccountManager;
public RefreshAttributesJob() {
super(null, null);
}
public RefreshAttributesJob(Context context) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withRequirement(new NetworkRequirement(context))
.withWakeLock(true, 30, TimeUnit.SECONDS)
.withNetworkRequirement()
.withGroupId(RefreshAttributesJob.class.getName())
.create());
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun() throws IOException {

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
@ -8,8 +9,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKeyPair;
@ -24,6 +24,8 @@ import java.util.List;
import javax.inject.Inject;
import androidx.work.Data;
public class RefreshPreKeysJob extends MasterSecretJob implements InjectableType {
private static final String TAG = RefreshPreKeysJob.class.getSimpleName();
@ -32,18 +34,26 @@ public class RefreshPreKeysJob extends MasterSecretJob implements InjectableType
@Inject transient SignalServiceAccountManager accountManager;
public RefreshPreKeysJob() {
super(null, null);
}
public RefreshPreKeysJob(Context context) {
super(context, JobParameters.newBuilder()
.withGroupId(RefreshPreKeysJob.class.getSimpleName())
.withRequirement(new NetworkRequirement(context))
.withRequirement(new MasterSecretRequirement(context))
.withNetworkRequirement()
.withMasterSecretRequirement()
.withRetryCount(5)
.create());
}
@Override
public void onAdded() {
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override

View File

@ -5,7 +5,8 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
@ -18,21 +19,29 @@ import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
public class RequestGroupInfoJob extends ContextJob implements InjectableType {
private static final String TAG = RequestGroupInfoJob.class.getSimpleName();
private static final long serialVersionUID = 0L;
private static final String KEY_SOURCE = "source";
private static final String KEY_GROUP_ID = "group_id";
@Inject transient SignalServiceMessageSender messageSender;
private final String source;
private final byte[] groupId;
private String source;
private byte[] groupId;
public RequestGroupInfoJob() {
super(null, null);
}
public RequestGroupInfoJob(@NonNull Context context, @NonNull String source, @NonNull byte[] groupId) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withPersistence()
.withNetworkRequirement()
.withRetryCount(50)
.create());
@ -41,7 +50,21 @@ public class RequestGroupInfoJob extends ContextJob implements InjectableType {
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
source = data.getString(KEY_SOURCE);
try {
groupId = GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID));
} catch (IOException e) {
throw new AssertionError(e);
}
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_SOURCE, source)
.putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false))
.build();
}
@Override
public void onRun() throws IOException, UntrustedIdentityException {

View File

@ -2,14 +2,17 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Util;
@ -23,21 +26,31 @@ import java.io.InputStream;
import javax.inject.Inject;
import androidx.work.Data;
public class RetrieveProfileAvatarJob extends ContextJob implements InjectableType {
private static final String TAG = RetrieveProfileAvatarJob.class.getSimpleName();
private static final int MAX_PROFILE_SIZE_BYTES = 20 * 1024 * 1024;
private static final String KEY_PROFILE_AVATAR = "profile_avatar";
private static final String KEY_ADDRESS = "address";
@Inject SignalServiceMessageReceiver receiver;
private final String profileAvatar;
private final Recipient recipient;
private String profileAvatar;
private Recipient recipient;
public RetrieveProfileAvatarJob() {
super(null, null);
}
public RetrieveProfileAvatarJob(Context context, Recipient recipient, String profileAvatar) {
super(context, JobParameters.newBuilder()
.withGroupId(RetrieveProfileAvatarJob.class.getSimpleName() + recipient.getAddress().serialize())
.withRequirement(new NetworkRequirement(context))
.withDuplicatesIgnored(true)
.withNetworkRequirement()
.create());
this.recipient = recipient;
@ -45,7 +58,17 @@ public class RetrieveProfileAvatarJob extends ContextJob implements InjectableTy
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
profileAvatar = data.getString(KEY_PROFILE_AVATAR);
recipient = Recipient.from(context, Address.fromSerialized(data.getString(KEY_ADDRESS)), true);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_PROFILE_AVATAR, profileAvatar)
.putString(KEY_ADDRESS, recipient.getAddress().serialize())
.build();
}
@Override
public void onRun() throws IOException {

View File

@ -4,6 +4,9 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
@ -29,16 +32,25 @@ import java.util.List;
import javax.inject.Inject;
import androidx.work.Data;
public class RetrieveProfileJob extends ContextJob implements InjectableType {
private static final String TAG = RetrieveProfileJob.class.getSimpleName();
private static final String KEY_ADDRESS = "address";
@Inject transient SignalServiceMessageReceiver receiver;
private final Recipient recipient;
private Recipient recipient;
public RetrieveProfileJob() {
super(null, null);
}
public RetrieveProfileJob(Context context, Recipient recipient) {
super(context, JobParameters.newBuilder()
.withNetworkRequirement()
.withRetryCount(3)
.create());
@ -46,7 +58,14 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
recipient = Recipient.from(context, Address.fromSerialized(data.getString(KEY_ADDRESS)), true);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_ADDRESS, recipient.getAddress().serialize()).build();
}
@Override
public void onRun() throws IOException, InvalidKeyException {

View File

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
@ -9,8 +10,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKeyPair;
@ -20,23 +20,33 @@ import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
import javax.inject.Inject;
import androidx.work.Data;
public class RotateSignedPreKeyJob extends MasterSecretJob implements InjectableType {
private static final String TAG = RotateSignedPreKeyJob.class.getName();
@Inject transient SignalServiceAccountManager accountManager;
public RotateSignedPreKeyJob() {
super(null, null);
}
public RotateSignedPreKeyJob(Context context) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withRequirement(new MasterSecretRequirement(context))
.withNetworkRequirement()
.withMasterSecretRequirement()
.withRetryCount(5)
.create());
}
@Override
public void onAdded() {
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override

View File

@ -2,11 +2,12 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -16,26 +17,36 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import androidx.work.Data;
public class SendReadReceiptJob extends ContextJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = SendReadReceiptJob.class.getSimpleName();
private static final String KEY_ADDRESS = "address";
private static final String KEY_MESSAGE_IDS = "message_ids";
private static final String KEY_TIMESTAMP = "timestamp";
@Inject transient SignalServiceMessageSender messageSender;
private final String address;
private final List<Long> messageIds;
private final long timestamp;
private String address;
private List<Long> messageIds;
private long timestamp;
public SendReadReceiptJob() {
super(null, null);
}
public SendReadReceiptJob(Context context, Address address, List<Long> messageIds) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withPersistence()
.withNetworkRequirement()
.create());
this.address = address.serialize();
@ -44,7 +55,29 @@ public class SendReadReceiptJob extends ContextJob implements InjectableType {
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
address = data.getString(KEY_ADDRESS);
timestamp = data.getLong(KEY_TIMESTAMP);
long[] ids = data.getLongArray(KEY_MESSAGE_IDS);
messageIds = new ArrayList<>(ids.length);
for (long id : ids) {
messageIds.add(id);
}
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
long[] ids = new long[messageIds.size()];
for (int i = 0; i < ids.length; i++) {
ids[i] = messageIds.get(i);
}
return dataBuilder.putString(KEY_ADDRESS, address)
.putLongArray(KEY_MESSAGE_IDS, ids)
.putLong(KEY_TIMESTAMP, timestamp)
.build();
}
@Override
public void onRun() throws IOException, UntrustedIdentityException {

View File

@ -1,12 +1,13 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -14,6 +15,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.net.InetAddress;
import java.net.UnknownHostException;
import androidx.work.Data;
public class ServiceOutageDetectionJob extends ContextJob {
private static final String TAG = ServiceOutageDetectionJob.class.getSimpleName();
@ -22,16 +25,26 @@ public class ServiceOutageDetectionJob extends ContextJob {
private static final String IP_FAILURE = "127.0.0.2";
private static final long CHECK_TIME = 1000 * 60;
public ServiceOutageDetectionJob() {
super(null, null);
}
public ServiceOutageDetectionJob(Context context) {
super(context, new JobParameters.Builder()
.withGroupId(ServiceOutageDetectionJob.class.getSimpleName())
.withRequirement(new NetworkRequirement(context))
.withDuplicatesIgnored(true)
.withNetworkRequirement()
.withRetryCount(5)
.create());
}
@Override
public void onAdded() {
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override

View File

@ -4,36 +4,47 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.telephony.SmsMessage;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobs.requirements.SqlCipherMigrationRequirement;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import androidx.work.Data;
public class SmsReceiveJob extends ContextJob {
private static final long serialVersionUID = 1L;
private static final String TAG = SmsReceiveJob.class.getSimpleName();
private final @Nullable Object[] pdus;
private final int subscriptionId;
private static final String KEY_PDUS = "pdus";
private static final String KEY_SUBSCRIPTION_ID = "subscription_id";
private @Nullable Object[] pdus;
private int subscriptionId;
public SmsReceiveJob() {
super(null, null);
}
public SmsReceiveJob(@NonNull Context context, @Nullable Object[] pdus, int subscriptionId) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withWakeLock(true)
.withRequirement(new SqlCipherMigrationRequirement(context))
.withSqlCipherRequirement()
.create());
this.pdus = pdus;
@ -41,7 +52,31 @@ public class SmsReceiveJob extends ContextJob {
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
String[] encoded = data.getStringArray(KEY_PDUS);
pdus = new Object[encoded.length];
try {
for (int i = 0; i < encoded.length; i++) {
pdus[i] = Base64.decode(encoded[i]);
}
} catch (IOException e) {
throw new AssertionError(e);
}
subscriptionId = data.getInt(KEY_SUBSCRIPTION_ID);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
String[] encoded = new String[pdus.length];
for (int i = 0; i < pdus.length; i++) {
encoded[i] = Base64.encodeBytes((byte[]) pdus[i]);
}
return dataBuilder.putStringArray(KEY_PDUS, encoded)
.putInt(KEY_SUBSCRIPTION_ID, subscriptionId)
.build();
}
@Override
public void onRun() throws MigrationPendingException {

View File

@ -5,8 +5,11 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
@ -14,9 +17,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobs.requirements.NetworkOrServiceRequirement;
import org.thoughtcrime.securesms.jobs.requirements.ServiceRequirement;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
@ -27,18 +27,35 @@ import org.thoughtcrime.securesms.jobmanager.JobParameters;
import java.util.ArrayList;
import androidx.work.Data;
public class SmsSendJob extends SendJob {
private static final long serialVersionUID = -5118520036244759718L;
private static final String TAG = SmsSendJob.class.getSimpleName();
private static final String KEY_MESSAGE_ID = "message_id";
private final long messageId;
private long messageId;
public SmsSendJob() {
super(null, null);
}
public SmsSendJob(Context context, long messageId, String name) {
super(context, constructParameters(context, name));
super(context, constructParameters(name));
this.messageId = messageId;
}
@Override
protected void initialize(@NonNull SafeData data) {
messageId = data.getLong(KEY_MESSAGE_ID);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId).build();
}
@Override
public void onAdded() {
Log.i(TAG, "onAdded() messageId: " + messageId);
@ -190,19 +207,11 @@ public class SmsSendJob extends SendJob {
}
}
private static JobParameters constructParameters(Context context, String name) {
private static JobParameters constructParameters(String name) {
JobParameters.Builder builder = JobParameters.newBuilder()
.withPersistence()
.withRequirement(new MasterSecretRequirement(context))
.withMasterSecretRequirement()
.withRetryCount(15)
.withGroupId(name);
if (TextSecurePreferences.isWifiSmsEnabled(context)) {
builder.withRequirement(new NetworkOrServiceRequirement(context));
} else {
builder.withRequirement(new ServiceRequirement(context));
}
return builder.create();
}

View File

@ -2,7 +2,10 @@ package org.thoughtcrime.securesms.jobs;
import android.app.Activity;
import android.content.Context;
import android.support.annotation.NonNull;
import android.telephony.SmsManager;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
@ -12,23 +15,31 @@ import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
import androidx.work.Data;
public class SmsSentJob extends MasterSecretJob {
private static final long serialVersionUID = -2624694558755317560L;
private static final String TAG = SmsSentJob.class.getSimpleName();
private final long messageId;
private final String action;
private final int result;
private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_ACTION = "action";
private static final String KEY_RESULT = "result";
private long messageId;
private String action;
private int result;
public SmsSentJob() {
super(null, null);
}
public SmsSentJob(Context context, long messageId, String action, int result) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withRequirement(new MasterSecretRequirement(context))
.withMasterSecretRequirement()
.create());
this.messageId = messageId;
@ -37,8 +48,18 @@ public class SmsSentJob extends MasterSecretJob {
}
@Override
public void onAdded() {
protected void initialize(@NonNull SafeData data) {
messageId = data.getLong(KEY_MESSAGE_ID);
action = data.getString(KEY_ACTION);
result = data.getInt(KEY_RESULT);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId)
.putString(KEY_ACTION, action)
.putInt(KEY_RESULT, result)
.build();
}
@Override

View File

@ -17,29 +17,42 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class TrimThreadJob extends Job {
import androidx.work.Data;
public class TrimThreadJob extends ContextJob {
private static final String TAG = TrimThreadJob.class.getSimpleName();
private final Context context;
private final long threadId;
private static final String KEY_THREAD_ID = "thread_id";
private long threadId;
public TrimThreadJob() {
super(null, null);
}
public TrimThreadJob(Context context, long threadId) {
super(JobParameters.newBuilder().withGroupId(TrimThreadJob.class.getSimpleName()).create());
super(context, JobParameters.newBuilder().withGroupId(TrimThreadJob.class.getSimpleName()).create());
this.context = context;
this.threadId = threadId;
}
@Override
public void onAdded() {
protected void initialize(@NonNull SafeData data) {
threadId = data.getLong(KEY_THREAD_ID);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putLong(KEY_THREAD_ID, threadId).build();
}
@Override

View File

@ -9,14 +9,16 @@ import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.service.UpdateApkReadyListener;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.Hex;
@ -26,8 +28,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.concurrent.TimeUnit;
import androidx.work.Data;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
@ -36,17 +38,26 @@ public class UpdateApkJob extends ContextJob {
private static final String TAG = UpdateApkJob.class.getSimpleName();
public UpdateApkJob() {
super(null, null);
}
public UpdateApkJob(Context context) {
super(context, JobParameters.newBuilder()
.withGroupId(UpdateApkJob.class.getSimpleName())
.withRequirement(new NetworkRequirement(context))
.withWakeLock(true, 30, TimeUnit.SECONDS)
.withNetworkRequirement()
.withRetryCount(2)
.create());
}
@Override
public void onAdded() {}
protected void initialize(@NonNull SafeData data) {
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun() throws IOException, PackageManager.NameNotFoundException {