mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-09 21:18:33 +00:00
Turn MessageRetrievalService into IncomingMessageObserver.
Due to an Android P bug, we basically need to stop calling startService() in onResume()/onPause(). That means I had to turn MessageRetrieval service into a singlton instead of a service. I also moved the offending KeyCachingService calls into static methods that didn't have to start the service.
This commit is contained in:
parent
7a6d863ff7
commit
45e0bb281f
@ -430,7 +430,7 @@
|
|||||||
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.WebRtcCallService"/>
|
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.WebRtcCallService"/>
|
||||||
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
|
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
|
||||||
<service android:enabled="true" android:exported="false" android:name=".service.KeyCachingService"/>
|
<service android:enabled="true" android:exported="false" android:name=".service.KeyCachingService"/>
|
||||||
<service android:enabled="true" android:name=".service.MessageRetrievalService"/>
|
<service android:enabled="true" android:name=".service.IncomingMessageObserver$ForegroundService"/>
|
||||||
|
|
||||||
<service android:name=".service.QuickResponseService"
|
<service android:name=".service.QuickResponseService"
|
||||||
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
|
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
|
||||||
|
@ -47,6 +47,8 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
|||||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||||
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
|
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
|
||||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||||
|
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
||||||
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.service.LocalBackupListener;
|
import org.thoughtcrime.securesms.service.LocalBackupListener;
|
||||||
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
||||||
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
||||||
@ -77,10 +79,11 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
|
|
||||||
private static final String TAG = ApplicationContext.class.getSimpleName();
|
private static final String TAG = ApplicationContext.class.getSimpleName();
|
||||||
|
|
||||||
private ExpiringMessageManager expiringMessageManager;
|
private ExpiringMessageManager expiringMessageManager;
|
||||||
private JobManager jobManager;
|
private JobManager jobManager;
|
||||||
private ObjectGraph objectGraph;
|
private IncomingMessageObserver incomingMessageObserver;
|
||||||
private PersistentLogger persistentLogger;
|
private ObjectGraph objectGraph;
|
||||||
|
private PersistentLogger persistentLogger;
|
||||||
|
|
||||||
private volatile boolean isAppVisible;
|
private volatile boolean isAppVisible;
|
||||||
|
|
||||||
@ -97,6 +100,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
initializeCrashHandling();
|
initializeCrashHandling();
|
||||||
initializeDependencyInjection();
|
initializeDependencyInjection();
|
||||||
initializeJobManager();
|
initializeJobManager();
|
||||||
|
initializeMessageRetrieval();
|
||||||
initializeExpiringMessageManager();
|
initializeExpiringMessageManager();
|
||||||
initializeGcmCheck();
|
initializeGcmCheck();
|
||||||
initializeSignedPreKeyCheck();
|
initializeSignedPreKeyCheck();
|
||||||
@ -113,12 +117,14 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
isAppVisible = true;
|
isAppVisible = true;
|
||||||
Log.i(TAG, "App is now visible.");
|
Log.i(TAG, "App is now visible.");
|
||||||
executePendingContactSync();
|
executePendingContactSync();
|
||||||
|
KeyCachingService.onAppForegrounded(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop(@NonNull LifecycleOwner owner) {
|
public void onStop(@NonNull LifecycleOwner owner) {
|
||||||
isAppVisible = false;
|
isAppVisible = false;
|
||||||
Log.i(TAG, "App is no longer visible.");
|
Log.i(TAG, "App is no longer visible.");
|
||||||
|
KeyCachingService.onAppBackgrounded(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -168,6 +174,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
this.jobManager = new JobManager(WorkManager.getInstance());
|
this.jobManager = new JobManager(WorkManager.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void initializeMessageRetrieval() {
|
||||||
|
this.incomingMessageObserver = new IncomingMessageObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeDependencyInjection() {
|
private void initializeDependencyInjection() {
|
||||||
this.objectGraph = ObjectGraph.create(new SignalCommunicationModule(this, new SignalServiceNetworkAccess(this)),
|
this.objectGraph = ObjectGraph.create(new SignalCommunicationModule(this, new SignalServiceNetworkAccess(this)),
|
||||||
new AxolotlStorageModule(this));
|
new AxolotlStorageModule(this));
|
||||||
|
@ -15,7 +15,6 @@ import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
|||||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.service.MessageRetrievalService;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
@ -35,7 +34,6 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
|||||||
|
|
||||||
private SignalServiceNetworkAccess networkAccess;
|
private SignalServiceNetworkAccess networkAccess;
|
||||||
private BroadcastReceiver clearKeyReceiver;
|
private BroadcastReceiver clearKeyReceiver;
|
||||||
private boolean isVisible;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final void onCreate(Bundle savedInstanceState) {
|
protected final void onCreate(Bundle savedInstanceState) {
|
||||||
@ -61,28 +59,16 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
|||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
Log.i(TAG, "onResume()");
|
Log.i(TAG, "onResume()");
|
||||||
super.onResume();
|
super.onResume();
|
||||||
isVisible = true;
|
|
||||||
|
|
||||||
// Android P has a bug in foreground timings where starting a service in onResume() can still crash
|
if (networkAccess.isCensored(this)) {
|
||||||
Util.postToMain(() -> {
|
ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this));
|
||||||
KeyCachingService.registerPassphraseActivityStarted(this);
|
}
|
||||||
|
|
||||||
if (!networkAccess.isCensored(this)) MessageRetrievalService.registerActivityStarted(this);
|
|
||||||
else ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
Log.i(TAG, "onPause()");
|
Log.i(TAG, "onPause()");
|
||||||
super.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
|
@Override
|
||||||
@ -95,8 +81,8 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
|||||||
@Override
|
@Override
|
||||||
public void onMasterSecretCleared() {
|
public void onMasterSecretCleared() {
|
||||||
Log.i(TAG, "onMasterSecretCleared()");
|
Log.i(TAG, "onMasterSecretCleared()");
|
||||||
if (isVisible) routeApplicationState(true);
|
if (ApplicationContext.getInstance(this).isAppVisible()) routeApplicationState(true);
|
||||||
else finish();
|
else finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T extends Fragment> T initFragment(@IdRes int target,
|
protected <T extends Fragment> T initFragment(@IdRes int target,
|
||||||
|
@ -44,7 +44,6 @@ import org.thoughtcrime.securesms.events.WebRtcViewModel;
|
|||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.service.MessageRetrievalService;
|
|
||||||
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
||||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
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";
|
public static final String END_CALL_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".END_CALL_ACTION";
|
||||||
|
|
||||||
private WebRtcCallScreen callScreen;
|
private WebRtcCallScreen callScreen;
|
||||||
private SignalServiceNetworkAccess networkAccess;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@ -89,12 +87,6 @@ public class WebRtcCallActivity extends Activity {
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
Log.i(TAG, "onResume()");
|
Log.i(TAG, "onResume()");
|
||||||
super.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();
|
initializeScreenshotSecurity();
|
||||||
EventBus.getDefault().register(this);
|
EventBus.getDefault().register(this);
|
||||||
}
|
}
|
||||||
@ -116,11 +108,6 @@ public class WebRtcCallActivity extends Activity {
|
|||||||
Log.i(TAG, "onPause");
|
Log.i(TAG, "onPause");
|
||||||
super.onPause();
|
super.onPause();
|
||||||
EventBus.getDefault().unregister(this);
|
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
|
@Override
|
||||||
@ -152,8 +139,6 @@ public class WebRtcCallActivity extends Activity {
|
|||||||
callScreen.setCameraFlipButtonListener(new CameraFlipButtonListener());
|
callScreen.setCameraFlipButtonListener(new CameraFlipButtonListener());
|
||||||
callScreen.setSpeakerButtonListener(new SpeakerButtonListener());
|
callScreen.setSpeakerButtonListener(new SpeakerButtonListener());
|
||||||
callScreen.setBluetoothButtonListener(new BluetoothButtonListener());
|
callScreen.setBluetoothButtonListener(new BluetoothButtonListener());
|
||||||
|
|
||||||
networkAccess = new SignalServiceNetworkAccess(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSetMuteAudio(boolean enabled) {
|
private void handleSetMuteAudio(boolean enabled) {
|
||||||
|
@ -38,7 +38,7 @@ import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
|||||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||||
import org.thoughtcrime.securesms.push.SecurityEventListener;
|
import org.thoughtcrime.securesms.push.SecurityEventListener;
|
||||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
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.service.WebRtcCallService;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
@ -61,7 +61,7 @@ import dagger.Provides;
|
|||||||
PushMediaSendJob.class,
|
PushMediaSendJob.class,
|
||||||
AttachmentDownloadJob.class,
|
AttachmentDownloadJob.class,
|
||||||
RefreshPreKeysJob.class,
|
RefreshPreKeysJob.class,
|
||||||
MessageRetrievalService.class,
|
IncomingMessageObserver.class,
|
||||||
PushNotificationReceiveJob.class,
|
PushNotificationReceiveJob.class,
|
||||||
MultiDeviceContactUpdateJob.class,
|
MultiDeviceContactUpdateJob.class,
|
||||||
MultiDeviceGroupUpdateJob.class,
|
MultiDeviceGroupUpdateJob.class,
|
||||||
@ -118,10 +118,10 @@ public class SignalCommunicationModule {
|
|||||||
new DynamicCredentialsProvider(context),
|
new DynamicCredentialsProvider(context),
|
||||||
new SignalProtocolStoreImpl(context),
|
new SignalProtocolStoreImpl(context),
|
||||||
BuildConfig.USER_AGENT,
|
BuildConfig.USER_AGENT,
|
||||||
Optional.fromNullable(MessageRetrievalService.getPipe()),
|
Optional.fromNullable(IncomingMessageObserver.getPipe()),
|
||||||
Optional.of(new SecurityEventListener(context)));
|
Optional.of(new SecurityEventListener(context)));
|
||||||
} else {
|
} else {
|
||||||
this.messageSender.setMessagePipe(MessageRetrievalService.getPipe());
|
this.messageSender.setMessagePipe(IncomingMessageObserver.getPipe());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.messageSender;
|
return this.messageSender;
|
||||||
@ -142,6 +142,11 @@ public class SignalCommunicationModule {
|
|||||||
return this.messageReceiver;
|
return this.messageReceiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
synchronized SignalServiceNetworkAccess provideSignalServiceNetworkAccess() {
|
||||||
|
return networkAccess;
|
||||||
|
}
|
||||||
|
|
||||||
private static class DynamicCredentialsProvider implements CredentialsProvider {
|
private static class DynamicCredentialsProvider implements CredentialsProvider {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
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.Base64;
|
||||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
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 {
|
private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException {
|
||||||
SignalServiceMessagePipe pipe = MessageRetrievalService.getPipe();
|
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
|
||||||
|
|
||||||
if (pipe != null) {
|
if (pipe != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -51,8 +51,8 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.service.MessageRetrievalService;
|
|
||||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
@ -164,7 +164,7 @@ public class MessageNotifier {
|
|||||||
if (notification.getId() != SUMMARY_NOTIFICATION_ID &&
|
if (notification.getId() != SUMMARY_NOTIFICATION_ID &&
|
||||||
notification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION &&
|
notification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION &&
|
||||||
notification.getId() != KeyCachingService.SERVICE_RUNNING_ID &&
|
notification.getId() != KeyCachingService.SERVICE_RUNNING_ID &&
|
||||||
notification.getId() != MessageRetrievalService.FOREGROUND_ID &&
|
notification.getId() != IncomingMessageObserver.FOREGROUND_ID &&
|
||||||
notification.getId() != PENDING_MESSAGES_ID)
|
notification.getId() != PENDING_MESSAGES_ID)
|
||||||
{
|
{
|
||||||
for (NotificationItem item : notificationState.getNotifications()) {
|
for (NotificationItem item : notificationState.getNotifications()) {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,7 @@ import android.os.Binder;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
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";
|
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 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 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";
|
public static final String LOCALE_CHANGE_EVENT = "org.thoughtcrime.securesms.service.action.LOCALE_CHANGE_EVENT";
|
||||||
|
|
||||||
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||||
|
|
||||||
private PendingIntent pending;
|
|
||||||
private int activitiesRunning = 0;
|
|
||||||
private final IBinder binder = new KeySetBinder();
|
private final IBinder binder = new KeySetBinder();
|
||||||
|
|
||||||
private static MasterSecret masterSecret;
|
private static MasterSecret masterSecret;
|
||||||
@ -98,6 +95,14 @@ public class KeyCachingService extends Service {
|
|||||||
return masterSecret;
|
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")
|
@SuppressLint("StaticFieldLeak")
|
||||||
public void setMasterSecret(final MasterSecret masterSecret) {
|
public void setMasterSecret(final MasterSecret masterSecret) {
|
||||||
synchronized (KeyCachingService.class) {
|
synchronized (KeyCachingService.class) {
|
||||||
@ -105,7 +110,7 @@ public class KeyCachingService extends Service {
|
|||||||
|
|
||||||
foregroundService();
|
foregroundService();
|
||||||
broadcastNewSecret();
|
broadcastNewSecret();
|
||||||
startTimeoutIfAppropriate();
|
startTimeoutIfAppropriate(this);
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
new AsyncTask<Void, Void, Void>() {
|
||||||
@Override
|
@Override
|
||||||
@ -127,8 +132,6 @@ public class KeyCachingService extends Service {
|
|||||||
if (intent.getAction() != null) {
|
if (intent.getAction() != null) {
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case CLEAR_KEY_ACTION: handleClearKey(); break;
|
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 PASSPHRASE_EXPIRED_EVENT: handleClearKey(); break;
|
||||||
case DISABLE_ACTION: handleDisableService(); break;
|
case DISABLE_ACTION: handleDisableService(); break;
|
||||||
case LOCALE_CHANGE_EVENT: handleLocaleChanged(); break;
|
case LOCALE_CHANGE_EVENT: handleLocaleChanged(); break;
|
||||||
@ -143,8 +146,6 @@ public class KeyCachingService extends Service {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
Log.i(TAG, "onCreate()");
|
Log.i(TAG, "onCreate()");
|
||||||
super.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)) {
|
if (TextSecurePreferences.isPasswordDisabled(this) && !TextSecurePreferences.isScreenLockEnabled(this)) {
|
||||||
try {
|
try {
|
||||||
@ -174,21 +175,6 @@ public class KeyCachingService extends Service {
|
|||||||
startActivity(intent);
|
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")
|
@SuppressLint("StaticFieldLeak")
|
||||||
private void handleClearKey() {
|
private void handleClearKey() {
|
||||||
Log.i(TAG, "handleClearKey()");
|
Log.i(TAG, "handleClearKey()");
|
||||||
@ -233,27 +219,29 @@ public class KeyCachingService extends Service {
|
|||||||
foregroundService();
|
foregroundService();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startTimeoutIfAppropriate() {
|
private static void startTimeoutIfAppropriate(@NonNull Context context) {
|
||||||
boolean timeoutEnabled = TextSecurePreferences.isPassphraseTimeoutEnabled(this);
|
boolean timeoutEnabled = TextSecurePreferences.isPassphraseTimeoutEnabled(context);
|
||||||
long screenTimeout = TextSecurePreferences.getScreenLockTimeout(this);
|
long screenTimeout = TextSecurePreferences.getScreenLockTimeout(context);
|
||||||
|
|
||||||
if ((activitiesRunning == 0) && (KeyCachingService.masterSecret != null) &&
|
if ((KeyCachingService.masterSecret != null) &&
|
||||||
(timeoutEnabled && !TextSecurePreferences.isPasswordDisabled(this)) ||
|
(timeoutEnabled && !TextSecurePreferences.isPasswordDisabled(context)) ||
|
||||||
(screenTimeout >= 60 && TextSecurePreferences.isScreenLockEnabled(this)))
|
(screenTimeout >= 60 && TextSecurePreferences.isScreenLockEnabled(context)))
|
||||||
{
|
{
|
||||||
long passphraseTimeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(this);
|
long passphraseTimeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(context);
|
||||||
long screenLockTimeoutSeconds = TextSecurePreferences.getScreenLockTimeout(this);
|
long screenLockTimeoutSeconds = TextSecurePreferences.getScreenLockTimeout(context);
|
||||||
|
|
||||||
long timeoutMillis;
|
long timeoutMillis;
|
||||||
|
|
||||||
if (!TextSecurePreferences.isPasswordDisabled(this)) timeoutMillis = TimeUnit.MINUTES.toMillis(passphraseTimeoutMinutes);
|
if (!TextSecurePreferences.isPasswordDisabled(context)) timeoutMillis = TimeUnit.MINUTES.toMillis(passphraseTimeoutMinutes);
|
||||||
else timeoutMillis = TimeUnit.SECONDS.toMillis(screenLockTimeoutSeconds);
|
else timeoutMillis = TimeUnit.SECONDS.toMillis(screenLockTimeoutSeconds);
|
||||||
|
|
||||||
Log.i(TAG, "Starting timeout: " + timeoutMillis);
|
Log.i(TAG, "Starting timeout: " + timeoutMillis);
|
||||||
|
|
||||||
AlarmManager alarmManager = ServiceUtil.getAlarmManager(this);
|
AlarmManager alarmManager = ServiceUtil.getAlarmManager(context);
|
||||||
alarmManager.cancel(pending);
|
PendingIntent expirationIntent = buildExpirationPendingIntent(context);
|
||||||
alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + timeoutMillis, pending);
|
|
||||||
|
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);
|
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
|
@Override
|
||||||
public IBinder onBind(Intent arg0) {
|
public IBinder onBind(Intent arg0) {
|
||||||
return binder;
|
return binder;
|
||||||
@ -348,16 +341,4 @@ public class KeyCachingService extends Service {
|
|||||||
return KeyCachingService.this;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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<Intent> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,21 +4,19 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
|
||||||
|
|
||||||
public class PersistentConnectionBootListener extends BroadcastReceiver {
|
public class PersistentConnectionBootListener extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private static final String TAG = PersistentConnectionBootListener.class.getSimpleName();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (intent != null && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
|
if (intent != null && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
|
||||||
if (TextSecurePreferences.isGcmDisabled(context)) {
|
Log.i(TAG, "Received boot event. Application should be started, allowing non-GCM devices to start a foreground service.");
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user