mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-25 22:49:42 +00:00
Support for using Signal without Play Services
This is now possible with beta calling, so non-GCM users are a part of beta calling by default. // FREEBIE
This commit is contained in:
@@ -5,9 +5,11 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.gcm.GcmBroadcastReceiver;
|
||||
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
|
||||
@@ -24,16 +26,20 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class MessageRetrievalService extends Service implements Runnable, InjectableType, RequirementListener {
|
||||
public class MessageRetrievalService extends Service implements InjectableType, RequirementListener {
|
||||
|
||||
private static final String TAG = MessageRetrievalService.class.getSimpleName();
|
||||
|
||||
public static final String ACTION_ACTIVITY_STARTED = "ACTIVITY_STARTED";
|
||||
public static final String ACTION_ACTIVITY_FINISHED = "ACTIVITY_FINISHED";
|
||||
public static final String ACTION_PUSH_RECEIVED = "PUSH_RECEIVED";
|
||||
public static final String ACTION_INITIALIZE = "INITIALIZE";
|
||||
public static final int FOREGROUND_ID = 313399;
|
||||
|
||||
private static final long REQUEST_TIMEOUT_MINUTES = 1;
|
||||
|
||||
private NetworkRequirement networkRequirement;
|
||||
@@ -42,8 +48,9 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
@Inject
|
||||
public SignalServiceMessageReceiver receiver;
|
||||
|
||||
private int activeActivities = 0;
|
||||
private List<Intent> pushPending = new LinkedList<>();
|
||||
private int activeActivities = 0;
|
||||
private List<Intent> pushPending = new LinkedList<>();
|
||||
private MessageRetrievalThread retrievalThread = null;
|
||||
|
||||
public static SignalServiceMessagePipe pipe = null;
|
||||
|
||||
@@ -56,7 +63,11 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
networkRequirementProvider = new NetworkRequirementProvider(this);
|
||||
|
||||
networkRequirementProvider.setListener(this);
|
||||
new Thread(this, "MessageRetrievalService").start();
|
||||
|
||||
retrievalThread = new MessageRetrievalThread();
|
||||
retrievalThread.start();
|
||||
|
||||
setForegroundIfNecessary();
|
||||
}
|
||||
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
@@ -70,44 +81,11 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
Log.w(TAG, "Waiting for websocket state change....");
|
||||
waitForConnectionNecessary();
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
Log.w(TAG, "Making websocket connection....");
|
||||
pipe = receiver.createMessagePipe();
|
||||
|
||||
try {
|
||||
while (isConnectionNecessary()) {
|
||||
try {
|
||||
Log.w(TAG, "Reading message...");
|
||||
pipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
|
||||
new SignalServiceMessagePipe.MessagePipeCallback() {
|
||||
@Override
|
||||
public void onMessage(SignalServiceEnvelope envelope) {
|
||||
Log.w(TAG, "Retrieved envelope! " + envelope.getSource());
|
||||
|
||||
PushContentReceiveJob receiveJob = new PushContentReceiveJob(MessageRetrievalService.this);
|
||||
receiveJob.handle(envelope, false);
|
||||
|
||||
decrementPushReceived();
|
||||
}
|
||||
});
|
||||
} catch (TimeoutException e) {
|
||||
Log.w(TAG, "Application level read timeout...");
|
||||
} catch (InvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.w(TAG, e);
|
||||
} finally {
|
||||
Log.w(TAG, "Shutting down pipe...");
|
||||
shutdown(pipe);
|
||||
}
|
||||
|
||||
Log.w(TAG, "Looping...");
|
||||
if (retrievalThread != null) {
|
||||
retrievalThread.stopThread();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +101,18 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setForegroundIfNecessary() {
|
||||
if (TextSecurePreferences.isGcmDisabled(this)) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setContentTitle(getString(R.string.MessageRetrievalService_signal));
|
||||
builder.setContentText(getString(R.string.MessageRetrievalService_background_connection_enabled));
|
||||
builder.setPriority(NotificationCompat.PRIORITY_MIN);
|
||||
builder.setWhen(0);
|
||||
builder.setSmallIcon(R.drawable.ic_signal_grey_24dp);
|
||||
startForeground(FOREGROUND_ID, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void incrementActive() {
|
||||
activeActivities++;
|
||||
Log.w(TAG, "Active Count: " + activeActivities);
|
||||
@@ -149,11 +139,13 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
}
|
||||
|
||||
private synchronized boolean isConnectionNecessary() {
|
||||
Log.w(TAG, String.format("Network requirement: %s, active activities: %s, push pending: %s",
|
||||
networkRequirement.isPresent(), activeActivities, pushPending.size()));
|
||||
boolean isGcmDisabled = TextSecurePreferences.isGcmDisabled(this);
|
||||
|
||||
return TextSecurePreferences.isWebsocketRegistered(this) &&
|
||||
(activeActivities > 0 || !pushPending.isEmpty()) &&
|
||||
Log.w(TAG, String.format("Network requirement: %s, active activities: %s, push pending: %s, gcm disabled: %b",
|
||||
networkRequirement.isPresent(), activeActivities, pushPending.size(), isGcmDisabled));
|
||||
|
||||
return TextSecurePreferences.isWebsocketRegistered(this) &&
|
||||
(activeActivities > 0 || !pushPending.isEmpty() || isGcmDisabled) &&
|
||||
networkRequirement.isPresent();
|
||||
}
|
||||
|
||||
@@ -188,4 +180,59 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
public static @Nullable SignalServiceMessagePipe getPipe() {
|
||||
return pipe;
|
||||
}
|
||||
|
||||
private class MessageRetrievalThread extends Thread {
|
||||
|
||||
private AtomicBoolean stopThread = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!stopThread.get()) {
|
||||
Log.w(TAG, "Waiting for websocket state change....");
|
||||
waitForConnectionNecessary();
|
||||
|
||||
Log.w(TAG, "Making websocket connection....");
|
||||
pipe = receiver.createMessagePipe();
|
||||
|
||||
SignalServiceMessagePipe localPipe = pipe;
|
||||
|
||||
try {
|
||||
while (isConnectionNecessary() && !stopThread.get()) {
|
||||
try {
|
||||
Log.w(TAG, "Reading message...");
|
||||
localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
|
||||
new SignalServiceMessagePipe.MessagePipeCallback() {
|
||||
@Override
|
||||
public void onMessage(SignalServiceEnvelope envelope) {
|
||||
Log.w(TAG, "Retrieved envelope! " + envelope.getSource());
|
||||
|
||||
PushContentReceiveJob receiveJob = new PushContentReceiveJob(MessageRetrievalService.this);
|
||||
receiveJob.handle(envelope, false);
|
||||
|
||||
decrementPushReceived();
|
||||
}
|
||||
});
|
||||
} catch (TimeoutException e) {
|
||||
Log.w(TAG, "Application level read timeout...");
|
||||
} catch (InvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.w(TAG, e);
|
||||
} finally {
|
||||
Log.w(TAG, "Shutting down pipe...");
|
||||
shutdown(localPipe);
|
||||
}
|
||||
|
||||
Log.w(TAG, "Looping...");
|
||||
}
|
||||
|
||||
Log.w(TAG, "Exiting...");
|
||||
}
|
||||
|
||||
public void stopThread() {
|
||||
stopThread.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
|
||||
public class PersistentConnectionBootListener extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (TextSecurePreferences.isGcmDisabled(context)) {
|
||||
Intent serviceIntent = new Intent(context, MessageRetrievalService.class);
|
||||
serviceIntent.setAction(MessageRetrievalService.ACTION_INITIALIZE);
|
||||
context.startService(serviceIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,12 @@ public class RegistrationService extends Service {
|
||||
public static final String CHALLENGE_EVENT = "org.thoughtcrime.securesms.CHALLENGE_EVENT";
|
||||
public static final String REGISTRATION_EVENT = "org.thoughtcrime.securesms.REGISTRATION_EVENT";
|
||||
|
||||
public static final String CHALLENGE_EXTRA = "CAAChallenge";
|
||||
public static final String NUMBER_EXTRA = "e164number";
|
||||
public static final String MASTER_SECRET_EXTRA = "master_secret";
|
||||
public static final String GCM_SUPPORTED_EXTRA = "gcm_supported";
|
||||
public static final String PASSWORD_EXTRA = "password";
|
||||
public static final String SIGNALING_KEY_EXTRA = "signaling_key";
|
||||
public static final String CHALLENGE_EXTRA = "CAAChallenge";
|
||||
|
||||
private static final long REGISTRATION_TIMEOUT_MILLIS = 120000;
|
||||
|
||||
@@ -149,21 +154,22 @@ public class RegistrationService extends Service {
|
||||
|
||||
private void handleVoiceRequestedIntent(Intent intent) {
|
||||
setState(new RegistrationState(RegistrationState.STATE_VOICE_REQUESTED,
|
||||
intent.getStringExtra("e164number"),
|
||||
intent.getStringExtra("password")));
|
||||
intent.getStringExtra(NUMBER_EXTRA),
|
||||
intent.getStringExtra(PASSWORD_EXTRA)));
|
||||
}
|
||||
|
||||
private void handleVoiceRegistrationIntent(Intent intent) {
|
||||
markAsVerifying(true);
|
||||
|
||||
String number = intent.getStringExtra("e164number");
|
||||
String password = intent.getStringExtra("password");
|
||||
String signalingKey = intent.getStringExtra("signaling_key");
|
||||
String number = intent.getStringExtra(NUMBER_EXTRA);
|
||||
String password = intent.getStringExtra(PASSWORD_EXTRA);
|
||||
String signalingKey = intent.getStringExtra(SIGNALING_KEY_EXTRA);
|
||||
boolean supportsGcm = intent.getBooleanExtra(GCM_SUPPORTED_EXTRA, true);
|
||||
|
||||
try {
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(this, number, password);
|
||||
|
||||
handleCommonRegistration(accountManager, number, password, signalingKey);
|
||||
handleCommonRegistration(accountManager, number, password, signalingKey, supportsGcm);
|
||||
|
||||
markAsVerified(number, password, signalingKey);
|
||||
|
||||
@@ -183,8 +189,10 @@ public class RegistrationService extends Service {
|
||||
private void handleSmsRegistrationIntent(Intent intent) {
|
||||
markAsVerifying(true);
|
||||
|
||||
String number = intent.getStringExtra("e164number");
|
||||
int registrationId = TextSecurePreferences.getLocalRegistrationId(this);
|
||||
String number = intent.getStringExtra(NUMBER_EXTRA);
|
||||
boolean supportsGcm = intent.getBooleanExtra(GCM_SUPPORTED_EXTRA, true);
|
||||
int registrationId = TextSecurePreferences.getLocalRegistrationId(this);
|
||||
boolean supportsVideo = TextSecurePreferences.isWebrtcCallingEnabled(this) || !supportsGcm;
|
||||
|
||||
if (registrationId == 0) {
|
||||
registrationId = KeyHelper.generateRegistrationId(false);
|
||||
@@ -203,9 +211,9 @@ public class RegistrationService extends Service {
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_VERIFYING, number));
|
||||
String challenge = waitForChallenge();
|
||||
accountManager.verifyAccountWithCode(challenge, signalingKey, registrationId, true, TextSecurePreferences.isWebrtcCallingEnabled(this));
|
||||
accountManager.verifyAccountWithCode(challenge, signalingKey, registrationId, true, supportsVideo, !supportsGcm);
|
||||
|
||||
handleCommonRegistration(accountManager, number, password, signalingKey);
|
||||
handleCommonRegistration(accountManager, number, password, signalingKey, supportsGcm);
|
||||
markAsVerified(number, password, signalingKey);
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_COMPLETE, number));
|
||||
@@ -231,7 +239,7 @@ public class RegistrationService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCommonRegistration(SignalServiceAccountManager accountManager, String number, String password, String signalingKey)
|
||||
private void handleCommonRegistration(SignalServiceAccountManager accountManager, String number, String password, String signalingKey, boolean supportsGcm)
|
||||
throws IOException
|
||||
{
|
||||
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
|
||||
@@ -244,21 +252,29 @@ public class RegistrationService extends Service {
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
|
||||
|
||||
String gcmRegistrationId = GoogleCloudMessaging.getInstance(this).register(GcmRefreshJob.REGISTRATION_ID);
|
||||
accountManager.setGcmId(Optional.of(gcmRegistrationId));
|
||||
if (supportsGcm) {
|
||||
String gcmRegistrationId = GoogleCloudMessaging.getInstance(this).register(GcmRefreshJob.REGISTRATION_ID);
|
||||
accountManager.setGcmId(Optional.of(gcmRegistrationId));
|
||||
|
||||
TextSecurePreferences.setGcmRegistrationId(this, gcmRegistrationId);
|
||||
TextSecurePreferences.setGcmDisabled(this, false);
|
||||
} else {
|
||||
TextSecurePreferences.setGcmDisabled(this, true);
|
||||
}
|
||||
|
||||
TextSecurePreferences.setGcmRegistrationId(this, gcmRegistrationId);
|
||||
TextSecurePreferences.setWebsocketRegistered(this, true);
|
||||
|
||||
DatabaseFactory.getIdentityDatabase(this).saveIdentity(self.getRecipientId(), identityKey.getPublicKey());
|
||||
DirectoryHelper.refreshDirectory(this, accountManager, number);
|
||||
|
||||
RedPhoneAccountManager redPhoneAccountManager = new RedPhoneAccountManager(BuildConfig.REDPHONE_MASTER_URL,
|
||||
new RedPhoneTrustStore(this),
|
||||
number, password);
|
||||
if (supportsGcm) {
|
||||
RedPhoneAccountManager redPhoneAccountManager = new RedPhoneAccountManager(BuildConfig.REDPHONE_MASTER_URL,
|
||||
new RedPhoneTrustStore(this),
|
||||
number, password);
|
||||
|
||||
String verificationToken = accountManager.getAccountVerificationToken();
|
||||
redPhoneAccountManager.createAccount(verificationToken, new RedPhoneAccountAttributes(signalingKey, gcmRegistrationId));
|
||||
String verificationToken = accountManager.getAccountVerificationToken();
|
||||
redPhoneAccountManager.createAccount(verificationToken, new RedPhoneAccountAttributes(signalingKey, TextSecurePreferences.getGcmRegistrationId(this)));
|
||||
}
|
||||
|
||||
DirectoryRefreshListener.schedule(this);
|
||||
RotateSignedPreKeyListener.schedule(this);
|
||||
|
||||
Reference in New Issue
Block a user