mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
Start increasing frequency of signed prekey rotation
// FREEBIE
This commit is contained in:
parent
884d8b7f72
commit
bb5dcb7131
@ -502,7 +502,12 @@
|
||||
|
||||
<receiver android:name=".service.DirectoryRefreshListener">
|
||||
<intent-filter>
|
||||
<action android:name="org.whispersystems.whisperpush.DIRECTORY_REFRESH"/>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".service.RotateSignedPreKeyListener">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
@ -34,7 +34,9 @@ import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirementProvi
|
||||
import org.thoughtcrime.securesms.jobs.requirements.MediaNetworkRequirementProvider;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.ServiceRequirementProvider;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
|
||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.jobqueue.JobManager;
|
||||
import org.whispersystems.jobqueue.dependencies.DependencyInjector;
|
||||
@ -75,6 +77,7 @@ public class ApplicationContext extends Application implements DependencyInjecto
|
||||
initializeExpiringMessageManager();
|
||||
initializeGcmCheck();
|
||||
initializeSignedPreKeyCheck();
|
||||
initializePeriodicTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -150,4 +153,9 @@ public class ApplicationContext extends Application implements DependencyInjecto
|
||||
this.expiringMessageManager = new ExpiringMessageManager(this);
|
||||
}
|
||||
|
||||
private void initializePeriodicTasks() {
|
||||
RotateSignedPreKeyListener.schedule(this);
|
||||
DirectoryRefreshListener.schedule(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -73,7 +73,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
|
||||
initializeContactUpdatesReceiver();
|
||||
|
||||
DirectoryRefreshListener.schedule(this);
|
||||
RatingManager.showRatingDialogIfNecessary(this);
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ import org.whispersystems.libsignal.state.PreKeyStore;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyStore;
|
||||
import org.whispersystems.libsignal.util.Medium;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -46,7 +47,9 @@ import java.util.List;
|
||||
|
||||
public class PreKeyUtil {
|
||||
|
||||
public static final int BATCH_SIZE = 100;
|
||||
private static final String TAG = PreKeyUtil.class.getName();
|
||||
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
public static List<PreKeyRecord> generatePreKeys(Context context) {
|
||||
PreKeyStore preKeyStore = new TextSecurePreKeyStore(context);
|
||||
@ -66,7 +69,7 @@ public class PreKeyUtil {
|
||||
return records;
|
||||
}
|
||||
|
||||
public static SignedPreKeyRecord generateSignedPreKey(Context context, IdentityKeyPair identityKeyPair)
|
||||
public static SignedPreKeyRecord generateSignedPreKey(Context context, IdentityKeyPair identityKeyPair, boolean active)
|
||||
{
|
||||
try {
|
||||
SignedPreKeyStore signedPreKeyStore = new TextSecurePreKeyStore(context);
|
||||
@ -78,6 +81,10 @@ public class PreKeyUtil {
|
||||
signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record);
|
||||
setNextSignedPreKeyId(context, (signedPreKeyId + 1) % Medium.MAX_VALUE);
|
||||
|
||||
if (active) {
|
||||
setActiveSignedPreKeyId(context, signedPreKeyId);
|
||||
}
|
||||
|
||||
return record;
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
@ -104,7 +111,7 @@ public class PreKeyUtil {
|
||||
return record;
|
||||
}
|
||||
|
||||
private static void setNextPreKeyId(Context context, int id) {
|
||||
private static synchronized void setNextPreKeyId(Context context, int id) {
|
||||
try {
|
||||
File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);
|
||||
FileOutputStream fout = new FileOutputStream(nextFile);
|
||||
@ -115,18 +122,36 @@ public class PreKeyUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static void setNextSignedPreKeyId(Context context, int id) {
|
||||
private static synchronized void setNextSignedPreKeyId(Context context, int id) {
|
||||
try {
|
||||
File nextFile = new File(getSignedPreKeysDirectory(context), SignedPreKeyIndex.FILE_NAME);
|
||||
FileOutputStream fout = new FileOutputStream(nextFile);
|
||||
fout.write(JsonUtils.toJson(new SignedPreKeyIndex(id)).getBytes());
|
||||
fout.close();
|
||||
SignedPreKeyIndex index = getSignedPreKeyIndex(context).or(new SignedPreKeyIndex());
|
||||
index.nextSignedPreKeyId = id;
|
||||
|
||||
setSignedPreKeyIndex(context, index);
|
||||
} catch (IOException e) {
|
||||
Log.w("PreKeyUtil", e);
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNextPreKeyId(Context context) {
|
||||
public static synchronized void setActiveSignedPreKeyId(Context context, int id) {
|
||||
try {
|
||||
SignedPreKeyIndex index = getSignedPreKeyIndex(context).or(new SignedPreKeyIndex());
|
||||
index.activeSignedPreKeyId = id;
|
||||
|
||||
setSignedPreKeyIndex(context, index);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized int getActiveSignedPreKeyId(Context context) {
|
||||
Optional<SignedPreKeyIndex> index = getSignedPreKeyIndex(context);
|
||||
|
||||
if (index.isPresent()) return index.get().activeSignedPreKeyId;
|
||||
else return -1;
|
||||
}
|
||||
|
||||
private static synchronized int getNextPreKeyId(Context context) {
|
||||
try {
|
||||
File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);
|
||||
|
||||
@ -144,7 +169,7 @@ public class PreKeyUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNextSignedPreKeyId(Context context) {
|
||||
private static synchronized int getNextSignedPreKeyId(Context context) {
|
||||
try {
|
||||
File nextFile = new File(getSignedPreKeysDirectory(context), SignedPreKeyIndex.FILE_NAME);
|
||||
|
||||
@ -162,6 +187,32 @@ public class PreKeyUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static synchronized Optional<SignedPreKeyIndex> getSignedPreKeyIndex(Context context) {
|
||||
File indexFile = new File(getSignedPreKeysDirectory(context), SignedPreKeyIndex.FILE_NAME);
|
||||
|
||||
if (!indexFile.exists()) {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
try {
|
||||
InputStreamReader reader = new InputStreamReader(new FileInputStream(indexFile));
|
||||
SignedPreKeyIndex index = JsonUtils.fromJson(reader, SignedPreKeyIndex.class);
|
||||
reader.close();
|
||||
|
||||
return Optional.of(index);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
private static synchronized void setSignedPreKeyIndex(Context context, SignedPreKeyIndex index) throws IOException {
|
||||
File indexFile = new File(getSignedPreKeysDirectory(context), SignedPreKeyIndex.FILE_NAME);
|
||||
FileOutputStream fout = new FileOutputStream(indexFile);
|
||||
fout.write(JsonUtils.toJson(index).getBytes());
|
||||
fout.close();
|
||||
}
|
||||
|
||||
private static File getPreKeysDirectory(Context context) {
|
||||
return getKeysDirectory(context, TextSecurePreKeyStore.PREKEY_DIRECTORY);
|
||||
}
|
||||
@ -198,11 +249,11 @@ public class PreKeyUtil {
|
||||
@JsonProperty
|
||||
private int nextSignedPreKeyId;
|
||||
|
||||
@JsonProperty
|
||||
private int activeSignedPreKeyId = -1;
|
||||
|
||||
public SignedPreKeyIndex() {}
|
||||
|
||||
public SignedPreKeyIndex(int nextSignedPreKeyId) {
|
||||
this.nextSignedPreKeyId = nextSignedPreKeyId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -79,7 +79,9 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||
|
||||
for (File signedPreKeyFile : directory.listFiles()) {
|
||||
try {
|
||||
results.add(new SignedPreKeyRecord(loadSerializedRecord(signedPreKeyFile)));
|
||||
if (!"index.dat".equals(signedPreKeyFile.getName())) {
|
||||
results.add(new SignedPreKeyRecord(loadSerializedRecord(signedPreKeyFile)));
|
||||
}
|
||||
} catch (IOException | InvalidMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.push.SecurityEventListener;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
import org.thoughtcrime.securesms.service.MessageRetrievalService;
|
||||
@ -56,7 +57,8 @@ import dagger.Provides;
|
||||
GcmRefreshJob.class,
|
||||
RequestGroupInfoJob.class,
|
||||
PushGroupUpdateJob.class,
|
||||
AvatarDownloadJob.class})
|
||||
AvatarDownloadJob.class,
|
||||
RotateSignedPreKeyJob.class})
|
||||
public class SignalCommunicationModule {
|
||||
|
||||
private final Context context;
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
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.whispersystems.jobqueue.JobParameters;
|
||||
@ -11,7 +12,6 @@ import org.whispersystems.libsignal.InvalidKeyIdException;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyStore;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
@ -30,7 +30,7 @@ public class CleanPreKeysJob extends MasterSecretJob implements InjectableType {
|
||||
|
||||
private static final String TAG = CleanPreKeysJob.class.getSimpleName();
|
||||
|
||||
private static final int ARCHIVE_AGE_DAYS = 15;
|
||||
private static final long ARCHIVE_AGE = TimeUnit.DAYS.toMillis(7);
|
||||
|
||||
@Inject transient SignalServiceAccountManager accountManager;
|
||||
@Inject transient SignedPreKeyStoreFactory signedPreKeyStoreFactory;
|
||||
@ -51,17 +51,20 @@ public class CleanPreKeysJob extends MasterSecretJob implements InjectableType {
|
||||
@Override
|
||||
public void onRun(MasterSecret masterSecret) throws IOException {
|
||||
try {
|
||||
SignedPreKeyStore signedPreKeyStore = signedPreKeyStoreFactory.create();
|
||||
SignedPreKeyEntity currentSignedPreKey = accountManager.getSignedPreKey();
|
||||
Log.w(TAG, "Cleaning prekeys...");
|
||||
|
||||
if (currentSignedPreKey == null) return;
|
||||
int activeSignedPreKeyId = PreKeyUtil.getActiveSignedPreKeyId(context);
|
||||
SignedPreKeyStore signedPreKeyStore = signedPreKeyStoreFactory.create();
|
||||
|
||||
SignedPreKeyRecord currentRecord = signedPreKeyStore.loadSignedPreKey(currentSignedPreKey.getKeyId());
|
||||
if (activeSignedPreKeyId < 0) return;
|
||||
|
||||
SignedPreKeyRecord currentRecord = signedPreKeyStore.loadSignedPreKey(activeSignedPreKeyId);
|
||||
List<SignedPreKeyRecord> allRecords = signedPreKeyStore.loadSignedPreKeys();
|
||||
LinkedList<SignedPreKeyRecord> oldRecords = removeRecordFrom(currentRecord, allRecords);
|
||||
|
||||
Collections.sort(oldRecords, new SignedPreKeySorter());
|
||||
|
||||
Log.w(TAG, "Active signed prekey: " + activeSignedPreKeyId);
|
||||
Log.w(TAG, "Old signed prekey record count: " + oldRecords.size());
|
||||
|
||||
boolean foundAgedRecord = false;
|
||||
@ -69,7 +72,7 @@ public class CleanPreKeysJob extends MasterSecretJob implements InjectableType {
|
||||
for (SignedPreKeyRecord oldRecord : oldRecords) {
|
||||
long archiveDuration = System.currentTimeMillis() - oldRecord.getTimestamp();
|
||||
|
||||
if (archiveDuration >= TimeUnit.DAYS.toMillis(ARCHIVE_AGE_DAYS)) {
|
||||
if (archiveDuration >= ARCHIVE_AGE) {
|
||||
if (!foundAgedRecord) {
|
||||
foundAgedRecord = true;
|
||||
} else {
|
||||
|
@ -53,7 +53,7 @@ public class CreateSignedPreKeyJob extends MasterSecretJob implements Injectable
|
||||
}
|
||||
|
||||
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context);
|
||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKeyPair);
|
||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true);
|
||||
|
||||
accountManager.setSignedPreKey(signedPreKeyRecord);
|
||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
||||
|
@ -70,12 +70,10 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
@Override
|
||||
public void onAdded() {
|
||||
// DatabaseFactory.getMmsDatabase(context)
|
||||
// .markAsSending(messageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSend(MasterSecret masterSecret)
|
||||
public void onPushSend(MasterSecret masterSecret)
|
||||
throws MmsException, IOException, NoSuchMessageException
|
||||
{
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
@ -84,8 +82,6 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
try {
|
||||
deliver(masterSecret, message, filterRecipientId);
|
||||
|
||||
// database.markAsPush(messageId);
|
||||
// database.markAsSecure(messageId);
|
||||
database.markAsSent(messageId, true);
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
|
||||
@ -114,10 +110,8 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
}
|
||||
|
||||
database.addFailures(messageId, failures);
|
||||
// database.markAsPush(messageId);
|
||||
|
||||
if (e.getNetworkExceptions().isEmpty() && e.getUntrustedIdentityExceptions().isEmpty()) {
|
||||
// database.markAsSecure(messageId);
|
||||
database.markAsSent(messageId, true);
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
} else {
|
||||
|
@ -53,13 +53,11 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
@Override
|
||||
public void onAdded() {
|
||||
// MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
||||
// mmsDatabase.markAsSending(messageId);
|
||||
// mmsDatabase.markAsPush(messageId);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSend(MasterSecret masterSecret)
|
||||
public void onPushSend(MasterSecret masterSecret)
|
||||
throws RetryLaterException, MmsException, NoSuchMessageException,
|
||||
UndeliverableMessageException
|
||||
{
|
||||
@ -69,8 +67,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
try {
|
||||
deliver(masterSecret, message);
|
||||
// database.markAsPush(messageId);
|
||||
// database.markAsSecure(messageId);
|
||||
database.markAsSent(messageId, true);
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
|
||||
@ -91,7 +87,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
database.addMismatchedIdentity(messageId, recipientId, uie.getIdentityKey());
|
||||
database.markAsSentFailed(messageId);
|
||||
// database.markAsPush(messageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@ package org.thoughtcrime.securesms.jobs;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.TextSecureExpiredException;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@ -12,6 +14,7 @@ import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||
@ -48,6 +51,19 @@ public abstract class PushSendJob extends SendJob {
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void onSend(MasterSecret masterSecret) throws Exception {
|
||||
if (TextSecurePreferences.getSignedPreKeyFailureCount(context) > 5) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RotateSignedPreKeyJob(context));
|
||||
|
||||
throw new TextSecureExpiredException("Too many signed prekey rotation failures");
|
||||
}
|
||||
|
||||
onPushSend(masterSecret);
|
||||
}
|
||||
|
||||
protected SignalServiceAddress getPushAddress(String number) throws InvalidNumberException {
|
||||
String e164number = Util.canonicalizeNumber(context, number);
|
||||
String relay = TextSecureDirectory.getInstance(context).getRelay(e164number);
|
||||
@ -93,4 +109,6 @@ public abstract class PushSendJob extends SendJob {
|
||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void onPushSend(MasterSecret masterSecret) throws Exception;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
public void onAdded() {}
|
||||
|
||||
@Override
|
||||
public void onSend(MasterSecret masterSecret) throws NoSuchMessageException, RetryLaterException {
|
||||
public void onPushSend(MasterSecret masterSecret) throws NoSuchMessageException, RetryLaterException {
|
||||
ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager();
|
||||
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
||||
SmsMessageRecord record = database.getMessage(masterSecret, messageId);
|
||||
|
@ -60,12 +60,13 @@ public class RefreshPreKeysJob extends MasterSecretJob implements InjectableType
|
||||
List<PreKeyRecord> preKeyRecords = PreKeyUtil.generatePreKeys(context);
|
||||
PreKeyRecord lastResortKeyRecord = PreKeyUtil.generateLastResortKey(context);
|
||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
|
||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey);
|
||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false);
|
||||
|
||||
Log.w(TAG, "Registering new prekeys...");
|
||||
|
||||
accountManager.setPreKeys(identityKey.getPublicKey(), lastResortKeyRecord, signedPreKeyRecord, preKeyRecords);
|
||||
|
||||
PreKeyUtil.setActiveSignedPreKeyId(context, signedPreKeyRecord.getId());
|
||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
|
@ -0,0 +1,69 @@
|
||||
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.util.TextSecurePreferences;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class RotateSignedPreKeyJob extends MasterSecretJob implements InjectableType {
|
||||
|
||||
private static final String TAG = RotateSignedPreKeyJob.class.getName();
|
||||
|
||||
@Inject transient SignalServiceAccountManager accountManager;
|
||||
|
||||
public RotateSignedPreKeyJob(Context context) {
|
||||
super(context, JobParameters.newBuilder()
|
||||
.withRequirement(new NetworkRequirement(context))
|
||||
.withRequirement(new MasterSecretRequirement(context))
|
||||
.withRetryCount(5)
|
||||
.create());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdded() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun(MasterSecret masterSecret) throws Exception {
|
||||
Log.w(TAG, "Rotating signed prekey...");
|
||||
|
||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
|
||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false);
|
||||
|
||||
accountManager.setSignedPreKey(signedPreKeyRecord);
|
||||
|
||||
PreKeyUtil.setActiveSignedPreKeyId(context, signedPreKeyRecord.getId());
|
||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
||||
TextSecurePreferences.setSignedPreKeyFailureCount(context, 0);
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new CleanPreKeysJob(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetryThrowable(Exception exception) {
|
||||
return exception instanceof PushNetworkException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
TextSecurePreferences.setSignedPreKeyFailureCount(context, TextSecurePreferences.getSignedPreKeyFailureCount(context) + 1);
|
||||
}
|
||||
}
|
@ -1,64 +1,39 @@
|
||||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
public class DirectoryRefreshListener extends BroadcastReceiver {
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
private static final String TAG = DirectoryRefreshListener.class.getSimpleName();
|
||||
public class DirectoryRefreshListener extends PersistentAlarmManagerListener {
|
||||
|
||||
private static final String REFRESH_EVENT = "org.whispersystems.whisperpush.DIRECTORY_REFRESH";
|
||||
private static final String BOOT_EVENT = "android.intent.action.BOOT_COMPLETED";
|
||||
|
||||
private static final long INTERVAL = 12 * 60 * 60 * 1000; // 12 hours.
|
||||
private static final long INTERVAL = TimeUnit.HOURS.toMillis(12);
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (REFRESH_EVENT.equals(intent.getAction())) handleRefreshAction(context);
|
||||
else if (BOOT_EVENT.equals(intent.getAction())) handleBootEvent(context);
|
||||
protected long getNextScheduledExecutionTime(Context context) {
|
||||
return TextSecurePreferences.getDirectoryRefreshTime(context);
|
||||
}
|
||||
|
||||
private void handleBootEvent(Context context) {
|
||||
schedule(context);
|
||||
}
|
||||
@Override
|
||||
protected long onAlarm(Context context, long scheduledTime) {
|
||||
if (scheduledTime != 0 && TextSecurePreferences.isPushRegistered(context)) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(context));
|
||||
}
|
||||
|
||||
private void handleRefreshAction(Context context) {
|
||||
schedule(context);
|
||||
long newTime = System.currentTimeMillis() + INTERVAL;
|
||||
TextSecurePreferences.setDirectoryRefreshTime(context, newTime);
|
||||
|
||||
return newTime;
|
||||
}
|
||||
|
||||
public static void schedule(Context context) {
|
||||
if (!TextSecurePreferences.isPushRegistered(context)) return;
|
||||
|
||||
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
|
||||
Intent intent = new Intent(DirectoryRefreshListener.REFRESH_EVENT);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
|
||||
long time = TextSecurePreferences.getDirectoryRefreshTime(context);
|
||||
|
||||
if (time <= System.currentTimeMillis()) {
|
||||
if (time != 0) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(context));
|
||||
}
|
||||
|
||||
time = System.currentTimeMillis() + INTERVAL;
|
||||
}
|
||||
|
||||
Log.w(TAG, "Scheduling for: " + time);
|
||||
|
||||
alarmManager.cancel(pendingIntent);
|
||||
alarmManager.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);
|
||||
|
||||
TextSecurePreferences.setDirectoryRefreshTime(context, time);
|
||||
new DirectoryRefreshListener().onReceive(context, new Intent());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
public abstract class PersistentAlarmManagerListener extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = PersistentAlarmManagerListener.class.getSimpleName();
|
||||
|
||||
protected abstract long getNextScheduledExecutionTime(Context context);
|
||||
protected abstract long onAlarm(Context context, long scheduledTime);
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
long scheduledTime = getNextScheduledExecutionTime(context);
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
Intent alarmIntent = new Intent(context, getClass());
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
|
||||
|
||||
if (System.currentTimeMillis() >= scheduledTime) {
|
||||
scheduledTime = onAlarm(context, scheduledTime);
|
||||
}
|
||||
|
||||
Log.w(TAG, getClass() + " scheduling for: " + scheduledTime);
|
||||
|
||||
alarmManager.cancel(pendingIntent);
|
||||
alarmManager.set(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent);
|
||||
}
|
||||
}
|
@ -239,7 +239,7 @@ public class RegistrationService extends Service {
|
||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(this);
|
||||
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(this);
|
||||
PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this);
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, identityKey);
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, identityKey, true);
|
||||
accountManager.setPreKeys(identityKey.getPublicKey(),lastResort, signedPreKey, records);
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
|
||||
@ -261,6 +261,7 @@ public class RegistrationService extends Service {
|
||||
redPhoneAccountManager.createAccount(verificationToken, new RedPhoneAccountAttributes(signalingKey, gcmRegistrationId));
|
||||
|
||||
DirectoryRefreshListener.schedule(this);
|
||||
RotateSignedPreKeyListener.schedule(this);
|
||||
}
|
||||
|
||||
private synchronized String waitForChallenge() throws AccountVerificationTimeoutException {
|
||||
|
@ -0,0 +1,39 @@
|
||||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RotateSignedPreKeyListener extends PersistentAlarmManagerListener {
|
||||
|
||||
private static final long INTERVAL = TimeUnit.DAYS.toMillis(2);
|
||||
|
||||
@Override
|
||||
protected long getNextScheduledExecutionTime(Context context) {
|
||||
return TextSecurePreferences.getSignedPreKeyRotationTime(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long onAlarm(Context context, long scheduledTime) {
|
||||
if (scheduledTime != 0 && TextSecurePreferences.isPushRegistered(context)) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RotateSignedPreKeyJob(context));
|
||||
}
|
||||
|
||||
long nextTime = System.currentTimeMillis() + INTERVAL;
|
||||
TextSecurePreferences.setSignedPreKeyRotationTime(context, nextTime);
|
||||
|
||||
return nextTime;
|
||||
}
|
||||
|
||||
public static void schedule(Context context) {
|
||||
new RotateSignedPreKeyListener().onReceive(context, new Intent());
|
||||
}
|
||||
}
|
@ -72,6 +72,7 @@ public class TextSecurePreferences {
|
||||
private static final String PROMPTED_SHARE_PREF = "pref_prompted_share";
|
||||
private static final String SIGNALING_KEY_PREF = "pref_signaling_key";
|
||||
private static final String DIRECTORY_FRESH_TIME_PREF = "pref_directory_refresh_time";
|
||||
private static final String SIGNED_PREKEY_ROTATION_TIME_PREF = "pref_signed_pre_key_rotation_time";
|
||||
private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications";
|
||||
private static final String BLOCKING_IDENTITY_CHANGES_PREF = "pref_blocking_identity_changes";
|
||||
|
||||
@ -84,6 +85,7 @@ public class TextSecurePreferences {
|
||||
private static final String WEBSOCKET_REGISTERED_PREF = "pref_websocket_registered";
|
||||
private static final String RATING_LATER_PREF = "pref_rating_later";
|
||||
private static final String RATING_ENABLED_PREF = "pref_rating_enabled";
|
||||
private static final String SIGNED_PREKEY_FAILURE_COUNT_PREF = "pref_signed_prekey_failure_count";
|
||||
|
||||
public static final String REPEAT_ALERTS_PREF = "pref_repeat_alerts";
|
||||
public static final String NOTIFICATION_PRIVACY_PREF = "pref_notification_privacy";
|
||||
@ -122,6 +124,14 @@ public class TextSecurePreferences {
|
||||
setBooleanPreference(context, BLOCKING_IDENTITY_CHANGES_PREF, value);
|
||||
}
|
||||
|
||||
public static void setSignedPreKeyFailureCount(Context context, int value) {
|
||||
setIntegerPrefrence(context, SIGNED_PREKEY_FAILURE_COUNT_PREF, value);
|
||||
}
|
||||
|
||||
public static int getSignedPreKeyFailureCount(Context context) {
|
||||
return getIntegerPreference(context, SIGNED_PREKEY_FAILURE_COUNT_PREF, 0);
|
||||
}
|
||||
|
||||
public static NotificationPrivacyPreference getNotificationPrivacy(Context context) {
|
||||
return new NotificationPrivacyPreference(getStringPreference(context, NOTIFICATION_PRIVACY_PREF, "all"));
|
||||
}
|
||||
@ -214,6 +224,14 @@ public class TextSecurePreferences {
|
||||
return getBooleanPreference(context, IN_THREAD_NOTIFICATION_PREF, true);
|
||||
}
|
||||
|
||||
public static long getSignedPreKeyRotationTime(Context context) {
|
||||
return getLongPreference(context, SIGNED_PREKEY_ROTATION_TIME_PREF, 0L);
|
||||
}
|
||||
|
||||
public static void setSignedPreKeyRotationTime(Context context, long value) {
|
||||
setLongPreference(context, SIGNED_PREKEY_ROTATION_TIME_PREF, value);
|
||||
}
|
||||
|
||||
public static long getDirectoryRefreshTime(Context context) {
|
||||
return getLongPreference(context, DIRECTORY_FRESH_TIME_PREF, 0L);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user