mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-19 21:58:29 +00:00
Add refresh path for PreKey queue.
This commit is contained in:
parent
926d3c929f
commit
ad5d6d5bb7
@ -205,6 +205,7 @@
|
|||||||
<service android:enabled="true" android:name=".service.SendReceiveService"/>
|
<service android:enabled="true" android:name=".service.SendReceiveService"/>
|
||||||
<service android:enabled="true" android:name=".service.RegistrationService"/>
|
<service android:enabled="true" android:name=".service.RegistrationService"/>
|
||||||
<service android:enabled="true" android:name=".service.DirectoryRefreshService"/>
|
<service android:enabled="true" android:name=".service.DirectoryRefreshService"/>
|
||||||
|
<service android:enabled="true" android:name=".service.PreKeyService"/>
|
||||||
<service android:enabled="true" android:name=".gcm.GcmIntentService"/>
|
<service android:enabled="true" android:name=".gcm.GcmIntentService"/>
|
||||||
|
|
||||||
<service android:name=".service.QuickResponseService"
|
<service android:name=".service.QuickResponseService"
|
||||||
|
@ -34,7 +34,6 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -78,38 +77,38 @@ public class PreKeyUtil {
|
|||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<PreKeyRecord> getPreKeys(Context context, MasterSecret masterSecret) {
|
// public static List<PreKeyRecord> getPreKeys(Context context, MasterSecret masterSecret) {
|
||||||
List<PreKeyRecord> records = new LinkedList<PreKeyRecord>();
|
// List<PreKeyRecord> records = new LinkedList<PreKeyRecord>();
|
||||||
File directory = getPreKeysDirectory(context);
|
// File directory = getPreKeysDirectory(context);
|
||||||
String[] keyRecordIds = directory.list();
|
// String[] keyRecordIds = directory.list();
|
||||||
|
//
|
||||||
Arrays.sort(keyRecordIds, new PreKeyRecordIdComparator());
|
// Arrays.sort(keyRecordIds, new PreKeyRecordIdComparator());
|
||||||
|
//
|
||||||
for (String keyRecordId : keyRecordIds) {
|
// for (String keyRecordId : keyRecordIds) {
|
||||||
try {
|
// try {
|
||||||
if (!keyRecordId.equals(PreKeyIndex.FILE_NAME) && Integer.parseInt(keyRecordId) != Medium.MAX_VALUE) {
|
// if (!keyRecordId.equals(PreKeyIndex.FILE_NAME) && Integer.parseInt(keyRecordId) != Medium.MAX_VALUE) {
|
||||||
records.add(new PreKeyRecord(context, masterSecret, Integer.parseInt(keyRecordId)));
|
// records.add(new PreKeyRecord(context, masterSecret, Integer.parseInt(keyRecordId)));
|
||||||
}
|
// }
|
||||||
} catch (InvalidKeyIdException e) {
|
// } catch (InvalidKeyIdException e) {
|
||||||
Log.w("PreKeyUtil", e);
|
// Log.w("PreKeyUtil", e);
|
||||||
new File(getPreKeysDirectory(context), keyRecordId).delete();
|
// new File(getPreKeysDirectory(context), keyRecordId).delete();
|
||||||
} catch (NumberFormatException nfe) {
|
// } catch (NumberFormatException nfe) {
|
||||||
Log.w("PreKeyUtil", nfe);
|
// Log.w("PreKeyUtil", nfe);
|
||||||
new File(getPreKeysDirectory(context), keyRecordId).delete();
|
// new File(getPreKeysDirectory(context), keyRecordId).delete();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return records;
|
// return records;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public static void clearPreKeys(Context context) {
|
// public static void clearPreKeys(Context context) {
|
||||||
File directory = getPreKeysDirectory(context);
|
// File directory = getPreKeysDirectory(context);
|
||||||
String[] keyRecords = directory.list();
|
// String[] keyRecords = directory.list();
|
||||||
|
//
|
||||||
for (String keyRecord : keyRecords) {
|
// for (String keyRecord : keyRecords) {
|
||||||
new File(directory, keyRecord).delete();
|
// new File(directory, keyRecord).delete();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private static void setNextPreKeyId(Context context, int id) {
|
private static void setNextPreKeyId(Context context, int id) {
|
||||||
try {
|
try {
|
||||||
@ -126,7 +125,7 @@ public class PreKeyUtil {
|
|||||||
try {
|
try {
|
||||||
File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);
|
File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);
|
||||||
|
|
||||||
if (nextFile.exists()) {
|
if (!nextFile.exists()) {
|
||||||
return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
|
return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
|
||||||
} else {
|
} else {
|
||||||
InputStreamReader reader = new InputStreamReader(new FileInputStream(nextFile));
|
InputStreamReader reader = new InputStreamReader(new FileInputStream(nextFile));
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package org.whispersystems.textsecure.push;
|
||||||
|
|
||||||
|
public class PreKeyStatus {
|
||||||
|
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
public PreKeyStatus() {}
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@ -40,6 +40,7 @@ public class PushServiceSocket {
|
|||||||
private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/code/%s";
|
private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/code/%s";
|
||||||
private static final String VERIFY_ACCOUNT_PATH = "/v1/accounts/code/%s";
|
private static final String VERIFY_ACCOUNT_PATH = "/v1/accounts/code/%s";
|
||||||
private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/";
|
private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/";
|
||||||
|
private static final String PREKEY_METADATA_PATH = "/v1/keys/";
|
||||||
private static final String PREKEY_PATH = "/v1/keys/%s";
|
private static final String PREKEY_PATH = "/v1/keys/%s";
|
||||||
private static final String PREKEY_DEVICE_PATH = "/v1/keys/%s/%s";
|
private static final String PREKEY_DEVICE_PATH = "/v1/keys/%s/%s";
|
||||||
|
|
||||||
@ -123,6 +124,13 @@ public class PushServiceSocket {
|
|||||||
PreKeyList.toJson(new PreKeyList(lastResortEntity, entities)));
|
PreKeyList.toJson(new PreKeyList(lastResortEntity, entities)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getAvailablePreKeys() throws IOException {
|
||||||
|
String responseText = makeRequest(PREKEY_METADATA_PATH, "GET", null);
|
||||||
|
PreKeyStatus preKeyStatus = new Gson().fromJson(responseText, PreKeyStatus.class);
|
||||||
|
|
||||||
|
return preKeyStatus.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
public List<PreKeyEntity> getPreKeys(PushAddress destination) throws IOException {
|
public List<PreKeyEntity> getPreKeys(PushAddress destination) throws IOException {
|
||||||
try {
|
try {
|
||||||
String deviceId = String.valueOf(destination.getDeviceId());
|
String deviceId = String.valueOf(destination.getDeviceId());
|
||||||
|
@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessageV2;
|
|||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||||
|
import org.thoughtcrime.securesms.service.PreKeyService;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
|
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
@ -121,6 +122,8 @@ public class KeyExchangeProcessorV2 extends KeyExchangeProcessor {
|
|||||||
PreKeyRecord.delete(context, preKeyId);
|
PreKeyRecord.delete(context, preKeyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PreKeyService.initiateRefresh(context, masterSecret);
|
||||||
|
|
||||||
DatabaseFactory.getIdentityDatabase(context)
|
DatabaseFactory.getIdentityDatabase(context)
|
||||||
.saveIdentity(masterSecret, recipientDevice.getRecipientId(), theirIdentityKey);
|
.saveIdentity(masterSecret, recipientDevice.getRecipientId(), theirIdentityKey);
|
||||||
}
|
}
|
||||||
|
90
src/org/thoughtcrime/securesms/service/PreKeyService.java
Normal file
90
src/org/thoughtcrime/securesms/service/PreKeyService.java
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package org.thoughtcrime.securesms.service;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||||
|
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||||
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
|
import org.whispersystems.textsecure.crypto.PreKeyUtil;
|
||||||
|
import org.whispersystems.textsecure.crypto.ecc.Curve;
|
||||||
|
import org.whispersystems.textsecure.push.PushServiceSocket;
|
||||||
|
import org.whispersystems.textsecure.storage.PreKeyRecord;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
public class PreKeyService extends Service {
|
||||||
|
|
||||||
|
private static final String TAG = PreKeyService.class.getSimpleName();
|
||||||
|
public static final String REFRESH_ACTION = "org.thoughtcrime.securesms.PreKeyService.REFRESH";
|
||||||
|
|
||||||
|
private static final int PREKEY_MINIMUM = 10;
|
||||||
|
|
||||||
|
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
|
public static void initiateRefresh(Context context, MasterSecret masterSecret) {
|
||||||
|
Intent intent = new Intent(context, PreKeyService.class);
|
||||||
|
intent.setAction(PreKeyService.REFRESH_ACTION);
|
||||||
|
intent.putExtra("master_secret", masterSecret);
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flats, int startId) {
|
||||||
|
if (REFRESH_ACTION.equals(intent.getAction())) {
|
||||||
|
MasterSecret masterSecret = intent.getParcelableExtra("master_secret");
|
||||||
|
executor.execute(new RefreshTask(this, masterSecret));
|
||||||
|
}
|
||||||
|
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RefreshTask implements Runnable {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final MasterSecret masterSecret;
|
||||||
|
|
||||||
|
public RefreshTask(Context context, MasterSecret masterSecret) {
|
||||||
|
this.context = context.getApplicationContext();
|
||||||
|
this.masterSecret = masterSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
if (!TextSecurePreferences.isPushRegistered(context)) return;
|
||||||
|
|
||||||
|
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
||||||
|
int availableKeys = socket.getAvailablePreKeys();
|
||||||
|
|
||||||
|
if (availableKeys >= PREKEY_MINIMUM) {
|
||||||
|
Log.w(TAG, "Available keys sufficient: " + availableKeys);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PreKeyRecord> preKeyRecords = PreKeyUtil.generatePreKeys(context, masterSecret);
|
||||||
|
PreKeyRecord lastResortKeyRecord = PreKeyUtil.generateLastResortKey(context, masterSecret);
|
||||||
|
IdentityKey identityKey = IdentityKeyUtil.getIdentityKey(context, Curve.DJB_TYPE);
|
||||||
|
|
||||||
|
Log.w(TAG, "Registering new prekeys...");
|
||||||
|
|
||||||
|
socket.registerPreKeys(identityKey, lastResortKeyRecord, preKeyRecords);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -68,7 +68,6 @@ public class RegistrationService extends Service {
|
|||||||
public static final String GCM_REGISTRATION_ID = "GCMRegistrationId";
|
public static final String GCM_REGISTRATION_ID = "GCMRegistrationId";
|
||||||
|
|
||||||
private static final long REGISTRATION_TIMEOUT_MILLIS = 120000;
|
private static final long REGISTRATION_TIMEOUT_MILLIS = 120000;
|
||||||
private static final Object GENERATING_PREKEYS_SEMAPHOR = new Object();
|
|
||||||
|
|
||||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
private final Binder binder = new RegistrationServiceBinder();
|
private final Binder binder = new RegistrationServiceBinder();
|
||||||
@ -144,27 +143,6 @@ public class RegistrationService extends Service {
|
|||||||
registerReceiver(gcmRegistrationReceiver, filter);
|
registerReceiver(gcmRegistrationReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializePreKeyGenerator(final MasterSecret masterSecret) {
|
|
||||||
synchronized (GENERATING_PREKEYS_SEMAPHOR) {
|
|
||||||
if (generatingPreKeys) return;
|
|
||||||
else generatingPreKeys = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
new Thread() {
|
|
||||||
public void run() {
|
|
||||||
if (PreKeyUtil.getPreKeys(RegistrationService.this, masterSecret).size() < PreKeyUtil.BATCH_SIZE) {
|
|
||||||
PreKeyUtil.generatePreKeys(RegistrationService.this, masterSecret);
|
|
||||||
PreKeyUtil.generateLastResortKey(RegistrationService.this, masterSecret);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (GENERATING_PREKEYS_SEMAPHOR) {
|
|
||||||
generatingPreKeys = false;
|
|
||||||
GENERATING_PREKEYS_SEMAPHOR.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void shutdownChallengeListener() {
|
private synchronized void shutdownChallengeListener() {
|
||||||
if (challengeReceiver != null) {
|
if (challengeReceiver != null) {
|
||||||
unregisterReceiver(challengeReceiver);
|
unregisterReceiver(challengeReceiver);
|
||||||
@ -195,7 +173,6 @@ public class RegistrationService extends Service {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
initializeGcmRegistrationListener();
|
initializeGcmRegistrationListener();
|
||||||
initializePreKeyGenerator(masterSecret);
|
|
||||||
|
|
||||||
PushServiceSocket socket = PushServiceSocketFactory.create(this, number, password);
|
PushServiceSocket socket = PushServiceSocketFactory.create(this, number, password);
|
||||||
|
|
||||||
@ -240,8 +217,6 @@ public class RegistrationService extends Service {
|
|||||||
|
|
||||||
initializeChallengeListener();
|
initializeChallengeListener();
|
||||||
initializeGcmRegistrationListener();
|
initializeGcmRegistrationListener();
|
||||||
initializePreKeyGenerator(masterSecret);
|
|
||||||
|
|
||||||
|
|
||||||
setState(new RegistrationState(RegistrationState.STATE_CONNECTING, number));
|
setState(new RegistrationState(RegistrationState.STATE_CONNECTING, number));
|
||||||
PushServiceSocket socket = PushServiceSocketFactory.create(this, number, password);
|
PushServiceSocket socket = PushServiceSocketFactory.create(this, number, password);
|
||||||
@ -287,7 +262,7 @@ public class RegistrationService extends Service {
|
|||||||
{
|
{
|
||||||
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
|
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
|
||||||
IdentityKey identityKey = IdentityKeyUtil.getIdentityKey(this, Curve.DJB_TYPE);
|
IdentityKey identityKey = IdentityKeyUtil.getIdentityKey(this, Curve.DJB_TYPE);
|
||||||
List<PreKeyRecord> records = waitForPreKeys(masterSecret);
|
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(this, masterSecret);
|
||||||
PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this, masterSecret);
|
PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this, masterSecret);
|
||||||
socket.registerPreKeys(identityKey, lastResort, records);
|
socket.registerPreKeys(identityKey, lastResort, records);
|
||||||
|
|
||||||
@ -333,20 +308,6 @@ public class RegistrationService extends Service {
|
|||||||
return this.gcmRegistrationId;
|
return this.gcmRegistrationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<PreKeyRecord> waitForPreKeys(MasterSecret masterSecret) {
|
|
||||||
synchronized (GENERATING_PREKEYS_SEMAPHOR) {
|
|
||||||
while (generatingPreKeys) {
|
|
||||||
try {
|
|
||||||
GENERATING_PREKEYS_SEMAPHOR.wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return PreKeyUtil.getPreKeys(this, masterSecret);
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void challengeReceived(String challenge) {
|
private synchronized void challengeReceived(String challenge) {
|
||||||
this.challenge = challenge;
|
this.challenge = challenge;
|
||||||
notifyAll();
|
notifyAll();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user