diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 15cab4f9f9..0297bbd1e0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -430,7 +430,7 @@
-
+
{
- KeyCachingService.registerPassphraseActivityStarted(this);
-
- if (!networkAccess.isCensored(this)) MessageRetrievalService.registerActivityStarted(this);
- else ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this));
- });
+ if (networkAccess.isCensored(this)) {
+ ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this));
+ }
}
@Override
protected void onPause() {
Log.i(TAG, "onPause()");
super.onPause();
- isVisible = false;
-
- // Android P has a bug in foreground timings where starting a service in onPause() can still crash
- Util.postToMain(() -> {
- KeyCachingService.registerPassphraseActivityStopped(this);
- if (!networkAccess.isCensored(this)) MessageRetrievalService.registerActivityStopped(this);
- });
}
@Override
@@ -95,8 +81,8 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
@Override
public void onMasterSecretCleared() {
Log.i(TAG, "onMasterSecretCleared()");
- if (isVisible) routeApplicationState(true);
- else finish();
+ if (ApplicationContext.getInstance(this).isAppVisible()) routeApplicationState(true);
+ else finish();
}
protected T initFragment(@IdRes int target,
diff --git a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java
index 9ce0d8547c..ef44371e1f 100644
--- a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java
+++ b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java
@@ -44,7 +44,6 @@ import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.recipients.Recipient;
-import org.thoughtcrime.securesms.service.MessageRetrievalService;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -67,7 +66,6 @@ public class WebRtcCallActivity extends Activity {
public static final String END_CALL_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".END_CALL_ACTION";
private WebRtcCallScreen callScreen;
- private SignalServiceNetworkAccess networkAccess;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -89,12 +87,6 @@ public class WebRtcCallActivity extends Activity {
public void onResume() {
Log.i(TAG, "onResume()");
super.onResume();
-
- // Android P has a bug in foreground timings where starting a service in onResume() can still crash
- Util.postToMain(() -> {
- if (!networkAccess.isCensored(this)) MessageRetrievalService.registerActivityStarted(this);
- });
-
initializeScreenshotSecurity();
EventBus.getDefault().register(this);
}
@@ -116,11 +108,6 @@ public class WebRtcCallActivity extends Activity {
Log.i(TAG, "onPause");
super.onPause();
EventBus.getDefault().unregister(this);
-
- // Android P has a bug in foreground timings where starting a service in onPause() can still crash
- Util.postToMain(() -> {
- if (!networkAccess.isCensored(this)) MessageRetrievalService.registerActivityStopped(this);
- });
}
@Override
@@ -152,8 +139,6 @@ public class WebRtcCallActivity extends Activity {
callScreen.setCameraFlipButtonListener(new CameraFlipButtonListener());
callScreen.setSpeakerButtonListener(new SpeakerButtonListener());
callScreen.setBluetoothButtonListener(new BluetoothButtonListener());
-
- networkAccess = new SignalServiceNetworkAccess(this);
}
private void handleSetMuteAudio(boolean enabled) {
diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
index d6dcbac99e..7045604091 100644
--- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
+++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
@@ -38,7 +38,7 @@ import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.push.SecurityEventListener;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
-import org.thoughtcrime.securesms.service.MessageRetrievalService;
+import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
@@ -61,7 +61,7 @@ import dagger.Provides;
PushMediaSendJob.class,
AttachmentDownloadJob.class,
RefreshPreKeysJob.class,
- MessageRetrievalService.class,
+ IncomingMessageObserver.class,
PushNotificationReceiveJob.class,
MultiDeviceContactUpdateJob.class,
MultiDeviceGroupUpdateJob.class,
@@ -118,10 +118,10 @@ public class SignalCommunicationModule {
new DynamicCredentialsProvider(context),
new SignalProtocolStoreImpl(context),
BuildConfig.USER_AGENT,
- Optional.fromNullable(MessageRetrievalService.getPipe()),
+ Optional.fromNullable(IncomingMessageObserver.getPipe()),
Optional.of(new SecurityEventListener(context)));
} else {
- this.messageSender.setMessagePipe(MessageRetrievalService.getPipe());
+ this.messageSender.setMessagePipe(IncomingMessageObserver.getPipe());
}
return this.messageSender;
@@ -142,6 +142,11 @@ public class SignalCommunicationModule {
return this.messageReceiver;
}
+ @Provides
+ synchronized SignalServiceNetworkAccess provideSignalServiceNetworkAccess() {
+ return networkAccess;
+ }
+
private static class DynamicCredentialsProvider implements CredentialsProvider {
private final Context context;
diff --git a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java
index baf554299a..37a878986e 100644
--- a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java
@@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.recipients.Recipient;
-import org.thoughtcrime.securesms.service.MessageRetrievalService;
+import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.Util;
@@ -107,7 +107,7 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
}
private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException {
- SignalServiceMessagePipe pipe = MessageRetrievalService.getPipe();
+ SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
if (pipe != null) {
try {
diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
index 7316e011a9..e6cc3f0b13 100644
--- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
+++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
@@ -51,8 +51,8 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.service.KeyCachingService;
-import org.thoughtcrime.securesms.service.MessageRetrievalService;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -164,7 +164,7 @@ public class MessageNotifier {
if (notification.getId() != SUMMARY_NOTIFICATION_ID &&
notification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION &&
notification.getId() != KeyCachingService.SERVICE_RUNNING_ID &&
- notification.getId() != MessageRetrievalService.FOREGROUND_ID &&
+ notification.getId() != IncomingMessageObserver.FOREGROUND_ID &&
notification.getId() != PENDING_MESSAGES_ID)
{
for (NotificationItem item : notificationState.getNotifications()) {
diff --git a/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java b/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java
new file mode 100644
index 0000000000..1e1147904f
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java
@@ -0,0 +1,202 @@
+package org.thoughtcrime.securesms.service;
+
+import android.app.Service;
+import android.arch.lifecycle.DefaultLifecycleObserver;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.ProcessLifecycleOwner;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.content.ContextCompat;
+
+import org.thoughtcrime.securesms.logging.Log;
+
+import org.thoughtcrime.securesms.ApplicationContext;
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.dependencies.InjectableType;
+import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
+import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirementProvider;
+import org.thoughtcrime.securesms.jobmanager.requirements.RequirementListener;
+import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
+import org.thoughtcrime.securesms.notifications.NotificationChannels;
+import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+import org.whispersystems.libsignal.InvalidVersionException;
+import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
+import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.inject.Inject;
+
+public class IncomingMessageObserver implements InjectableType, RequirementListener {
+
+ private static final String TAG = IncomingMessageObserver.class.getSimpleName();
+
+ public static final int FOREGROUND_ID = 313399;
+ private static final long REQUEST_TIMEOUT_MINUTES = 1;
+
+ private static SignalServiceMessagePipe pipe = null;
+
+ private final Context context;
+ private final NetworkRequirement networkRequirement;
+
+ private boolean appVisible;
+
+ @Inject SignalServiceMessageReceiver receiver;
+ @Inject SignalServiceNetworkAccess networkAccess;
+
+ public IncomingMessageObserver(@NonNull Context context) {
+ ApplicationContext.getInstance(context).injectDependencies(this);
+
+ this.context = context;
+ this.networkRequirement = new NetworkRequirement(context);
+
+ new NetworkRequirementProvider(context).setListener(this);
+ new MessageRetrievalThread().start();
+
+ if (TextSecurePreferences.isGcmDisabled(context)) {
+ ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class));
+ }
+
+ ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() {
+ @Override
+ public void onStart(@NonNull LifecycleOwner owner) {
+ onAppForegrounded();
+ }
+
+ @Override
+ public void onStop(@NonNull LifecycleOwner owner) {
+ onAppBackgrounded();
+ }
+ });
+ }
+
+ @Override
+ public void onRequirementStatusChanged() {
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+
+ private synchronized void onAppForegrounded() {
+ appVisible = true;
+ notifyAll();
+ }
+
+ private synchronized void onAppBackgrounded() {
+ appVisible = false;
+ notifyAll();
+ }
+
+ private synchronized boolean isConnectionNecessary() {
+ boolean isGcmDisabled = TextSecurePreferences.isGcmDisabled(context);
+
+ Log.d(TAG, String.format("Network requirement: %s, app visible: %s, gcm disabled: %b",
+ networkRequirement.isPresent(), appVisible, isGcmDisabled));
+
+ return TextSecurePreferences.isPushRegistered(context) &&
+ TextSecurePreferences.isWebsocketRegistered(context) &&
+ (appVisible || isGcmDisabled) &&
+ networkRequirement.isPresent() &&
+ !networkAccess.isCensored(context);
+ }
+
+ private synchronized void waitForConnectionNecessary() {
+ try {
+ while (!isConnectionNecessary()) wait();
+ } catch (InterruptedException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private void shutdown(SignalServiceMessagePipe pipe) {
+ try {
+ pipe.shutdown();
+ } catch (Throwable t) {
+ Log.w(TAG, t);
+ }
+ }
+
+ public static @Nullable SignalServiceMessagePipe getPipe() {
+ return pipe;
+ }
+
+ private class MessageRetrievalThread extends Thread implements Thread.UncaughtExceptionHandler {
+
+ MessageRetrievalThread() {
+ super("MessageRetrievalService");
+ setUncaughtExceptionHandler(this);
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ Log.i(TAG, "Waiting for websocket state change....");
+ waitForConnectionNecessary();
+
+ Log.i(TAG, "Making websocket connection....");
+ pipe = receiver.createMessagePipe();
+
+ SignalServiceMessagePipe localPipe = pipe;
+
+ try {
+ while (isConnectionNecessary()) {
+ try {
+ Log.i(TAG, "Reading message...");
+ localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
+ envelope -> {
+ Log.i(TAG, "Retrieved envelope! " + envelope.getSource());
+ new PushContentReceiveJob(context).processEnvelope(envelope);
+ });
+ } 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.i(TAG, "Looping...");
+ }
+ }
+
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ Log.w(TAG, "*** Uncaught exception!");
+ Log.w(TAG, e);
+ }
+ }
+
+ public static class ForegroundService extends Service {
+
+ @Override
+ public @Nullable IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ super.onStartCommand(intent, flags, startId);
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), NotificationChannels.OTHER);
+ builder.setContentTitle(getApplicationContext().getString(R.string.MessageRetrievalService_signal));
+ builder.setContentText(getApplicationContext().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());
+
+ return Service.START_STICKY;
+ }
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/service/KeyCachingService.java b/src/org/thoughtcrime/securesms/service/KeyCachingService.java
index c4134e029a..152cbe7f3e 100644
--- a/src/org/thoughtcrime/securesms/service/KeyCachingService.java
+++ b/src/org/thoughtcrime/securesms/service/KeyCachingService.java
@@ -29,6 +29,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.SystemClock;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import org.thoughtcrime.securesms.logging.Log;
@@ -68,14 +69,10 @@ public class KeyCachingService extends Service {
private static final String PASSPHRASE_EXPIRED_EVENT = "org.thoughtcrime.securesms.service.action.PASSPHRASE_EXPIRED_EVENT";
public static final String CLEAR_KEY_ACTION = "org.thoughtcrime.securesms.service.action.CLEAR_KEY";
public static final String DISABLE_ACTION = "org.thoughtcrime.securesms.service.action.DISABLE";
- public static final String ACTIVITY_START_EVENT = "org.thoughtcrime.securesms.service.action.ACTIVITY_START_EVENT";
- public static final String ACTIVITY_STOP_EVENT = "org.thoughtcrime.securesms.service.action.ACTIVITY_STOP_EVENT";
public static final String LOCALE_CHANGE_EVENT = "org.thoughtcrime.securesms.service.action.LOCALE_CHANGE_EVENT";
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
- private PendingIntent pending;
- private int activitiesRunning = 0;
private final IBinder binder = new KeySetBinder();
private static MasterSecret masterSecret;
@@ -98,6 +95,14 @@ public class KeyCachingService extends Service {
return masterSecret;
}
+ public static void onAppForegrounded(@NonNull Context context) {
+ ServiceUtil.getAlarmManager(context).cancel(buildExpirationPendingIntent(context));
+ }
+
+ public static void onAppBackgrounded(@NonNull Context context) {
+ startTimeoutIfAppropriate(context);
+ }
+
@SuppressLint("StaticFieldLeak")
public void setMasterSecret(final MasterSecret masterSecret) {
synchronized (KeyCachingService.class) {
@@ -105,7 +110,7 @@ public class KeyCachingService extends Service {
foregroundService();
broadcastNewSecret();
- startTimeoutIfAppropriate();
+ startTimeoutIfAppropriate(this);
new AsyncTask() {
@Override
@@ -127,8 +132,6 @@ public class KeyCachingService extends Service {
if (intent.getAction() != null) {
switch (intent.getAction()) {
case CLEAR_KEY_ACTION: handleClearKey(); break;
- case ACTIVITY_START_EVENT: handleActivityStarted(); break;
- case ACTIVITY_STOP_EVENT: handleActivityStopped(); break;
case PASSPHRASE_EXPIRED_EVENT: handleClearKey(); break;
case DISABLE_ACTION: handleDisableService(); break;
case LOCALE_CHANGE_EVENT: handleLocaleChanged(); break;
@@ -143,8 +146,6 @@ public class KeyCachingService extends Service {
public void onCreate() {
Log.i(TAG, "onCreate()");
super.onCreate();
- this.pending = PendingIntent.getService(this, 0, new Intent(PASSPHRASE_EXPIRED_EVENT, null,
- this, KeyCachingService.class), 0);
if (TextSecurePreferences.isPasswordDisabled(this) && !TextSecurePreferences.isScreenLockEnabled(this)) {
try {
@@ -174,21 +175,6 @@ public class KeyCachingService extends Service {
startActivity(intent);
}
- private void handleActivityStarted() {
- Log.d(TAG, "Incrementing activity count...");
-
- AlarmManager alarmManager = ServiceUtil.getAlarmManager(this);
- alarmManager.cancel(pending);
- activitiesRunning++;
- }
-
- private void handleActivityStopped() {
- Log.d(TAG, "Decrementing activity count...");
-
- activitiesRunning--;
- startTimeoutIfAppropriate();
- }
-
@SuppressLint("StaticFieldLeak")
private void handleClearKey() {
Log.i(TAG, "handleClearKey()");
@@ -233,27 +219,29 @@ public class KeyCachingService extends Service {
foregroundService();
}
- private void startTimeoutIfAppropriate() {
- boolean timeoutEnabled = TextSecurePreferences.isPassphraseTimeoutEnabled(this);
- long screenTimeout = TextSecurePreferences.getScreenLockTimeout(this);
+ private static void startTimeoutIfAppropriate(@NonNull Context context) {
+ boolean timeoutEnabled = TextSecurePreferences.isPassphraseTimeoutEnabled(context);
+ long screenTimeout = TextSecurePreferences.getScreenLockTimeout(context);
- if ((activitiesRunning == 0) && (KeyCachingService.masterSecret != null) &&
- (timeoutEnabled && !TextSecurePreferences.isPasswordDisabled(this)) ||
- (screenTimeout >= 60 && TextSecurePreferences.isScreenLockEnabled(this)))
+ if ((KeyCachingService.masterSecret != null) &&
+ (timeoutEnabled && !TextSecurePreferences.isPasswordDisabled(context)) ||
+ (screenTimeout >= 60 && TextSecurePreferences.isScreenLockEnabled(context)))
{
- long passphraseTimeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(this);
- long screenLockTimeoutSeconds = TextSecurePreferences.getScreenLockTimeout(this);
+ long passphraseTimeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(context);
+ long screenLockTimeoutSeconds = TextSecurePreferences.getScreenLockTimeout(context);
long timeoutMillis;
- if (!TextSecurePreferences.isPasswordDisabled(this)) timeoutMillis = TimeUnit.MINUTES.toMillis(passphraseTimeoutMinutes);
- else timeoutMillis = TimeUnit.SECONDS.toMillis(screenLockTimeoutSeconds);
+ if (!TextSecurePreferences.isPasswordDisabled(context)) timeoutMillis = TimeUnit.MINUTES.toMillis(passphraseTimeoutMinutes);
+ else timeoutMillis = TimeUnit.SECONDS.toMillis(screenLockTimeoutSeconds);
Log.i(TAG, "Starting timeout: " + timeoutMillis);
- AlarmManager alarmManager = ServiceUtil.getAlarmManager(this);
- alarmManager.cancel(pending);
- alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + timeoutMillis, pending);
+ AlarmManager alarmManager = ServiceUtil.getAlarmManager(context);
+ PendingIntent expirationIntent = buildExpirationPendingIntent(context);
+
+ alarmManager.cancel(expirationIntent);
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + timeoutMillis, expirationIntent);
}
}
@@ -338,6 +326,11 @@ public class KeyCachingService extends Service {
return PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
}
+ private static PendingIntent buildExpirationPendingIntent(@NonNull Context context) {
+ Intent expirationIntent = new Intent(PASSPHRASE_EXPIRED_EVENT, null, context, KeyCachingService.class);
+ return PendingIntent.getService(context, 0, expirationIntent, 0);
+ }
+
@Override
public IBinder onBind(Intent arg0) {
return binder;
@@ -348,16 +341,4 @@ public class KeyCachingService extends Service {
return KeyCachingService.this;
}
}
-
- public static void registerPassphraseActivityStarted(Context activity) {
- Intent intent = new Intent(activity, KeyCachingService.class);
- intent.setAction(KeyCachingService.ACTIVITY_START_EVENT);
- activity.startService(intent);
- }
-
- public static void registerPassphraseActivityStopped(Context activity) {
- Intent intent = new Intent(activity, KeyCachingService.class);
- intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT);
- activity.startService(intent);
- }
}
diff --git a/src/org/thoughtcrime/securesms/service/MessageRetrievalService.java b/src/org/thoughtcrime/securesms/service/MessageRetrievalService.java
deleted file mode 100644
index 4b5735deee..0000000000
--- a/src/org/thoughtcrime/securesms/service/MessageRetrievalService.java
+++ /dev/null
@@ -1,247 +0,0 @@
-package org.thoughtcrime.securesms.service;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.support.annotation.Nullable;
-import android.support.v4.app.NotificationCompat;
-
-import org.thoughtcrime.securesms.logging.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.jobmanager.requirements.NetworkRequirement;
-import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirementProvider;
-import org.thoughtcrime.securesms.jobmanager.requirements.RequirementListener;
-import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
-import org.thoughtcrime.securesms.notifications.NotificationChannels;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.whispersystems.libsignal.InvalidVersionException;
-import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
-import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
-
-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 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;
- private NetworkRequirementProvider networkRequirementProvider;
-
- @Inject
- public SignalServiceMessageReceiver receiver;
-
- private int activeActivities = 0;
- private List pushPending = new LinkedList<>();
- private MessageRetrievalThread retrievalThread = null;
-
- public static SignalServiceMessagePipe pipe = null;
-
- @Override
- public void onCreate() {
- super.onCreate();
- ApplicationContext.getInstance(this).injectDependencies(this);
-
- networkRequirement = new NetworkRequirement(this);
- networkRequirementProvider = new NetworkRequirementProvider(this);
-
- networkRequirementProvider.setListener(this);
-
- retrievalThread = new MessageRetrievalThread();
- retrievalThread.start();
-
- setForegroundIfNecessary();
- }
-
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent == null) return START_STICKY;
-
- if (ACTION_ACTIVITY_STARTED.equals(intent.getAction())) incrementActive();
- else if (ACTION_ACTIVITY_FINISHED.equals(intent.getAction())) decrementActive();
- else if (ACTION_PUSH_RECEIVED.equals(intent.getAction())) incrementPushReceived(intent);
-
- return START_STICKY;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- if (retrievalThread != null) {
- retrievalThread.stopThread();
- }
-
- sendBroadcast(new Intent("org.thoughtcrime.securesms.RESTART"));
- }
-
- @Override
- public void onRequirementStatusChanged() {
- synchronized (this) {
- notifyAll();
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- private void setForegroundIfNecessary() {
- if (TextSecurePreferences.isGcmDisabled(this)) {
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NotificationChannels.OTHER);
- 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.d(TAG, "Active Count: " + activeActivities);
- notifyAll();
- }
-
- private synchronized void decrementActive() {
- activeActivities--;
- Log.d(TAG, "Active Count: " + activeActivities);
- notifyAll();
- }
-
- private synchronized void incrementPushReceived(Intent intent) {
- pushPending.add(intent);
- notifyAll();
- }
-
- private synchronized void decrementPushReceived() {
- if (!pushPending.isEmpty()) {
- Intent intent = pushPending.remove(0);
- GcmBroadcastReceiver.completeWakefulIntent(intent);
- notifyAll();
- }
- }
-
- private synchronized boolean isConnectionNecessary() {
- boolean isGcmDisabled = TextSecurePreferences.isGcmDisabled(this);
-
- Log.d(TAG, String.format("Network requirement: %s, active activities: %s, push pending: %s, gcm disabled: %b",
- networkRequirement.isPresent(), activeActivities, pushPending.size(), isGcmDisabled));
-
- return TextSecurePreferences.isPushRegistered(this) &&
- TextSecurePreferences.isWebsocketRegistered(this) &&
- (activeActivities > 0 || !pushPending.isEmpty() || isGcmDisabled) &&
- networkRequirement.isPresent();
- }
-
- private synchronized void waitForConnectionNecessary() {
- try {
- while (!isConnectionNecessary()) wait();
- } catch (InterruptedException e) {
- throw new AssertionError(e);
- }
- }
-
- private void shutdown(SignalServiceMessagePipe pipe) {
- try {
- pipe.shutdown();
- } catch (Throwable t) {
- Log.w(TAG, t);
- }
- }
-
- public static void registerActivityStarted(Context activity) {
- Intent intent = new Intent(activity, MessageRetrievalService.class);
- intent.setAction(MessageRetrievalService.ACTION_ACTIVITY_STARTED);
- activity.startService(intent);
- }
-
- public static void registerActivityStopped(Context activity) {
- Intent intent = new Intent(activity, MessageRetrievalService.class);
- intent.setAction(MessageRetrievalService.ACTION_ACTIVITY_FINISHED);
- activity.startService(intent);
- }
-
- public static @Nullable SignalServiceMessagePipe getPipe() {
- return pipe;
- }
-
- private class MessageRetrievalThread extends Thread implements Thread.UncaughtExceptionHandler {
-
- private AtomicBoolean stopThread = new AtomicBoolean(false);
-
- MessageRetrievalThread() {
- super("MessageRetrievalService");
- setUncaughtExceptionHandler(this);
- }
-
- @Override
- public void run() {
- while (!stopThread.get()) {
- Log.i(TAG, "Waiting for websocket state change....");
- waitForConnectionNecessary();
-
- Log.i(TAG, "Making websocket connection....");
- pipe = receiver.createMessagePipe();
-
- SignalServiceMessagePipe localPipe = pipe;
-
- try {
- while (isConnectionNecessary() && !stopThread.get()) {
- try {
- Log.i(TAG, "Reading message...");
- localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
- envelope -> {
- Log.i(TAG, "Retrieved envelope! " + envelope.getSource());
- new PushContentReceiveJob(getApplicationContext()).processEnvelope(envelope);
- 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.i(TAG, "Looping...");
- }
-
- Log.i(TAG, "Exiting...");
- }
-
- private void stopThread() {
- stopThread.set(true);
- }
-
- @Override
- public void uncaughtException(Thread t, Throwable e) {
- Log.w(TAG, "*** Uncaught exception!");
- Log.w(TAG, e);
- }
- }
-}
diff --git a/src/org/thoughtcrime/securesms/service/PersistentConnectionBootListener.java b/src/org/thoughtcrime/securesms/service/PersistentConnectionBootListener.java
index 4040696ec6..2a2a4a71c1 100644
--- a/src/org/thoughtcrime/securesms/service/PersistentConnectionBootListener.java
+++ b/src/org/thoughtcrime/securesms/service/PersistentConnectionBootListener.java
@@ -4,21 +4,19 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
+import android.util.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class PersistentConnectionBootListener extends BroadcastReceiver {
+
+ private static final String TAG = PersistentConnectionBootListener.class.getSimpleName();
+
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
- if (TextSecurePreferences.isGcmDisabled(context)) {
- Intent serviceIntent = new Intent(context, MessageRetrievalService.class);
- serviceIntent.setAction(MessageRetrievalService.ACTION_INITIALIZE);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(serviceIntent);
- else context.startService(serviceIntent);
- }
+ Log.i(TAG, "Received boot event. Application should be started, allowing non-GCM devices to start a foreground service.");
}
}
}