From 9a6f65988f8a96b180308bbe8860169cfb527561 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 11 Nov 2014 19:57:53 -0800 Subject: [PATCH] Add support for dependency injection, and accompanying tests. --- .../securesms/jobs/CleanPreKeysJobTest.java | 153 ++++++++++++++++++ .../jobs/DeliveryReceiptJobTest.java | 101 ++++++++++++ .../securesms/service/PreKeyServiceTest.java | 92 ----------- build.gradle | 2 + .../whispersystems/jobqueue/JobManager.java | 10 +- .../dependencies/DependencyInjector.java | 5 + .../persistence/PersistentStorage.java | 25 +-- .../api/TextSecureMessageReceiver.java | 13 +- .../push/exceptions/PushNetworkException.java | 4 + .../securesms/ApplicationContext.java | 25 ++- .../dependencies/AxolotlStorageModule.java | 34 ++++ .../dependencies/InjectableType.java | 4 + .../TextSecureCommunicationModule.java | 77 +++++++++ .../securesms/jobs/AttachmentDownloadJob.java | 40 +++-- .../securesms/jobs/AvatarDownloadJob.java | 5 +- .../securesms/jobs/CleanPreKeysJob.java | 73 ++++----- .../securesms/jobs/CreateSignedPreKeyJob.java | 21 +-- .../securesms/jobs/DeliveryReceiptJob.java | 19 ++- .../securesms/jobs/MasterSecretJob.java | 17 +- .../securesms/jobs/MmsDownloadJob.java | 5 +- .../securesms/jobs/MmsSendJob.java | 10 +- .../securesms/jobs/PushDecryptJob.java | 16 +- .../securesms/jobs/PushGroupSendJob.java | 32 ++-- .../securesms/jobs/PushMediaSendJob.java | 35 ++-- .../securesms/jobs/PushTextSendJob.java | 55 ++++--- .../securesms/jobs/RefreshPreKeysJob.java | 22 ++- .../securesms/jobs/SmsDecryptJob.java | 16 +- .../securesms/jobs/SmsSendJob.java | 18 +-- .../securesms/jobs/SmsSentJob.java | 6 +- .../securesms/push/SecurityEventListener.java | 32 ++++ .../push/TextSecureCommunicationFactory.java | 32 ---- 31 files changed, 663 insertions(+), 336 deletions(-) create mode 100644 androidTest/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJobTest.java create mode 100644 androidTest/java/org/thoughtcrime/securesms/jobs/DeliveryReceiptJobTest.java delete mode 100644 androidTest/java/org/thoughtcrime/securesms/service/PreKeyServiceTest.java create mode 100644 jobqueue/src/main/java/org/whispersystems/jobqueue/dependencies/DependencyInjector.java create mode 100644 src/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java create mode 100644 src/org/thoughtcrime/securesms/dependencies/InjectableType.java create mode 100644 src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java create mode 100644 src/org/thoughtcrime/securesms/push/SecurityEventListener.java diff --git a/androidTest/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJobTest.java b/androidTest/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJobTest.java new file mode 100644 index 0000000000..7814379a1d --- /dev/null +++ b/androidTest/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJobTest.java @@ -0,0 +1,153 @@ +package org.thoughtcrime.securesms.jobs; + +import android.test.AndroidTestCase; + +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyStore; +import org.whispersystems.textsecure.api.TextSecureAccountManager; +import org.whispersystems.textsecure.push.SignedPreKeyEntity; +import org.whispersystems.textsecure.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import dagger.Module; +import dagger.ObjectGraph; +import dagger.Provides; + +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class CleanPreKeysJobTest extends AndroidTestCase { + + public void testSignedPreKeyRotationNotRegistered() throws IOException, MasterSecretJob.RequirementNotMetException { + TextSecureAccountManager accountManager = mock(TextSecureAccountManager.class); + SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class); + MasterSecret masterSecret = mock(MasterSecret.class); + when(accountManager.getSignedPreKey()).thenReturn(null); + + CleanPreKeysJob cleanPreKeysJob = new CleanPreKeysJob(getContext()); + + ObjectGraph objectGraph = ObjectGraph.create(new TestModule(accountManager, signedPreKeyStore)); + objectGraph.inject(cleanPreKeysJob); + + cleanPreKeysJob.onRun(masterSecret); + + verify(accountManager).getSignedPreKey(); + verifyNoMoreInteractions(signedPreKeyStore); + } + + public void testSignedPreKeyEviction() throws Exception { + SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class); + TextSecureAccountManager accountManager = mock(TextSecureAccountManager.class); + SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class); + MasterSecret masterSecret = mock(MasterSecret.class); + + when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133); + when(accountManager.getSignedPreKey()).thenReturn(currentSignedPreKeyEntity); + + final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(), new byte[64]); + + List records = new LinkedList() {{ + add(new SignedPreKeyRecord(2, 11, Curve.generateKeyPair(), new byte[32])); + add(new SignedPreKeyRecord(4, System.currentTimeMillis() - 100, Curve.generateKeyPair(), new byte[64])); + add(currentRecord); + add(new SignedPreKeyRecord(3, System.currentTimeMillis() - 90, Curve.generateKeyPair(), new byte[64])); + add(new SignedPreKeyRecord(1, 10, Curve.generateKeyPair(), new byte[32])); + }}; + + when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records); + when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord); + + CleanPreKeysJob cleanPreKeysJob = new CleanPreKeysJob(getContext()); + + ObjectGraph objectGraph = ObjectGraph.create(new TestModule(accountManager, signedPreKeyStore)); + objectGraph.inject(cleanPreKeysJob); + + cleanPreKeysJob.onRun(masterSecret); + + verify(signedPreKeyStore).removeSignedPreKey(eq(1)); + verify(signedPreKeyStore, times(1)).removeSignedPreKey(anyInt()); + } + + public void testSignedPreKeyNoEviction() throws Exception { + SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class); + TextSecureAccountManager accountManager = mock(TextSecureAccountManager.class); + SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class); + + when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133); + when(accountManager.getSignedPreKey()).thenReturn(currentSignedPreKeyEntity); + + final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(), new byte[64]); + + List records = new LinkedList() {{ + add(currentRecord); + }}; + + when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records); + when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord); + + CleanPreKeysJob cleanPreKeysJob = new CleanPreKeysJob(getContext()); + + ObjectGraph objectGraph = ObjectGraph.create(new TestModule(accountManager, signedPreKeyStore)); + objectGraph.inject(cleanPreKeysJob); + + verify(signedPreKeyStore, never()).removeSignedPreKey(anyInt()); + } + + public void testConnectionError() throws Exception { + SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class); + TextSecureAccountManager accountManager = mock(TextSecureAccountManager.class); + MasterSecret masterSecret = mock(MasterSecret.class); + + when(accountManager.getSignedPreKey()).thenThrow(new PushNetworkException("Connectivity error!")); + + CleanPreKeysJob cleanPreKeysJob = new CleanPreKeysJob(getContext()); + + ObjectGraph objectGraph = ObjectGraph.create(new TestModule(accountManager, signedPreKeyStore)); + objectGraph.inject(cleanPreKeysJob); + + try { + cleanPreKeysJob.onRun(masterSecret); + throw new AssertionError("should have failed!"); + } catch (IOException e) { + assertTrue(cleanPreKeysJob.onShouldRetry(e)); + } + } + + @Module(injects = {CleanPreKeysJob.class}) + public static class TestModule { + private final TextSecureAccountManager accountManager; + private final SignedPreKeyStore signedPreKeyStore; + + private TestModule(TextSecureAccountManager accountManager, SignedPreKeyStore signedPreKeyStore) { + this.accountManager = accountManager; + this.signedPreKeyStore = signedPreKeyStore; + } + + @Provides TextSecureAccountManager provideTextSecureAccountManager() { + return accountManager; + } + + @Provides + AxolotlStorageModule.SignedPreKeyStoreFactory provideSignedPreKeyStore() { + return new AxolotlStorageModule.SignedPreKeyStoreFactory() { + @Override + public SignedPreKeyStore create(MasterSecret masterSecret) { + return signedPreKeyStore; + } + }; + } + } + +} diff --git a/androidTest/java/org/thoughtcrime/securesms/jobs/DeliveryReceiptJobTest.java b/androidTest/java/org/thoughtcrime/securesms/jobs/DeliveryReceiptJobTest.java new file mode 100644 index 0000000000..60c2b0be43 --- /dev/null +++ b/androidTest/java/org/thoughtcrime/securesms/jobs/DeliveryReceiptJobTest.java @@ -0,0 +1,101 @@ +package org.thoughtcrime.securesms.jobs; + +import android.test.AndroidTestCase; + +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.whispersystems.textsecure.api.TextSecureMessageSender; +import org.whispersystems.textsecure.push.PushAddress; +import org.whispersystems.textsecure.push.exceptions.NotFoundException; +import org.whispersystems.textsecure.push.exceptions.PushNetworkException; + +import java.io.IOException; + +import dagger.Module; +import dagger.ObjectGraph; +import dagger.Provides; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule.TextSecureMessageSenderFactory; + +public class DeliveryReceiptJobTest extends AndroidTestCase { + + public void testDelivery() throws IOException { + TextSecureMessageSender textSecureMessageSender = mock(TextSecureMessageSender.class); + long timestamp = System.currentTimeMillis(); + + DeliveryReceiptJob deliveryReceiptJob = new DeliveryReceiptJob(getContext(), + "+14152222222", + timestamp, "foo"); + + ObjectGraph objectGraph = ObjectGraph.create(new TestModule(textSecureMessageSender)); + objectGraph.inject(deliveryReceiptJob); + + deliveryReceiptJob.onRun(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PushAddress.class); + verify(textSecureMessageSender).sendDeliveryReceipt(captor.capture(), eq(timestamp)); + + assertTrue(captor.getValue().getRelay().equals("foo")); + assertTrue(captor.getValue().getNumber().equals("+14152222222")); + } + + public void testNetworkError() throws IOException { + TextSecureMessageSender textSecureMessageSender = mock(TextSecureMessageSender.class); + long timestamp = System.currentTimeMillis(); + + Mockito.doThrow(new PushNetworkException("network error")) + .when(textSecureMessageSender) + .sendDeliveryReceipt(any(PushAddress.class), eq(timestamp)); + + + DeliveryReceiptJob deliveryReceiptJob = new DeliveryReceiptJob(getContext(), + "+14152222222", + timestamp, "foo"); + + ObjectGraph objectGraph = ObjectGraph.create(new TestModule(textSecureMessageSender)); + objectGraph.inject(deliveryReceiptJob); + + try { + deliveryReceiptJob.onRun(); + throw new AssertionError(); + } catch (IOException e) { + assertTrue(deliveryReceiptJob.onShouldRetry(e)); + } + + Mockito.doThrow(new NotFoundException("not found")) + .when(textSecureMessageSender) + .sendDeliveryReceipt(any(PushAddress.class), eq(timestamp)); + + try { + deliveryReceiptJob.onRun(); + throw new AssertionError(); + } catch (IOException e) { + assertFalse(deliveryReceiptJob.onShouldRetry(e)); + } + } + + @Module(injects = DeliveryReceiptJob.class) + public static class TestModule { + + private final TextSecureMessageSender textSecureMessageSender; + + public TestModule(TextSecureMessageSender textSecureMessageSender) { + this.textSecureMessageSender = textSecureMessageSender; + } + + @Provides TextSecureMessageSenderFactory provideTextSecureMessageSenderFactory() { + return new TextSecureMessageSenderFactory() { + @Override + public TextSecureMessageSender create(MasterSecret masterSecret) { + return textSecureMessageSender; + } + }; + } + } + +} diff --git a/androidTest/java/org/thoughtcrime/securesms/service/PreKeyServiceTest.java b/androidTest/java/org/thoughtcrime/securesms/service/PreKeyServiceTest.java deleted file mode 100644 index 318c71db06..0000000000 --- a/androidTest/java/org/thoughtcrime/securesms/service/PreKeyServiceTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.thoughtcrime.securesms.service; - -import android.test.AndroidTestCase; - -import org.whispersystems.libaxolotl.ecc.Curve; -import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; -import org.whispersystems.libaxolotl.state.SignedPreKeyStore; -import org.whispersystems.textsecure.push.PushServiceSocket; -import org.whispersystems.textsecure.push.SignedPreKeyEntity; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -public class PreKeyServiceTest extends AndroidTestCase { - - public void testSignedPreKeyRotationNotRegistered() throws IOException { - SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class); - PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class); - - when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(null); - - PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore, - pushServiceSocket); - - cleanTask.run(); - - verify(pushServiceSocket).getCurrentSignedPreKey(); - verifyNoMoreInteractions(signedPreKeyStore); - } - - public void testSignedPreKeyEviction() throws Exception { - SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class); - PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class); - SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class); - - when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133); - when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(currentSignedPreKeyEntity); - - final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(true), new byte[64]); - - List records = new LinkedList() {{ - add(new SignedPreKeyRecord(1, 10, Curve.generateKeyPair(true), new byte[32])); - add(new SignedPreKeyRecord(2, 11, Curve.generateKeyPair(true), new byte[32])); - add(new SignedPreKeyRecord(3, System.currentTimeMillis() - 90, Curve.generateKeyPair(true), new byte[64])); - add(new SignedPreKeyRecord(4, System.currentTimeMillis() - 100, Curve.generateKeyPair(true), new byte[64])); - add(currentRecord); - }}; - - when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records); - when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord); - - PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore, pushServiceSocket); - cleanTask.run(); - - verify(signedPreKeyStore).removeSignedPreKey(eq(1)); - verify(signedPreKeyStore).removeSignedPreKey(eq(2)); - verify(signedPreKeyStore, times(2)).removeSignedPreKey(anyInt()); - } - - public void testSignedPreKeyNoEviction() throws Exception { - SignedPreKeyStore signedPreKeyStore = mock(SignedPreKeyStore.class); - PushServiceSocket pushServiceSocket = mock(PushServiceSocket.class); - SignedPreKeyEntity currentSignedPreKeyEntity = mock(SignedPreKeyEntity.class); - - when(currentSignedPreKeyEntity.getKeyId()).thenReturn(3133); - when(pushServiceSocket.getCurrentSignedPreKey()).thenReturn(currentSignedPreKeyEntity); - - final SignedPreKeyRecord currentRecord = new SignedPreKeyRecord(3133, System.currentTimeMillis(), Curve.generateKeyPair(true), new byte[64]); - - List records = new LinkedList() {{ - add(currentRecord); - }}; - - when(signedPreKeyStore.loadSignedPreKeys()).thenReturn(records); - when(signedPreKeyStore.loadSignedPreKey(eq(3133))).thenReturn(currentRecord); - - PreKeyService.CleanSignedPreKeysTask cleanTask = new PreKeyService.CleanSignedPreKeysTask(signedPreKeyStore, pushServiceSocket); - cleanTask.run(); - - verify(signedPreKeyStore, never()).removeSignedPreKey(anyInt()); - } -} diff --git a/build.gradle b/build.gradle index 7efeba4130..2462a365f0 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,8 @@ dependencies { compile ('com.android.support:support-v4-preferencefragment:1.0.0@aar'){ exclude module: 'support-v4' } + compile 'com.squareup.dagger:dagger:1.2.2' + provided 'com.squareup.dagger:dagger-compiler:1.2.2' androidTestCompile 'com.squareup:fest-android:1.0.8' androidTestCompile 'com.google.dexmaker:dexmaker:1.1' diff --git a/jobqueue/src/main/java/org/whispersystems/jobqueue/JobManager.java b/jobqueue/src/main/java/org/whispersystems/jobqueue/JobManager.java index 69d6536235..63e4071df1 100644 --- a/jobqueue/src/main/java/org/whispersystems/jobqueue/JobManager.java +++ b/jobqueue/src/main/java/org/whispersystems/jobqueue/JobManager.java @@ -19,6 +19,7 @@ package org.whispersystems.jobqueue; import android.content.Context; import android.util.Log; +import org.whispersystems.jobqueue.dependencies.DependencyInjector; import org.whispersystems.jobqueue.persistence.JobSerializer; import org.whispersystems.jobqueue.persistence.PersistentStorage; import org.whispersystems.jobqueue.requirements.RequirementListener; @@ -38,13 +39,16 @@ public class JobManager implements RequirementListener { private final PersistentStorage persistentStorage; private final List requirementProviders; + private final DependencyInjector dependencyInjector; public JobManager(Context context, String name, List requirementProviders, + DependencyInjector dependencyInjector, JobSerializer jobSerializer, int consumers) { - this.persistentStorage = new PersistentStorage(context, name, jobSerializer); + this.persistentStorage = new PersistentStorage(context, name, jobSerializer, dependencyInjector); this.requirementProviders = requirementProviders; + this.dependencyInjector = dependencyInjector; eventExecutor.execute(new LoadTask(null)); @@ -84,6 +88,10 @@ public class JobManager implements RequirementListener { persistentStorage.store(job); } + if (dependencyInjector != null) { + dependencyInjector.injectDependencies(job); + } + job.onAdded(); jobQueue.add(job); } catch (IOException e) { diff --git a/jobqueue/src/main/java/org/whispersystems/jobqueue/dependencies/DependencyInjector.java b/jobqueue/src/main/java/org/whispersystems/jobqueue/dependencies/DependencyInjector.java new file mode 100644 index 0000000000..e0234ef47b --- /dev/null +++ b/jobqueue/src/main/java/org/whispersystems/jobqueue/dependencies/DependencyInjector.java @@ -0,0 +1,5 @@ +package org.whispersystems.jobqueue.dependencies; + +public interface DependencyInjector { + public void injectDependencies(Object object); +} diff --git a/jobqueue/src/main/java/org/whispersystems/jobqueue/persistence/PersistentStorage.java b/jobqueue/src/main/java/org/whispersystems/jobqueue/persistence/PersistentStorage.java index 30ed1cb845..0047fda05e 100644 --- a/jobqueue/src/main/java/org/whispersystems/jobqueue/persistence/PersistentStorage.java +++ b/jobqueue/src/main/java/org/whispersystems/jobqueue/persistence/PersistentStorage.java @@ -25,6 +25,7 @@ import android.util.Log; import org.whispersystems.jobqueue.EncryptionKeys; import org.whispersystems.jobqueue.Job; +import org.whispersystems.jobqueue.dependencies.DependencyInjector; import java.io.IOException; import java.util.LinkedList; @@ -42,12 +43,17 @@ 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 DatabaseHelper databaseHelper; - private final JobSerializer jobSerializer; + private final DatabaseHelper databaseHelper; + private final JobSerializer jobSerializer; + private final DependencyInjector dependencyInjector; - public PersistentStorage(Context context, String name, JobSerializer serializer) { - this.databaseHelper = new DatabaseHelper(context, "_jobqueue-" + name); - this.jobSerializer = serializer; + public PersistentStorage(Context context, String name, + JobSerializer serializer, + DependencyInjector dependencyInjector) + { + this.databaseHelper = new DatabaseHelper(context, "_jobqueue-" + name); + this.jobSerializer = serializer; + this.dependencyInjector = dependencyInjector; } public void store(Job job) throws IOException { @@ -59,10 +65,6 @@ public class PersistentStorage { job.setPersistentId(id); } -// public List getAll(EncryptionKeys keys) { -// return getJobs(keys, null); -// } - public List getAllUnencrypted() { return getJobs(null, ENCRYPTED + " = 0"); } @@ -88,6 +90,11 @@ public class PersistentStorage { Job job = jobSerializer.deserialize(keys, encrypted, item); job.setPersistentId(id); + + if (dependencyInjector != null) { + dependencyInjector.injectDependencies(job); + } + results.add(job); } catch (IOException e) { Log.w("PersistentStore", e); diff --git a/library/src/org/whispersystems/textsecure/api/TextSecureMessageReceiver.java b/library/src/org/whispersystems/textsecure/api/TextSecureMessageReceiver.java index efc79d19eb..1138b39b5c 100644 --- a/library/src/org/whispersystems/textsecure/api/TextSecureMessageReceiver.java +++ b/library/src/org/whispersystems/textsecure/api/TextSecureMessageReceiver.java @@ -1,7 +1,6 @@ package org.whispersystems.textsecure.api; import org.whispersystems.libaxolotl.InvalidMessageException; -import org.whispersystems.libaxolotl.state.AxolotlStore; import org.whispersystems.textsecure.api.crypto.AttachmentCipherInputStream; import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer; import org.whispersystems.textsecure.push.PushServiceSocket; @@ -12,18 +11,12 @@ import java.io.InputStream; public class TextSecureMessageReceiver { - private final String signalingKey; - private final AxolotlStore axolotlStore; private final PushServiceSocket socket; - public TextSecureMessageReceiver(String signalingKey, String url, - PushServiceSocket.TrustStore trustStore, - String user, String password, - AxolotlStore axolotlStore) + public TextSecureMessageReceiver(String url, PushServiceSocket.TrustStore trustStore, + String user, String password) { - this.axolotlStore = axolotlStore; - this.signalingKey = signalingKey; - this.socket = new PushServiceSocket(url, trustStore, user, password); + this.socket = new PushServiceSocket(url, trustStore, user, password); } public InputStream retrieveAttachment(TextSecureAttachmentPointer pointer, File destination) diff --git a/library/src/org/whispersystems/textsecure/push/exceptions/PushNetworkException.java b/library/src/org/whispersystems/textsecure/push/exceptions/PushNetworkException.java index c3ab4417cf..fd51be93ee 100644 --- a/library/src/org/whispersystems/textsecure/push/exceptions/PushNetworkException.java +++ b/library/src/org/whispersystems/textsecure/push/exceptions/PushNetworkException.java @@ -6,4 +6,8 @@ public class PushNetworkException extends IOException { public PushNetworkException(Exception exception) { super(exception); } + + public PushNetworkException(String s) { + super(s); + } } diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index c077a0c956..5cc6af2f0b 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -20,17 +20,23 @@ import android.app.Application; import android.content.Context; import org.thoughtcrime.securesms.crypto.PRNGFixes; +import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule; +import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.jobs.persistence.EncryptingJobSerializer; import org.thoughtcrime.securesms.jobs.GcmRefreshJob; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirementProvider; +import org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.jobqueue.JobManager; +import org.whispersystems.jobqueue.dependencies.DependencyInjector; import org.whispersystems.jobqueue.requirements.NetworkRequirementProvider; import org.whispersystems.jobqueue.requirements.RequirementProvider; import java.util.LinkedList; import java.util.List; +import dagger.ObjectGraph; + /** * Will be called once when the TextSecure process is created. * @@ -39,9 +45,10 @@ import java.util.List; * * @author Moxie Marlinspike */ -public class ApplicationContext extends Application { +public class ApplicationContext extends Application implements DependencyInjector { private JobManager jobManager; + private ObjectGraph objectGraph; public static ApplicationContext getInstance(Context context) { return (ApplicationContext)context.getApplicationContext(); @@ -50,14 +57,23 @@ public class ApplicationContext extends Application { @Override public void onCreate() { initializeRandomNumberFix(); + initializeDependencyInjection(); initializeJobManager(); initializeGcmCheck(); } + @Override + public void injectDependencies(Object object) { + if (object instanceof InjectableType) { + objectGraph.inject(object); + } + } + public JobManager getJobManager() { return jobManager; } + private void initializeRandomNumberFix() { PRNGFixes.apply(); } @@ -68,10 +84,15 @@ public class ApplicationContext extends Application { add(new MasterSecretRequirementProvider(ApplicationContext.this)); }}; - this.jobManager = new JobManager(this, "TextSecureJobs", providers, + this.jobManager = new JobManager(this, "TextSecureJobs", providers, this, new EncryptingJobSerializer(this), 5); } + private void initializeDependencyInjection() { + this.objectGraph = ObjectGraph.create(new TextSecureCommunicationModule(this), + new AxolotlStorageModule(this)); + } + private void initializeGcmCheck() { if (TextSecurePreferences.isPushRegistered(this) && TextSecurePreferences.getGcmRegistrationId(this) == null) diff --git a/src/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java b/src/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java new file mode 100644 index 0000000000..63bc496f3b --- /dev/null +++ b/src/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java @@ -0,0 +1,34 @@ +package org.thoughtcrime.securesms.dependencies; + +import android.content.Context; + +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore; +import org.thoughtcrime.securesms.jobs.CleanPreKeysJob; +import org.whispersystems.libaxolotl.state.SignedPreKeyStore; + +import dagger.Module; +import dagger.Provides; + +@Module (complete = false, injects = {CleanPreKeysJob.class}) +public class AxolotlStorageModule { + + private final Context context; + + public AxolotlStorageModule(Context context) { + this.context = context; + } + + @Provides SignedPreKeyStoreFactory provideSignedPreKeyStoreFactory() { + return new SignedPreKeyStoreFactory() { + @Override + public SignedPreKeyStore create(MasterSecret masterSecret) { + return new TextSecureAxolotlStore(context, masterSecret); + } + }; + } + + public static interface SignedPreKeyStoreFactory { + public SignedPreKeyStore create(MasterSecret masterSecret); + } +} diff --git a/src/org/thoughtcrime/securesms/dependencies/InjectableType.java b/src/org/thoughtcrime/securesms/dependencies/InjectableType.java new file mode 100644 index 0000000000..033b3ef45a --- /dev/null +++ b/src/org/thoughtcrime/securesms/dependencies/InjectableType.java @@ -0,0 +1,4 @@ +package org.thoughtcrime.securesms.dependencies; + +public interface InjectableType { +} diff --git a/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java new file mode 100644 index 0000000000..8a095e7dab --- /dev/null +++ b/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java @@ -0,0 +1,77 @@ +package org.thoughtcrime.securesms.dependencies; + +import android.content.Context; + +import org.thoughtcrime.securesms.Release; +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore; +import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; +import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; +import org.thoughtcrime.securesms.jobs.CleanPreKeysJob; +import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; +import org.thoughtcrime.securesms.jobs.DeliveryReceiptJob; +import org.thoughtcrime.securesms.jobs.PushGroupSendJob; +import org.thoughtcrime.securesms.jobs.PushMediaSendJob; +import org.thoughtcrime.securesms.jobs.PushTextSendJob; +import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; +import org.thoughtcrime.securesms.push.SecurityEventListener; +import org.thoughtcrime.securesms.push.TextSecurePushTrustStore; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.whispersystems.libaxolotl.util.guava.Optional; +import org.whispersystems.textsecure.api.TextSecureAccountManager; +import org.whispersystems.textsecure.api.TextSecureMessageReceiver; +import org.whispersystems.textsecure.api.TextSecureMessageSender; + +import dagger.Module; +import dagger.Provides; + +@Module(complete = false, injects = {CleanPreKeysJob.class, + CreateSignedPreKeyJob.class, + DeliveryReceiptJob.class, + PushGroupSendJob.class, + PushTextSendJob.class, + PushMediaSendJob.class, + AttachmentDownloadJob.class, + RefreshPreKeysJob.class}) +public class TextSecureCommunicationModule { + + private final Context context; + + public TextSecureCommunicationModule(Context context) { + this.context = context; + } + + @Provides TextSecureAccountManager provideTextSecureAccountManager() { + return new TextSecureAccountManager(Release.PUSH_URL, + new TextSecurePushTrustStore(context), + TextSecurePreferences.getLocalNumber(context), + TextSecurePreferences.getPushServerPassword(context)); + } + + @Provides TextSecureMessageSenderFactory provideTextSecureMessageSenderFactory() { + return new TextSecureMessageSenderFactory() { + @Override + public TextSecureMessageSender create(MasterSecret masterSecret) { + return new TextSecureMessageSender(Release.PUSH_URL, + new TextSecurePushTrustStore(context), + TextSecurePreferences.getLocalNumber(context), + TextSecurePreferences.getPushServerPassword(context), + new TextSecureAxolotlStore(context, masterSecret), + Optional.of((TextSecureMessageSender.EventListener) + new SecurityEventListener(context))); + } + }; + } + + @Provides TextSecureMessageReceiver provideTextSecureMessageReceiver() { + return new TextSecureMessageReceiver(Release.PUSH_URL, + new TextSecurePushTrustStore(context), + TextSecurePreferences.getLocalNumber(context), + TextSecurePreferences.getPushServerPassword(context)); + } + + public static interface TextSecureMessageSenderFactory { + public TextSecureMessageSender create(MasterSecret masterSecret); + } + +} diff --git a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java index a04468cb3d..1b578982c9 100644 --- a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java @@ -9,8 +9,8 @@ import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingPartDatabase; import org.thoughtcrime.securesms.database.PartDatabase; +import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; -import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; @@ -26,13 +26,17 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; +import javax.inject.Inject; + import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.PduPart; -public class AttachmentDownloadJob extends MasterSecretJob { +public class AttachmentDownloadJob extends MasterSecretJob implements InjectableType { private static final String TAG = AttachmentDownloadJob.class.getSimpleName(); + @Inject transient TextSecureMessageReceiver messageReceiver; + private final long messageId; public AttachmentDownloadJob(Context context, long messageId) { @@ -49,9 +53,8 @@ public class AttachmentDownloadJob extends MasterSecretJob { public void onAdded() {} @Override - public void onRun() throws RequirementNotMetException, IOException { - MasterSecret masterSecret = getMasterSecret(); - PartDatabase database = DatabaseFactory.getEncryptingPartDatabase(context, masterSecret); + public void onRun(MasterSecret masterSecret) throws IOException { + PartDatabase database = DatabaseFactory.getEncryptingPartDatabase(context, masterSecret); Log.w(TAG, "Downloading push parts for: " + messageId); @@ -65,23 +68,17 @@ public class AttachmentDownloadJob extends MasterSecretJob { @Override public void onCanceled() { - try { - MasterSecret masterSecret = getMasterSecret(); - PartDatabase database = DatabaseFactory.getEncryptingPartDatabase(context, masterSecret); - List> parts = database.getParts(messageId, false); + PartDatabase database = DatabaseFactory.getPartDatabase(context); + List> parts = database.getParts(messageId, false); - for (Pair partPair : parts) { - markFailed(masterSecret, messageId, partPair.second, partPair.first); - } - } catch (RequirementNotMetException e) { - Log.w(TAG, e); + for (Pair partPair : parts) { + markFailed(messageId, partPair.second, partPair.first); } } @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof PushNetworkException) return true; - if (throwable instanceof RequirementNotMetException) return true; + public boolean onShouldRetryThrowable(Throwable throwable) { + if (throwable instanceof PushNetworkException) return true; return false; } @@ -89,7 +86,6 @@ public class AttachmentDownloadJob extends MasterSecretJob { private void retrievePart(MasterSecret masterSecret, PduPart part, long messageId, long partId) throws IOException { - TextSecureMessageReceiver receiver = TextSecureCommunicationFactory.createReceiver(context, masterSecret); EncryptingPartDatabase database = DatabaseFactory.getEncryptingPartDatabase(context, masterSecret); File attachmentFile = null; @@ -97,12 +93,12 @@ public class AttachmentDownloadJob extends MasterSecretJob { attachmentFile = createTempFile(); TextSecureAttachmentPointer pointer = createAttachmentPointer(masterSecret, part); - InputStream attachment = receiver.retrieveAttachment(pointer, attachmentFile); + InputStream attachment = messageReceiver.retrieveAttachment(pointer, attachmentFile); database.updateDownloadedPart(messageId, partId, part, attachment); } catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) { Log.w(TAG, e); - markFailed(masterSecret, messageId, part, partId); + markFailed(messageId, part, partId); } finally { if (attachmentFile != null) attachmentFile.delete(); @@ -140,9 +136,9 @@ public class AttachmentDownloadJob extends MasterSecretJob { } } - private void markFailed(MasterSecret masterSecret, long messageId, PduPart part, long partId) { + private void markFailed(long messageId, PduPart part, long partId) { try { - EncryptingPartDatabase database = DatabaseFactory.getEncryptingPartDatabase(context, masterSecret); + PartDatabase database = DatabaseFactory.getPartDatabase(context); database.updateFailedDownloadedPart(messageId, partId, part); } catch (MmsException e) { Log.w(TAG, e); diff --git a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java index 4fd13e8496..a638b63a84 100644 --- a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java @@ -5,6 +5,7 @@ import android.graphics.Bitmap; import android.util.Log; import org.thoughtcrime.securesms.Release; +import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; @@ -47,7 +48,7 @@ public class AvatarDownloadJob extends MasterSecretJob { public void onAdded() {} @Override - public void onRun() throws IOException { + public void onRun(MasterSecret masterSecret) throws IOException { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase.GroupRecord record = database.getGroup(groupId); File attachment = null; @@ -90,7 +91,7 @@ public class AvatarDownloadJob extends MasterSecretJob { public void onCanceled() {} @Override - public boolean onShouldRetry(Throwable throwable) { + public boolean onShouldRetryThrowable(Throwable throwable) { if (throwable instanceof IOException) return true; return false; } diff --git a/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java b/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java index 55b817aaf4..a475d3edda 100644 --- a/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java +++ b/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java @@ -4,10 +4,8 @@ import android.content.Context; import android.util.Log; import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore; +import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; -import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; -import org.thoughtcrime.securesms.util.VisibleForTesting; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.libaxolotl.InvalidKeyIdException; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; @@ -20,17 +18,23 @@ import org.whispersystems.textsecure.push.exceptions.PushNetworkException; import java.io.IOException; import java.util.Collections; import java.util.Comparator; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; -public class CleanPreKeysJob extends MasterSecretJob { +import javax.inject.Inject; + +import static org.thoughtcrime.securesms.dependencies.AxolotlStorageModule.SignedPreKeyStoreFactory; + +public class CleanPreKeysJob extends MasterSecretJob implements InjectableType { private static final String TAG = CleanPreKeysJob.class.getSimpleName(); private static final int ARCHIVE_AGE_DAYS = 15; + @Inject transient TextSecureAccountManager accountManager; + @Inject transient SignedPreKeyStoreFactory signedPreKeyStoreFactory; + public CleanPreKeysJob(Context context) { super(context, JobParameters.newBuilder() .withGroupId(CleanPreKeysJob.class.getSimpleName()) @@ -45,40 +49,33 @@ public class CleanPreKeysJob extends MasterSecretJob { } @Override - public void onRun() throws RequirementNotMetException, IOException { + public void onRun(MasterSecret masterSecret) throws IOException { try { - MasterSecret masterSecret = getMasterSecret(); - SignedPreKeyStore signedPreKeyStore = createSignedPreKeyStore(context, masterSecret); - TextSecureAccountManager accountManager = createAccountManager(context); - + SignedPreKeyStore signedPreKeyStore = signedPreKeyStoreFactory.create(masterSecret); SignedPreKeyEntity currentSignedPreKey = accountManager.getSignedPreKey(); if (currentSignedPreKey == null) return; - SignedPreKeyRecord currentRecord = signedPreKeyStore.loadSignedPreKey(currentSignedPreKey.getKeyId()); - List allRecords = signedPreKeyStore.loadSignedPreKeys(); - List oldRecords = removeRecordFrom(currentRecord, allRecords); + SignedPreKeyRecord currentRecord = signedPreKeyStore.loadSignedPreKey(currentSignedPreKey.getKeyId()); + List allRecords = signedPreKeyStore.loadSignedPreKeys(); + LinkedList oldRecords = removeRecordFrom(currentRecord, allRecords); Collections.sort(oldRecords, new SignedPreKeySorter()); Log.w(TAG, "Old signed prekey record count: " + oldRecords.size()); - if (oldRecords.size() < 2) { - return; - } + boolean foundAgedRecord = false; - SignedPreKeyRecord latestRecord = oldRecords.get(0); - long latestRecordArchiveDuration = System.currentTimeMillis() - latestRecord.getTimestamp(); + for (SignedPreKeyRecord oldRecord : oldRecords) { + long archiveDuration = System.currentTimeMillis() - oldRecord.getTimestamp(); - if (latestRecordArchiveDuration >= TimeUnit.DAYS.toMillis(ARCHIVE_AGE_DAYS)) { - Iterator iterator = oldRecords.iterator(); - iterator.next(); - - while (iterator.hasNext()) { - SignedPreKeyRecord expiredRecord = iterator.next(); - Log.w(TAG, "Removing signed prekey record: " + expiredRecord.getId() + " with timestamp: " + expiredRecord.getTimestamp()); - - signedPreKeyStore.removeSignedPreKey(expiredRecord.getId()); + if (archiveDuration >= TimeUnit.DAYS.toMillis(ARCHIVE_AGE_DAYS)) { + if (!foundAgedRecord) { + foundAgedRecord = true; + } else { + Log.w(TAG, "Removing signed prekey record: " + oldRecord.getId() + " with timestamp: " + oldRecord.getTimestamp()); + signedPreKeyStore.removeSignedPreKey(oldRecord.getId()); + } } } } catch (InvalidKeyIdException e) { @@ -87,8 +84,7 @@ public class CleanPreKeysJob extends MasterSecretJob { } @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RequirementNotMetException) return true; + public boolean onShouldRetryThrowable(Throwable throwable) { if (throwable instanceof NonSuccessfulResponseCodeException) return false; if (throwable instanceof PushNetworkException) return true; return false; @@ -99,11 +95,11 @@ public class CleanPreKeysJob extends MasterSecretJob { Log.w(TAG, "Failed to execute clean signed prekeys task."); } - private List removeRecordFrom(SignedPreKeyRecord currentRecord, - List records) + private LinkedList removeRecordFrom(SignedPreKeyRecord currentRecord, + List records) { - List others = new LinkedList<>(); + LinkedList others = new LinkedList<>(); for (SignedPreKeyRecord record : records) { if (record.getId() != currentRecord.getId()) { @@ -114,20 +110,11 @@ public class CleanPreKeysJob extends MasterSecretJob { return others; } - @VisibleForTesting - protected TextSecureAccountManager createAccountManager(Context context) { - return TextSecureCommunicationFactory.createManager(context); - } - - protected SignedPreKeyStore createSignedPreKeyStore(Context context, MasterSecret masterSecret) { - return new TextSecureAxolotlStore(context, masterSecret); - } - private static class SignedPreKeySorter implements Comparator { @Override public int compare(SignedPreKeyRecord lhs, SignedPreKeyRecord rhs) { - if (lhs.getTimestamp() < rhs.getTimestamp()) return -1; - else if (lhs.getTimestamp() > rhs.getTimestamp()) return 1; + if (lhs.getTimestamp() > rhs.getTimestamp()) return -1; + else if (lhs.getTimestamp() < rhs.getTimestamp()) return 1; else return 0; } } diff --git a/src/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java b/src/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java index d6aa1b3eea..4ce100e867 100644 --- a/src/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java +++ b/src/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java @@ -6,6 +6,7 @@ import android.util.Log; 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.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.util.ParcelUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -15,13 +16,18 @@ import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.libaxolotl.IdentityKeyPair; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; import org.whispersystems.textsecure.api.TextSecureAccountManager; +import org.whispersystems.textsecure.push.exceptions.PushNetworkException; import java.io.IOException; -public class CreateSignedPreKeyJob extends ContextJob { +import javax.inject.Inject; + +public class CreateSignedPreKeyJob extends ContextJob implements InjectableType { private static final String TAG = CreateSignedPreKeyJob.class.getSimpleName(); + @Inject transient TextSecureAccountManager accountManager; + public CreateSignedPreKeyJob(Context context, MasterSecret masterSecret) { super(context, JobParameters.newBuilder() .withPersistence() @@ -35,7 +41,7 @@ public class CreateSignedPreKeyJob extends ContextJob { public void onAdded() {} @Override - public void onRun() throws Throwable { + public void onRun() throws IOException { MasterSecret masterSecret = ParcelUtil.deserialize(getEncryptionKeys().getEncoded(), MasterSecret.CREATOR); if (TextSecurePreferences.isSignedPreKeyRegistered(context)) { @@ -43,9 +49,8 @@ public class CreateSignedPreKeyJob extends ContextJob { return; } - IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret); - SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, masterSecret, identityKeyPair); - TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(context); + IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret); + SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, masterSecret, identityKeyPair); accountManager.setSignedPreKey(signedPreKeyRecord); TextSecurePreferences.setSignedPreKeyRegistered(context, true); @@ -56,11 +61,7 @@ public class CreateSignedPreKeyJob extends ContextJob { @Override public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof IOException) { - return true; - } - - Log.w(TAG, throwable); + if (throwable instanceof PushNetworkException) return true; return false; } } diff --git a/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java b/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java index f883464fde..0db87f8fdb 100644 --- a/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java @@ -4,6 +4,8 @@ import android.content.Context; import android.util.Log; import org.thoughtcrime.securesms.Release; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule; import org.thoughtcrime.securesms.push.TextSecurePushTrustStore; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.jobqueue.JobParameters; @@ -16,10 +18,16 @@ import org.whispersystems.textsecure.push.exceptions.PushNetworkException; import java.io.IOException; -public class DeliveryReceiptJob extends ContextJob { +import javax.inject.Inject; + +import static org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule.TextSecureMessageSenderFactory; + +public class DeliveryReceiptJob extends ContextJob implements InjectableType { private static final String TAG = DeliveryReceiptJob.class.getSimpleName(); + @Inject transient TextSecureMessageSenderFactory messageSenderFactory; + private final String destination; private final long timestamp; private final String relay; @@ -42,14 +50,9 @@ public class DeliveryReceiptJob extends ContextJob { @Override public void onRun() throws IOException { Log.w("DeliveryReceiptJob", "Sending delivery receipt..."); - TextSecureMessageSender messageSender = - new TextSecureMessageSender(Release.PUSH_URL, - new TextSecurePushTrustStore(context), - TextSecurePreferences.getLocalNumber(context), - TextSecurePreferences.getPushServerPassword(context), - null, Optional.absent()); + TextSecureMessageSender messageSender = messageSenderFactory.create(null); + PushAddress pushAddress = new PushAddress(-1, destination, 1, relay); - PushAddress pushAddress = new PushAddress(-1, destination, 1, relay); messageSender.sendDeliveryReceipt(pushAddress, timestamp); } diff --git a/src/org/thoughtcrime/securesms/jobs/MasterSecretJob.java b/src/org/thoughtcrime/securesms/jobs/MasterSecretJob.java index 061c7695ab..c477d16b94 100644 --- a/src/org/thoughtcrime/securesms/jobs/MasterSecretJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MasterSecretJob.java @@ -12,7 +12,22 @@ public abstract class MasterSecretJob extends ContextJob { super(context, parameters); } - protected MasterSecret getMasterSecret() throws RequirementNotMetException { + @Override + public void onRun() throws Throwable { + MasterSecret masterSecret = getMasterSecret(); + onRun(masterSecret); + } + + @Override + public boolean onShouldRetry(Throwable throwable) { + if (throwable instanceof RequirementNotMetException) return true; + return onShouldRetryThrowable(throwable); + } + + public abstract void onRun(MasterSecret masterSecret) throws Throwable; + public abstract boolean onShouldRetryThrowable(Throwable throwable); + + private MasterSecret getMasterSecret() throws RequirementNotMetException { MasterSecret masterSecret = KeyCachingService.getMasterSecret(context); if (masterSecret == null) throw new RequirementNotMetException(); diff --git a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java index 8f4c5a5d93..b90f12833d 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java @@ -74,10 +74,9 @@ public class MmsDownloadJob extends MasterSecretJob { } @Override - public void onRun() throws RequirementNotMetException { + public void onRun(MasterSecret masterSecret) { Log.w(TAG, "MmsDownloadJob:onRun()"); - MasterSecret masterSecret = getMasterSecret(); MmsDatabase database = DatabaseFactory.getMmsDatabase(context); Optional notification = database.getNotification(messageId); @@ -180,7 +179,7 @@ public class MmsDownloadJob extends MasterSecretJob { } @Override - public boolean onShouldRetry(Throwable throwable) { + public boolean onShouldRetryThrowable(Throwable throwable) { return false; } diff --git a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java index 8db1de800f..6b7521982c 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java @@ -60,10 +60,9 @@ public class MmsSendJob extends MasterSecretJob { } @Override - public void onRun() throws RequirementNotMetException, MmsException, NoSuchMessageException { - MasterSecret masterSecret = getMasterSecret(); - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - SendReq message = database.getOutgoingMessage(masterSecret, messageId); + public void onRun(MasterSecret masterSecret) throws MmsException, NoSuchMessageException { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + SendReq message = database.getOutgoingMessage(masterSecret, messageId); try { MmsSendResult result = deliver(masterSecret, message); @@ -85,8 +84,7 @@ public class MmsSendJob extends MasterSecretJob { } @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RequirementNotMetException) return true; + public boolean onShouldRetryThrowable(Throwable throwable) { return false; } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 40b03f4207..77fe5b0d5e 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -69,24 +69,22 @@ public class PushDecryptJob extends MasterSecretJob { } @Override - public void onRun() throws RequirementNotMetException, NoSuchMessageException { - MasterSecret masterSecret = getMasterSecret(); - PushDatabase database = DatabaseFactory.getPushDatabase(context); - TextSecureEnvelope envelope = database.get(messageId); + public void onRun(MasterSecret masterSecret) throws NoSuchMessageException { + PushDatabase database = DatabaseFactory.getPushDatabase(context); + TextSecureEnvelope envelope = database.get(messageId); handleMessage(masterSecret, envelope); database.delete(messageId); } @Override - public void onCanceled() { - + public boolean onShouldRetryThrowable(Throwable throwable) { + return false; } @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RequirementNotMetException) return true; - return false; + public void onCanceled() { + } private void handleMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) { diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index b7e68a0faa..dcac734331 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -8,9 +8,9 @@ import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.NoSuchMessageException; +import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.PartParser; -import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.Recipients; @@ -19,10 +19,10 @@ import org.thoughtcrime.securesms.util.GroupUtil; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.textsecure.api.TextSecureMessageSender; +import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException; import org.whispersystems.textsecure.api.messages.TextSecureAttachment; import org.whispersystems.textsecure.api.messages.TextSecureGroup; import org.whispersystems.textsecure.api.messages.TextSecureMessage; -import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException; import org.whispersystems.textsecure.push.PushAddress; import org.whispersystems.textsecure.push.PushMessageProtos; import org.whispersystems.textsecure.push.exceptions.EncapsulatedExceptions; @@ -33,13 +33,19 @@ import java.io.IOException; import java.util.LinkedList; import java.util.List; +import javax.inject.Inject; + import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.SendReq; -public class PushGroupSendJob extends PushSendJob { +import static org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule.TextSecureMessageSenderFactory; + +public class PushGroupSendJob extends PushSendJob implements InjectableType { private static final String TAG = PushGroupSendJob.class.getSimpleName(); + @Inject transient TextSecureMessageSenderFactory messageSenderFactory; + private final long messageId; public PushGroupSendJob(Context context, long messageId, String destination) { @@ -60,10 +66,9 @@ public class PushGroupSendJob extends PushSendJob { } @Override - public void onRun() throws RequirementNotMetException, MmsException, IOException, NoSuchMessageException { - MasterSecret masterSecret = getMasterSecret(); - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - SendReq message = database.getOutgoingMessage(masterSecret, messageId); + public void onRun(MasterSecret masterSecret) throws MmsException, IOException, NoSuchMessageException { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + SendReq message = database.getOutgoingMessage(masterSecret, messageId); try { deliver(masterSecret, message); @@ -92,21 +97,20 @@ public class PushGroupSendJob extends PushSendJob { } @Override - public void onCanceled() { - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); + public boolean onShouldRetryThrowable(Throwable throwable) { + if (throwable instanceof IOException) return true; + return false; } @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RequirementNotMetException) return true; - if (throwable instanceof IOException) return true; - return false; + public void onCanceled() { + DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); } private void deliver(MasterSecret masterSecret, SendReq message) throws IOException, RecipientFormattingException, InvalidNumberException, EncapsulatedExceptions { - TextSecureMessageSender messageSender = TextSecureCommunicationFactory.createSender(context, masterSecret); + TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret); byte[] groupId = GroupUtil.getDecodedId(message.getTo()[0].getString()); Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false); List addresses = getPushAddresses(recipients); diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java index 279382bc44..5735078cef 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java @@ -9,8 +9,8 @@ import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; +import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.mms.PartParser; -import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFormattingException; @@ -21,9 +21,9 @@ import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.SecureFallbackApprovalException; import org.whispersystems.libaxolotl.state.AxolotlStore; import org.whispersystems.textsecure.api.TextSecureMessageSender; +import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException; import org.whispersystems.textsecure.api.messages.TextSecureAttachment; import org.whispersystems.textsecure.api.messages.TextSecureMessage; -import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException; import org.whispersystems.textsecure.push.PushAddress; import org.whispersystems.textsecure.push.UnregisteredUserException; import org.whispersystems.textsecure.storage.RecipientDevice; @@ -32,13 +32,19 @@ import org.whispersystems.textsecure.util.InvalidNumberException; import java.io.IOException; import java.util.List; +import javax.inject.Inject; + import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.SendReq; -public class PushMediaSendJob extends PushSendJob { +import static org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule.TextSecureMessageSenderFactory; + +public class PushMediaSendJob extends PushSendJob implements InjectableType { private static final String TAG = PushMediaSendJob.class.getSimpleName(); + @Inject transient TextSecureMessageSenderFactory messageSenderFactory; + private final long messageId; public PushMediaSendJob(Context context, long messageId, String destination) { @@ -52,12 +58,11 @@ public class PushMediaSendJob extends PushSendJob { } @Override - public void onRun() - throws RequirementNotMetException, RetryLaterException, MmsException, NoSuchMessageException + public void onRun(MasterSecret masterSecret) + throws RetryLaterException, MmsException, NoSuchMessageException { - MasterSecret masterSecret = getMasterSecret(); - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - SendReq message = database.getOutgoingMessage(masterSecret, messageId); + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + SendReq message = database.getOutgoingMessage(masterSecret, messageId); try { deliver(masterSecret, message); @@ -80,25 +85,25 @@ public class PushMediaSendJob extends PushSendJob { } } + @Override + public boolean onShouldRetryThrowable(Throwable throwable) { + if (throwable instanceof RequirementNotMetException) return true; + return false; + } + @Override public void onCanceled() { DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); notifyMediaMessageDeliveryFailed(context, messageId); } - @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RetryLaterException) return true; - if (throwable instanceof RequirementNotMetException) return true; - return false; - } private void deliver(MasterSecret masterSecret, SendReq message) throws RetryLaterException, SecureFallbackApprovalException, InsecureFallbackApprovalException, UntrustedIdentityException { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - TextSecureMessageSender messageSender = TextSecureCommunicationFactory.createSender(context, masterSecret); + TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret); String destination = message.getTo()[0].getString(); boolean isSmsFallbackSupported = isSmsFallbackSupported(context, destination); diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java index eb926d8426..76181df937 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -10,8 +10,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; +import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.IncomingIdentityUpdateMessage; @@ -20,8 +20,8 @@ import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.SecureFallbackApprovalException; import org.whispersystems.libaxolotl.state.AxolotlStore; import org.whispersystems.textsecure.api.TextSecureMessageSender; -import org.whispersystems.textsecure.api.messages.TextSecureMessage; import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException; +import org.whispersystems.textsecure.api.messages.TextSecureMessage; import org.whispersystems.textsecure.push.PushAddress; import org.whispersystems.textsecure.push.UnregisteredUserException; import org.whispersystems.textsecure.storage.RecipientDevice; @@ -29,10 +29,16 @@ import org.whispersystems.textsecure.util.InvalidNumberException; import java.io.IOException; -public class PushTextSendJob extends PushSendJob { +import javax.inject.Inject; + +import static org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule.TextSecureMessageSenderFactory; + +public class PushTextSendJob extends PushSendJob implements InjectableType { private static final String TAG = PushTextSendJob.class.getSimpleName(); + @Inject transient TextSecureMessageSenderFactory messageSenderFactory; + private final long messageId; public PushTextSendJob(Context context, long messageId, String destination) { @@ -46,9 +52,7 @@ public class PushTextSendJob extends PushSendJob { } @Override - public void onRun() throws RequirementNotMetException, NoSuchMessageException, RetryLaterException - { - MasterSecret masterSecret = getMasterSecret(); + public void onRun(MasterSecret masterSecret) throws NoSuchMessageException, RetryLaterException { EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsMessageRecord record = database.getMessage(masterSecret, messageId); String destination = record.getIndividualRecipient().getNumber(); @@ -77,7 +81,24 @@ public class PushTextSendJob extends PushSendJob { } } - public void deliver(MasterSecret masterSecret, SmsMessageRecord message, String destination) + @Override + public boolean onShouldRetryThrowable(Throwable throwable) { + if (throwable instanceof RetryLaterException) return true; + + return false; + } + + @Override + public void onCanceled() { + DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); + + long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId); + Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(threadId); + + MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId); + } + + private void deliver(MasterSecret masterSecret, SmsMessageRecord message, String destination) throws UntrustedIdentityException, SecureFallbackApprovalException, InsecureFallbackApprovalException, RetryLaterException { @@ -85,7 +106,7 @@ public class PushTextSendJob extends PushSendJob { try { PushAddress address = getPushAddress(message.getIndividualRecipient()); - TextSecureMessageSender messageSender = TextSecureCommunicationFactory.createSender(context, masterSecret); + TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret); if (message.isEndSession()) { messageSender.sendMessage(address, new TextSecureMessage(message.getDateSent(), null, @@ -105,24 +126,6 @@ public class PushTextSendJob extends PushSendJob { } } - @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RequirementNotMetException) return true; - if (throwable instanceof RetryLaterException) return true; - - return false; - } - - @Override - public void onCanceled() { - DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); - - long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId); - Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(threadId); - - MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId); - } - private void fallbackOrAskApproval(MasterSecret masterSecret, SmsMessageRecord smsMessage, String destination) throws SecureFallbackApprovalException, InsecureFallbackApprovalException { diff --git a/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java b/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java index 105dc0f834..156f6eb76f 100644 --- a/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java +++ b/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java @@ -3,9 +3,11 @@ package org.thoughtcrime.securesms.jobs; import android.content.Context; import android.util.Log; +import org.thoughtcrime.securesms.ApplicationContext; 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.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -22,12 +24,16 @@ import org.whispersystems.textsecure.push.exceptions.PushNetworkException; import java.io.IOException; import java.util.List; -public class RefreshPreKeysJob extends MasterSecretJob { +import javax.inject.Inject; + +public class RefreshPreKeysJob extends MasterSecretJob implements InjectableType { private static final String TAG = RefreshPreKeysJob.class.getSimpleName(); private static final int PREKEY_MINIMUM = 10; + @Inject transient TextSecureAccountManager accountManager; + public RefreshPreKeysJob(Context context) { super(context, JobParameters.newBuilder() .withGroupId(RefreshPreKeysJob.class.getSimpleName()) @@ -43,12 +49,10 @@ public class RefreshPreKeysJob extends MasterSecretJob { } @Override - public void onRun() throws RequirementNotMetException, IOException { + public void onRun(MasterSecret masterSecret) throws IOException { if (!TextSecurePreferences.isPushRegistered(context)) return; - MasterSecret masterSecret = getMasterSecret(); - TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(context); - int availableKeys = accountManager.getPreKeysCount(); + int availableKeys = accountManager.getPreKeysCount(); if (availableKeys >= PREKEY_MINIMUM && TextSecurePreferences.isSignedPreKeyRegistered(context)) { Log.w(TAG, "Available keys sufficient: " + availableKeys); @@ -65,12 +69,14 @@ public class RefreshPreKeysJob extends MasterSecretJob { accountManager.setPreKeys(identityKey.getPublicKey(), lastResortKeyRecord, signedPreKeyRecord, preKeyRecords); TextSecurePreferences.setSignedPreKeyRegistered(context, true); -// PreKeyService.initiateClean(context, masterSecret); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new CleanPreKeysJob(context)); } @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RequirementNotMetException) return true; + public boolean onShouldRetryThrowable(Throwable throwable) { if (throwable instanceof NonSuccessfulResponseCodeException) return false; if (throwable instanceof PushNetworkException) return true; diff --git a/src/org/thoughtcrime/securesms/jobs/SmsDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/SmsDecryptJob.java index 4758dd084a..3294841713 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsDecryptJob.java @@ -62,9 +62,8 @@ public class SmsDecryptJob extends MasterSecretJob { } @Override - public void onRun() throws RequirementNotMetException, NoSuchMessageException { - MasterSecret masterSecret = getMasterSecret(); - EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); + public void onRun(MasterSecret masterSecret) throws NoSuchMessageException { + EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); try { SmsMessageRecord record = database.getMessage(masterSecret, messageId); @@ -94,6 +93,11 @@ public class SmsDecryptJob extends MasterSecretJob { } } + @Override + public boolean onShouldRetryThrowable(Throwable throwable) { + return false; + } + @Override public void onCanceled() { // TODO @@ -166,12 +170,6 @@ public class SmsDecryptJob extends MasterSecretJob { } } - @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RequirementNotMetException) return true; - return false; - } - private String getAsymmetricDecryptedBody(MasterSecret masterSecret, String body) throws InvalidMessageException { diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java index 389c0e3496..3895d1c5de 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java @@ -53,10 +53,9 @@ public class SmsSendJob extends MasterSecretJob { } @Override - public void onRun() throws RequirementNotMetException, NoSuchMessageException { - MasterSecret masterSecret = getMasterSecret(); - EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); - SmsMessageRecord record = database.getMessage(masterSecret, messageId); + public void onRun(MasterSecret masterSecret) throws NoSuchMessageException { + EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); + SmsMessageRecord record = database.getMessage(masterSecret, messageId); try { Log.w(TAG, "Sending message: " + messageId); @@ -73,6 +72,11 @@ public class SmsSendJob extends MasterSecretJob { } } + @Override + public boolean onShouldRetryThrowable(Throwable throwable) { + return false; + } + @Override public void onCanceled() { Log.w(TAG, "onCanceled()"); @@ -83,12 +87,6 @@ public class SmsSendJob extends MasterSecretJob { MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId); } - @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RequirementNotMetException) return true; - return false; - } - private void deliver(MasterSecret masterSecret, SmsMessageRecord record) throws UndeliverableMessageException, InsecureFallbackApprovalException { diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java index 7bdfe32b50..3bcbdfc390 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java @@ -44,9 +44,8 @@ public class SmsSentJob extends MasterSecretJob { } @Override - public void onRun() throws RequirementNotMetException { + public void onRun(MasterSecret masterSecret) { Log.w(TAG, "Got SMS callback: " + action + " , " + result); - MasterSecret masterSecret = getMasterSecret(); switch (action) { case SmsDeliveryListener.SENT_SMS_ACTION: @@ -59,8 +58,7 @@ public class SmsSentJob extends MasterSecretJob { } @Override - public boolean onShouldRetry(Throwable throwable) { - if (throwable instanceof RequirementNotMetException) return true; + public boolean onShouldRetryThrowable(Throwable throwable) { return false; } diff --git a/src/org/thoughtcrime/securesms/push/SecurityEventListener.java b/src/org/thoughtcrime/securesms/push/SecurityEventListener.java new file mode 100644 index 0000000000..6ff2a12fc2 --- /dev/null +++ b/src/org/thoughtcrime/securesms/push/SecurityEventListener.java @@ -0,0 +1,32 @@ +package org.thoughtcrime.securesms.push; + +import android.content.Context; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.crypto.SecurityEvent; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; +import org.thoughtcrime.securesms.recipients.RecipientFactory; +import org.thoughtcrime.securesms.recipients.Recipients; +import org.whispersystems.textsecure.api.TextSecureMessageSender; + +public class SecurityEventListener implements TextSecureMessageSender.EventListener { + private final Context context; + + public SecurityEventListener(Context context) { + this.context = context.getApplicationContext(); + } + + @Override + public void onSecurityEvent(long recipientId) { + Recipients recipients = RecipientFactory.getRecipientsForIds(context, String.valueOf(recipientId), false); + long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); + + SecurityEvent.broadcastSecurityUpdateEvent(context, threadId); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new RefreshPreKeysJob(context)); + } + +} diff --git a/src/org/thoughtcrime/securesms/push/TextSecureCommunicationFactory.java b/src/org/thoughtcrime/securesms/push/TextSecureCommunicationFactory.java index f39de70fba..9601912b55 100644 --- a/src/org/thoughtcrime/securesms/push/TextSecureCommunicationFactory.java +++ b/src/org/thoughtcrime/securesms/push/TextSecureCommunicationFactory.java @@ -18,23 +18,6 @@ import org.whispersystems.textsecure.api.TextSecureMessageSender; import static org.whispersystems.textsecure.api.TextSecureMessageSender.EventListener; public class TextSecureCommunicationFactory { - public static TextSecureMessageSender createSender(Context context, MasterSecret masterSecret) { - return new TextSecureMessageSender(Release.PUSH_URL, - new TextSecurePushTrustStore(context), - TextSecurePreferences.getLocalNumber(context), - TextSecurePreferences.getPushServerPassword(context), - new TextSecureAxolotlStore(context, masterSecret), - Optional.of((EventListener)new SecurityEventListener(context))); - } - - public static TextSecureMessageReceiver createReceiver(Context context, MasterSecret masterSecret) { - return new TextSecureMessageReceiver(TextSecurePreferences.getSignalingKey(context), - Release.PUSH_URL, - new TextSecurePushTrustStore(context), - TextSecurePreferences.getLocalNumber(context), - TextSecurePreferences.getPushServerPassword(context), - new TextSecureAxolotlStore(context, masterSecret)); - } public static TextSecureAccountManager createManager(Context context) { return new TextSecureAccountManager(Release.PUSH_URL, @@ -48,19 +31,4 @@ public class TextSecureCommunicationFactory { number, password); } - private static class SecurityEventListener implements EventListener { - - private final Context context; - - public SecurityEventListener(Context context) { - this.context = context.getApplicationContext(); - } - - @Override - public void onSecurityEvent(long recipientId) { - Recipients recipients = RecipientFactory.getRecipientsForIds(context, String.valueOf(recipientId), false); - long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); - SecurityEvent.broadcastSecurityUpdateEvent(context, threadId); - } - } }