Prepare the websocket keepalive for API 31.

fork-5.53.8
Greyson Parrelli 2022-09-30 16:41:00 -04:00
rodzic 437c3ffd66
commit afedbf40e3
8 zmienionych plików z 85 dodań i 32 usunięć

Wyświetl plik

@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.util.navigation.safeNavigate
import java.util.Optional
import java.util.concurrent.TimeUnit
import kotlin.math.max
import kotlin.time.Duration.Companion.seconds
class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__internal_preferences) {
@ -201,6 +202,25 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
sectionHeaderPref(R.string.preferences__internal_network)
switchPref(
title = DSLSettingsText.from("Force websocket mode"),
summary = DSLSettingsText.from("Pretend you have no Play Services. Ignores websocket messages and keeps the websocket open in a foreground service. You have to manually force-stop the app for changes to take effect."),
isChecked = state.forceWebsocketMode,
onClick = {
viewModel.setForceWebsocketMode(!state.forceWebsocketMode)
SimpleTask.run({
val jobState = ApplicationDependencies.getJobManager().runSynchronously(RefreshAttributesJob(), 10.seconds.inWholeMilliseconds)
return@run jobState.isPresent && jobState.get().isComplete
}, { success ->
if (success) {
Toast.makeText(context, "Successfully refreshed attributes. Force-stop the app for changes to take effect.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Failed to refresh attributes.", Toast.LENGTH_SHORT).show()
}
})
}
)
switchPref(
title = DSLSettingsText.from(R.string.preferences__internal_allow_censorship_toggle),
summary = DSLSettingsText.from(R.string.preferences__internal_allow_censorship_toggle_description),

Wyświetl plik

@ -10,6 +10,7 @@ data class InternalSettingsState(
val gv2ignoreServerChanges: Boolean,
val gv2ignoreP2PChanges: Boolean,
val allowCensorshipSetting: Boolean,
val forceWebsocketMode: Boolean,
val callingServer: String,
val callingAudioProcessingMethod: CallManager.AudioProcessingMethod,
val callingBandwidthMode: CallManager.BandwidthMode,

Wyświetl plik

@ -59,6 +59,11 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
refresh()
}
fun setForceWebsocketMode(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.FORCE_WEBSOCKET_MODE, enabled)
refresh()
}
fun setUseBuiltInEmoji(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.FORCE_BUILT_IN_EMOJI, enabled)
refresh()
@ -109,6 +114,7 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
gv2ignoreServerChanges = SignalStore.internalValues().gv2IgnoreServerChanges(),
gv2ignoreP2PChanges = SignalStore.internalValues().gv2IgnoreP2PChanges(),
allowCensorshipSetting = SignalStore.internalValues().allowChangingCensorshipSetting(),
forceWebsocketMode = SignalStore.internalValues().isWebsocketModeForced,
callingServer = SignalStore.internalValues().groupCallingServer(),
callingAudioProcessingMethod = SignalStore.internalValues().callingAudioProcessingMethod(),
callingBandwidthMode = SignalStore.internalValues().callingBandwidthMode(),

Wyświetl plik

@ -283,7 +283,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
@Override
public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier<SignalServiceConfiguration> signalServiceConfigurationSupplier) {
SleepTimer sleepTimer = SignalStore.account().isFcmEnabled() ? new UptimeSleepTimer() : new AlarmSleepTimer(context);
SleepTimer sleepTimer = !SignalStore.account().isFcmEnabled() || SignalStore.internalValues().isWebsocketModeForced() ? new AlarmSleepTimer(context) : new UptimeSleepTimer() ;
SignalWebSocketHealthMonitor healthMonitor = new SignalWebSocketHealthMonitor(context, sleepTimer);
SignalWebSocket signalWebSocket = new SignalWebSocket(provideWebSocketFactory(signalServiceConfigurationSupplier, healthMonitor));

Wyświetl plik

@ -84,7 +84,7 @@ public class RefreshAttributesJob extends BaseJob {
}
int registrationId = SignalStore.account().getRegistrationId();
boolean fetchesMessages = !SignalStore.account().isFcmEnabled();
boolean fetchesMessages = !SignalStore.account().isFcmEnabled() || SignalStore.internalValues().isWebsocketModeForced();
byte[] unidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey());
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
String registrationLockV1 = null;

Wyświetl plik

@ -26,6 +26,7 @@ public final class InternalValues extends SignalStoreValues {
public static final String CALLING_DISABLE_TELECOM = "internal.calling_disable_telecom";
public static final String SHAKE_TO_REPORT = "internal.shake_to_report";
public static final String DISABLE_STORAGE_SERVICE = "internal.disable_storage_service";
public static final String FORCE_WEBSOCKET_MODE = "internal.force_websocket_mode";
InternalValues(KeyValueStore store) {
super(store);
@ -164,4 +165,15 @@ public final class InternalValues extends SignalStoreValues {
return false;
}
}
/**
* Whether or not the system is forced to be in 'websocket mode', where FCM is ignored and we use a foreground service to keep the app alive.
*/
public boolean isWebsocketModeForced() {
if (FeatureFlags.internalUser()) {
return getBoolean(FORCE_WEBSOCKET_MODE, false);
} else {
return false;
}
}
}

Wyświetl plik

@ -81,7 +81,7 @@ public class IncomingMessageObserver {
new MessageRetrievalThread().start();
if (!SignalStore.account().isFcmEnabled()) {
if (!SignalStore.account().isFcmEnabled() || SignalStore.internalValues().isWebsocketModeForced()) {
ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class));
}
@ -157,22 +157,23 @@ public class IncomingMessageObserver {
}
private synchronized boolean isConnectionNecessary() {
boolean registered = SignalStore.account().isRegistered();
boolean fcmEnabled = SignalStore.account().isFcmEnabled();
boolean hasNetwork = NetworkConstraint.isMet(context);
boolean hasProxy = SignalStore.proxy().isProxyEnabled();
long oldRequest = System.currentTimeMillis() - OLD_REQUEST_WINDOW_MS;
boolean registered = SignalStore.account().isRegistered();
boolean fcmEnabled = SignalStore.account().isFcmEnabled();
boolean hasNetwork = NetworkConstraint.isMet(context);
boolean hasProxy = SignalStore.proxy().isProxyEnabled();
boolean forceWebsocket = SignalStore.internalValues().isWebsocketModeForced();
long oldRequest = System.currentTimeMillis() - OLD_REQUEST_WINDOW_MS;
boolean removedRequests = keepAliveTokens.entrySet().removeIf(e -> e.getValue() < oldRequest);
if (removedRequests) {
Log.d(TAG, "Removed old keep web socket open requests.");
}
Log.d(TAG, String.format("Network: %s, Foreground: %s, FCM: %s, Stay open requests: [%s], Censored: %s, Registered: %s, Proxy: %s",
hasNetwork, appVisible, fcmEnabled, Util.join(keepAliveTokens.entrySet(), ","), networkAccess.isCensored(), registered, hasProxy));
Log.d(TAG, String.format("Network: %s, Foreground: %s, FCM: %s, Stay open requests: [%s], Censored: %s, Registered: %s, Proxy: %s, Force websocket: %s",
hasNetwork, appVisible, fcmEnabled, Util.join(keepAliveTokens.entrySet(), ","), networkAccess.isCensored(), registered, hasProxy, forceWebsocket));
return registered &&
(appVisible || !fcmEnabled || Util.hasItems(keepAliveTokens)) &&
(appVisible || !fcmEnabled || forceWebsocket || Util.hasItems(keepAliveTokens)) &&
hasNetwork &&
!networkAccess.isCensored();
}

Wyświetl plik

@ -2,10 +2,12 @@ package org.thoughtcrime.securesms.util;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.SystemClock;
import androidx.core.app.AlarmManagerCompat;
@ -18,13 +20,12 @@ import org.whispersystems.signalservice.api.util.SleepTimer;
import java.util.concurrent.ConcurrentSkipListSet;
/**
* A sleep timer that is based on elapsed realtime, so
* that it works properly, even in low-power sleep modes.
*
* A sleep timer that is based on elapsed realtime, so that it works properly, even in low-power sleep modes.
*/
public class AlarmSleepTimer implements SleepTimer {
private static final String TAG = Log.tag(AlarmSleepTimer.class);
private static ConcurrentSkipListSet<Integer> actionIdList = new ConcurrentSkipListSet<>();
private static final ConcurrentSkipListSet<Integer> actionIdList = new ConcurrentSkipListSet<>();
private final Context context;
@ -33,23 +34,25 @@ public class AlarmSleepTimer implements SleepTimer {
}
@Override
public void sleep(long millis) {
final AlarmReceiver alarmReceiver = new AlarmSleepTimer.AlarmReceiver();
int actionId = 0;
public void sleep(long sleepDuration) {
AlarmReceiver alarmReceiver = new AlarmSleepTimer.AlarmReceiver();
int actionId = 0;
while (!actionIdList.add(actionId)){
actionId++;
}
try {
context.registerReceiver(alarmReceiver,
new IntentFilter(AlarmReceiver.WAKE_UP_THREAD_ACTION + "." + actionId));
String actionName = buildActionName(actionId);
context.registerReceiver(alarmReceiver, new IntentFilter(actionName));
final long startTime = System.currentTimeMillis();
alarmReceiver.setAlarm(millis, AlarmReceiver.WAKE_UP_THREAD_ACTION + "." + actionId);
long startTime = System.currentTimeMillis();
alarmReceiver.setAlarm(sleepDuration, actionName);
while (System.currentTimeMillis() - startTime < millis) {
while (System.currentTimeMillis() - startTime < sleepDuration) {
try {
synchronized (this) {
wait(millis - System.currentTimeMillis() + startTime);
wait(sleepDuration - (System.currentTimeMillis() - startTime));
}
} catch (InterruptedException e) {
Log.w(TAG, e);
@ -58,25 +61,35 @@ public class AlarmSleepTimer implements SleepTimer {
context.unregisterReceiver(alarmReceiver);
} catch(Exception e) {
Log.w(TAG, "Exception during sleep ...",e);
}finally {
} finally {
actionIdList.remove(actionId);
}
}
private static String buildActionName(int actionId) {
return AlarmReceiver.WAKE_UP_THREAD_ACTION + "." + actionId;
}
private class AlarmReceiver extends BroadcastReceiver {
private static final String WAKE_UP_THREAD_ACTION = "org.thoughtcrime.securesms.util.AlarmSleepTimer.AlarmReceiver.WAKE_UP_THREAD";
private void setAlarm(long millis, String action) {
final Intent intent = new Intent(action);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable());
final AlarmManager alarmManager = ContextCompat.getSystemService(context, AlarmManager.class);
final AlarmManager alarmManager = ServiceUtil.getAlarmManager(context);
Log.w(TAG, "Setting alarm to wake up in " + millis + "ms.");
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager,
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + millis,
pendingIntent);
if (Build.VERSION.SDK_INT < 31 || alarmManager.canScheduleExactAlarms()) {
Log.d(TAG, "Setting an exact alarm to wake up in " + millis + "ms.");
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager,
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + millis,
pendingIntent);
} else {
Log.w(TAG, "Setting an inexact alarm to wake up in " + millis + "ms. CanScheduleAlarms: " + alarmManager.canScheduleExactAlarms());
alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + millis,
pendingIntent);
}
}
@Override