kopia lustrzana https://github.com/ryukoposting/Signal-Android
Prepare the websocket keepalive for API 31.
rodzic
437c3ffd66
commit
afedbf40e3
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue