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.Optional
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__internal_preferences) {
|
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)
|
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(
|
switchPref(
|
||||||
title = DSLSettingsText.from(R.string.preferences__internal_allow_censorship_toggle),
|
title = DSLSettingsText.from(R.string.preferences__internal_allow_censorship_toggle),
|
||||||
summary = DSLSettingsText.from(R.string.preferences__internal_allow_censorship_toggle_description),
|
summary = DSLSettingsText.from(R.string.preferences__internal_allow_censorship_toggle_description),
|
||||||
|
|
|
@ -10,6 +10,7 @@ data class InternalSettingsState(
|
||||||
val gv2ignoreServerChanges: Boolean,
|
val gv2ignoreServerChanges: Boolean,
|
||||||
val gv2ignoreP2PChanges: Boolean,
|
val gv2ignoreP2PChanges: Boolean,
|
||||||
val allowCensorshipSetting: Boolean,
|
val allowCensorshipSetting: Boolean,
|
||||||
|
val forceWebsocketMode: Boolean,
|
||||||
val callingServer: String,
|
val callingServer: String,
|
||||||
val callingAudioProcessingMethod: CallManager.AudioProcessingMethod,
|
val callingAudioProcessingMethod: CallManager.AudioProcessingMethod,
|
||||||
val callingBandwidthMode: CallManager.BandwidthMode,
|
val callingBandwidthMode: CallManager.BandwidthMode,
|
||||||
|
|
|
@ -59,6 +59,11 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setForceWebsocketMode(enabled: Boolean) {
|
||||||
|
preferenceDataStore.putBoolean(InternalValues.FORCE_WEBSOCKET_MODE, enabled)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
fun setUseBuiltInEmoji(enabled: Boolean) {
|
fun setUseBuiltInEmoji(enabled: Boolean) {
|
||||||
preferenceDataStore.putBoolean(InternalValues.FORCE_BUILT_IN_EMOJI, enabled)
|
preferenceDataStore.putBoolean(InternalValues.FORCE_BUILT_IN_EMOJI, enabled)
|
||||||
refresh()
|
refresh()
|
||||||
|
@ -109,6 +114,7 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
|
||||||
gv2ignoreServerChanges = SignalStore.internalValues().gv2IgnoreServerChanges(),
|
gv2ignoreServerChanges = SignalStore.internalValues().gv2IgnoreServerChanges(),
|
||||||
gv2ignoreP2PChanges = SignalStore.internalValues().gv2IgnoreP2PChanges(),
|
gv2ignoreP2PChanges = SignalStore.internalValues().gv2IgnoreP2PChanges(),
|
||||||
allowCensorshipSetting = SignalStore.internalValues().allowChangingCensorshipSetting(),
|
allowCensorshipSetting = SignalStore.internalValues().allowChangingCensorshipSetting(),
|
||||||
|
forceWebsocketMode = SignalStore.internalValues().isWebsocketModeForced,
|
||||||
callingServer = SignalStore.internalValues().groupCallingServer(),
|
callingServer = SignalStore.internalValues().groupCallingServer(),
|
||||||
callingAudioProcessingMethod = SignalStore.internalValues().callingAudioProcessingMethod(),
|
callingAudioProcessingMethod = SignalStore.internalValues().callingAudioProcessingMethod(),
|
||||||
callingBandwidthMode = SignalStore.internalValues().callingBandwidthMode(),
|
callingBandwidthMode = SignalStore.internalValues().callingBandwidthMode(),
|
||||||
|
|
|
@ -283,7 +283,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier<SignalServiceConfiguration> signalServiceConfigurationSupplier) {
|
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);
|
SignalWebSocketHealthMonitor healthMonitor = new SignalWebSocketHealthMonitor(context, sleepTimer);
|
||||||
SignalWebSocket signalWebSocket = new SignalWebSocket(provideWebSocketFactory(signalServiceConfigurationSupplier, healthMonitor));
|
SignalWebSocket signalWebSocket = new SignalWebSocket(provideWebSocketFactory(signalServiceConfigurationSupplier, healthMonitor));
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ public class RefreshAttributesJob extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
int registrationId = SignalStore.account().getRegistrationId();
|
int registrationId = SignalStore.account().getRegistrationId();
|
||||||
boolean fetchesMessages = !SignalStore.account().isFcmEnabled();
|
boolean fetchesMessages = !SignalStore.account().isFcmEnabled() || SignalStore.internalValues().isWebsocketModeForced();
|
||||||
byte[] unidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey());
|
byte[] unidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey());
|
||||||
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
|
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
|
||||||
String registrationLockV1 = null;
|
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 CALLING_DISABLE_TELECOM = "internal.calling_disable_telecom";
|
||||||
public static final String SHAKE_TO_REPORT = "internal.shake_to_report";
|
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 DISABLE_STORAGE_SERVICE = "internal.disable_storage_service";
|
||||||
|
public static final String FORCE_WEBSOCKET_MODE = "internal.force_websocket_mode";
|
||||||
|
|
||||||
InternalValues(KeyValueStore store) {
|
InternalValues(KeyValueStore store) {
|
||||||
super(store);
|
super(store);
|
||||||
|
@ -164,4 +165,15 @@ public final class InternalValues extends SignalStoreValues {
|
||||||
return false;
|
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();
|
new MessageRetrievalThread().start();
|
||||||
|
|
||||||
if (!SignalStore.account().isFcmEnabled()) {
|
if (!SignalStore.account().isFcmEnabled() || SignalStore.internalValues().isWebsocketModeForced()) {
|
||||||
ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class));
|
ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,22 +157,23 @@ public class IncomingMessageObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized boolean isConnectionNecessary() {
|
private synchronized boolean isConnectionNecessary() {
|
||||||
boolean registered = SignalStore.account().isRegistered();
|
boolean registered = SignalStore.account().isRegistered();
|
||||||
boolean fcmEnabled = SignalStore.account().isFcmEnabled();
|
boolean fcmEnabled = SignalStore.account().isFcmEnabled();
|
||||||
boolean hasNetwork = NetworkConstraint.isMet(context);
|
boolean hasNetwork = NetworkConstraint.isMet(context);
|
||||||
boolean hasProxy = SignalStore.proxy().isProxyEnabled();
|
boolean hasProxy = SignalStore.proxy().isProxyEnabled();
|
||||||
long oldRequest = System.currentTimeMillis() - OLD_REQUEST_WINDOW_MS;
|
boolean forceWebsocket = SignalStore.internalValues().isWebsocketModeForced();
|
||||||
|
long oldRequest = System.currentTimeMillis() - OLD_REQUEST_WINDOW_MS;
|
||||||
|
|
||||||
boolean removedRequests = keepAliveTokens.entrySet().removeIf(e -> e.getValue() < oldRequest);
|
boolean removedRequests = keepAliveTokens.entrySet().removeIf(e -> e.getValue() < oldRequest);
|
||||||
if (removedRequests) {
|
if (removedRequests) {
|
||||||
Log.d(TAG, "Removed old keep web socket open requests.");
|
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",
|
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));
|
hasNetwork, appVisible, fcmEnabled, Util.join(keepAliveTokens.entrySet(), ","), networkAccess.isCensored(), registered, hasProxy, forceWebsocket));
|
||||||
|
|
||||||
return registered &&
|
return registered &&
|
||||||
(appVisible || !fcmEnabled || Util.hasItems(keepAliveTokens)) &&
|
(appVisible || !fcmEnabled || forceWebsocket || Util.hasItems(keepAliveTokens)) &&
|
||||||
hasNetwork &&
|
hasNetwork &&
|
||||||
!networkAccess.isCensored();
|
!networkAccess.isCensored();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,12 @@ package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
|
||||||
import androidx.core.app.AlarmManagerCompat;
|
import androidx.core.app.AlarmManagerCompat;
|
||||||
|
@ -18,13 +20,12 @@ import org.whispersystems.signalservice.api.util.SleepTimer;
|
||||||
import java.util.concurrent.ConcurrentSkipListSet;
|
import java.util.concurrent.ConcurrentSkipListSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A sleep timer that is based on elapsed realtime, so
|
* A sleep timer that is based on elapsed realtime, so that it works properly, even in low-power sleep modes.
|
||||||
* that it works properly, even in low-power sleep modes.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class AlarmSleepTimer implements SleepTimer {
|
public class AlarmSleepTimer implements SleepTimer {
|
||||||
private static final String TAG = Log.tag(AlarmSleepTimer.class);
|
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;
|
private final Context context;
|
||||||
|
|
||||||
|
@ -33,23 +34,25 @@ public class AlarmSleepTimer implements SleepTimer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sleep(long millis) {
|
public void sleep(long sleepDuration) {
|
||||||
final AlarmReceiver alarmReceiver = new AlarmSleepTimer.AlarmReceiver();
|
AlarmReceiver alarmReceiver = new AlarmSleepTimer.AlarmReceiver();
|
||||||
int actionId = 0;
|
int actionId = 0;
|
||||||
|
|
||||||
while (!actionIdList.add(actionId)){
|
while (!actionIdList.add(actionId)){
|
||||||
actionId++;
|
actionId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
context.registerReceiver(alarmReceiver,
|
String actionName = buildActionName(actionId);
|
||||||
new IntentFilter(AlarmReceiver.WAKE_UP_THREAD_ACTION + "." + actionId));
|
context.registerReceiver(alarmReceiver, new IntentFilter(actionName));
|
||||||
|
|
||||||
final long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
alarmReceiver.setAlarm(millis, AlarmReceiver.WAKE_UP_THREAD_ACTION + "." + actionId);
|
alarmReceiver.setAlarm(sleepDuration, actionName);
|
||||||
|
|
||||||
while (System.currentTimeMillis() - startTime < millis) {
|
while (System.currentTimeMillis() - startTime < sleepDuration) {
|
||||||
try {
|
try {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
wait(millis - System.currentTimeMillis() + startTime);
|
wait(sleepDuration - (System.currentTimeMillis() - startTime));
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
|
@ -58,25 +61,35 @@ public class AlarmSleepTimer implements SleepTimer {
|
||||||
context.unregisterReceiver(alarmReceiver);
|
context.unregisterReceiver(alarmReceiver);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
Log.w(TAG, "Exception during sleep ...",e);
|
Log.w(TAG, "Exception during sleep ...",e);
|
||||||
}finally {
|
} finally {
|
||||||
actionIdList.remove(actionId);
|
actionIdList.remove(actionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String buildActionName(int actionId) {
|
||||||
|
return AlarmReceiver.WAKE_UP_THREAD_ACTION + "." + actionId;
|
||||||
|
}
|
||||||
|
|
||||||
private class AlarmReceiver extends BroadcastReceiver {
|
private class AlarmReceiver extends BroadcastReceiver {
|
||||||
private static final String WAKE_UP_THREAD_ACTION = "org.thoughtcrime.securesms.util.AlarmSleepTimer.AlarmReceiver.WAKE_UP_THREAD";
|
private static final String WAKE_UP_THREAD_ACTION = "org.thoughtcrime.securesms.util.AlarmSleepTimer.AlarmReceiver.WAKE_UP_THREAD";
|
||||||
|
|
||||||
private void setAlarm(long millis, String action) {
|
private void setAlarm(long millis, String action) {
|
||||||
final Intent intent = new Intent(action);
|
final Intent intent = new Intent(action);
|
||||||
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable());
|
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.");
|
if (Build.VERSION.SDK_INT < 31 || alarmManager.canScheduleExactAlarms()) {
|
||||||
|
Log.d(TAG, "Setting an exact alarm to wake up in " + millis + "ms.");
|
||||||
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager,
|
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager,
|
||||||
AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
||||||
SystemClock.elapsedRealtime() + millis,
|
SystemClock.elapsedRealtime() + millis,
|
||||||
pendingIntent);
|
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
|
@Override
|
||||||
|
|
Ładowanie…
Reference in New Issue