kopia lustrzana https://github.com/ryukoposting/Signal-Android
Improve network reliability.
rodzic
1314b04994
commit
01e75120a7
|
@ -198,11 +198,11 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
|
||||||
sectionHeaderPref(R.string.preferences__internal_network)
|
sectionHeaderPref(R.string.preferences__internal_network)
|
||||||
|
|
||||||
switchPref(
|
switchPref(
|
||||||
title = DSLSettingsText.from(R.string.preferences__internal_force_censorship),
|
title = DSLSettingsText.from(R.string.preferences__internal_allow_censorship_toggle),
|
||||||
summary = DSLSettingsText.from(R.string.preferences__internal_force_censorship_description),
|
summary = DSLSettingsText.from(R.string.preferences__internal_allow_censorship_toggle_description),
|
||||||
isChecked = state.forceCensorship,
|
isChecked = state.allowCensorshipSetting,
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.setForceCensorship(!state.forceCensorship)
|
viewModel.setAllowCensorshipSetting(!state.allowCensorshipSetting)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ data class InternalSettingsState(
|
||||||
val gv2ignoreP2PChanges: Boolean,
|
val gv2ignoreP2PChanges: Boolean,
|
||||||
val disableAutoMigrationInitiation: Boolean,
|
val disableAutoMigrationInitiation: Boolean,
|
||||||
val disableAutoMigrationNotification: Boolean,
|
val disableAutoMigrationNotification: Boolean,
|
||||||
val forceCensorship: Boolean,
|
val allowCensorshipSetting: Boolean,
|
||||||
val callingServer: String,
|
val callingServer: String,
|
||||||
val audioProcessingMethod: CallManager.AudioProcessingMethod,
|
val audioProcessingMethod: CallManager.AudioProcessingMethod,
|
||||||
val useBuiltInEmojiSet: Boolean,
|
val useBuiltInEmojiSet: Boolean,
|
||||||
|
|
|
@ -66,8 +66,8 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setForceCensorship(enabled: Boolean) {
|
fun setAllowCensorshipSetting(enabled: Boolean) {
|
||||||
preferenceDataStore.putBoolean(InternalValues.FORCE_CENSORSHIP, enabled)
|
preferenceDataStore.putBoolean(InternalValues.ALLOW_CENSORSHIP_SETTING, enabled)
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
|
||||||
gv2ignoreP2PChanges = SignalStore.internalValues().gv2IgnoreP2PChanges(),
|
gv2ignoreP2PChanges = SignalStore.internalValues().gv2IgnoreP2PChanges(),
|
||||||
disableAutoMigrationInitiation = SignalStore.internalValues().disableGv1AutoMigrateInitiation(),
|
disableAutoMigrationInitiation = SignalStore.internalValues().disableGv1AutoMigrateInitiation(),
|
||||||
disableAutoMigrationNotification = SignalStore.internalValues().disableGv1AutoMigrateNotification(),
|
disableAutoMigrationNotification = SignalStore.internalValues().disableGv1AutoMigrateNotification(),
|
||||||
forceCensorship = SignalStore.internalValues().forcedCensorship(),
|
allowCensorshipSetting = SignalStore.internalValues().allowChangingCensorshipSetting(),
|
||||||
callingServer = SignalStore.internalValues().groupCallingServer(),
|
callingServer = SignalStore.internalValues().groupCallingServer(),
|
||||||
audioProcessingMethod = SignalStore.internalValues().audioProcessingMethod(),
|
audioProcessingMethod = SignalStore.internalValues().audioProcessingMethod(),
|
||||||
useBuiltInEmojiSet = SignalStore.internalValues().forceBuiltInEmoji(),
|
useBuiltInEmojiSet = SignalStore.internalValues().forceBuiltInEmoji(),
|
||||||
|
|
|
@ -138,6 +138,28 @@ class AdvancedPrivacySettingsFragment : DSLSettingsFragment(R.string.preferences
|
||||||
|
|
||||||
dividerPref()
|
dividerPref()
|
||||||
|
|
||||||
|
sectionHeaderPref(R.string.preferences_communication__category_censorship_circumvention)
|
||||||
|
|
||||||
|
val censorshipSummaryResId: Int = when (state.censorshipCircumventionState) {
|
||||||
|
CensorshipCircumventionState.AVAILABLE -> R.string.preferences_communication__censorship_circumvention_if_enabled_signal_will_attempt_to_circumvent_censorship
|
||||||
|
CensorshipCircumventionState.AVAILABLE_MANUALLY_DISABLED -> R.string.preferences_communication__censorship_circumvention_you_have_manually_disabled
|
||||||
|
CensorshipCircumventionState.AVAILABLE_AUTOMATICALLY_ENABLED -> R.string.preferences_communication__censorship_circumvention_has_been_activated_based_on_your_accounts_phone_number
|
||||||
|
CensorshipCircumventionState.UNAVAILABLE_CONNECTED -> R.string.preferences_communication__censorship_circumvention_is_not_necessary_you_are_already_connected
|
||||||
|
CensorshipCircumventionState.UNAVAILABLE_NO_INTERNET -> R.string.preferences_communication__censorship_circumvention_can_only_be_activated_when_connected_to_the_internet
|
||||||
|
}
|
||||||
|
|
||||||
|
switchPref(
|
||||||
|
title = DSLSettingsText.from(R.string.preferences_communication__censorship_circumvention),
|
||||||
|
summary = DSLSettingsText.from(censorshipSummaryResId),
|
||||||
|
isChecked = state.censorshipCircumventionEnabled,
|
||||||
|
isEnabled = state.censorshipCircumventionState.available,
|
||||||
|
onClick = {
|
||||||
|
viewModel.setCensorshipCircumventionEnabled(!state.censorshipCircumventionEnabled)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
dividerPref()
|
||||||
|
|
||||||
sectionHeaderPref(R.string.preferences_communication__category_sealed_sender)
|
sectionHeaderPref(R.string.preferences_communication__category_sealed_sender)
|
||||||
|
|
||||||
switchPref(
|
switchPref(
|
||||||
|
|
|
@ -3,7 +3,26 @@ package org.thoughtcrime.securesms.components.settings.app.privacy.advanced
|
||||||
data class AdvancedPrivacySettingsState(
|
data class AdvancedPrivacySettingsState(
|
||||||
val isPushEnabled: Boolean,
|
val isPushEnabled: Boolean,
|
||||||
val alwaysRelayCalls: Boolean,
|
val alwaysRelayCalls: Boolean,
|
||||||
|
val censorshipCircumventionState: CensorshipCircumventionState,
|
||||||
|
val censorshipCircumventionEnabled: Boolean,
|
||||||
val showSealedSenderStatusIcon: Boolean,
|
val showSealedSenderStatusIcon: Boolean,
|
||||||
val allowSealedSenderFromAnyone: Boolean,
|
val allowSealedSenderFromAnyone: Boolean,
|
||||||
val showProgressSpinner: Boolean
|
val showProgressSpinner: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
enum class CensorshipCircumventionState(val available: Boolean) {
|
||||||
|
/** The setting is unavailable because you're connected to the websocket */
|
||||||
|
UNAVAILABLE_CONNECTED(false),
|
||||||
|
|
||||||
|
/** The setting is unavailable because you have no network access at all */
|
||||||
|
UNAVAILABLE_NO_INTERNET(false),
|
||||||
|
|
||||||
|
/** The setting is available, and the user manually disabled it even though we thought they were censored */
|
||||||
|
AVAILABLE_MANUALLY_DISABLED(true),
|
||||||
|
|
||||||
|
/** The setting is available, and it's on because we think the user is censored */
|
||||||
|
AVAILABLE_AUTOMATICALLY_ENABLED(true),
|
||||||
|
|
||||||
|
/** The setting is generically available */
|
||||||
|
AVAILABLE(true),
|
||||||
|
}
|
||||||
|
|
|
@ -4,23 +4,40 @@ import android.content.SharedPreferences
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SettingsValues
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||||
import org.thoughtcrime.securesms.util.SingleLiveEvent
|
import org.thoughtcrime.securesms.util.SingleLiveEvent
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.util.livedata.Store
|
import org.thoughtcrime.securesms.util.livedata.Store
|
||||||
|
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
|
||||||
|
|
||||||
class AdvancedPrivacySettingsViewModel(
|
class AdvancedPrivacySettingsViewModel(
|
||||||
private val sharedPreferences: SharedPreferences,
|
private val sharedPreferences: SharedPreferences,
|
||||||
private val repository: AdvancedPrivacySettingsRepository
|
private val repository: AdvancedPrivacySettingsRepository
|
||||||
) : ViewModel() {
|
) : ViewModel(), NetworkConstraintObserver.NetworkListener {
|
||||||
|
|
||||||
private val store = Store(getState())
|
private val store = Store(getState())
|
||||||
private val singleEvents = SingleLiveEvent<Event>()
|
private val singleEvents = SingleLiveEvent<Event>()
|
||||||
|
|
||||||
val state: LiveData<AdvancedPrivacySettingsState> = store.stateLiveData
|
val state: LiveData<AdvancedPrivacySettingsState> = store.stateLiveData
|
||||||
val events: LiveData<Event> = singleEvents
|
val events: LiveData<Event> = singleEvents
|
||||||
|
val disposables: CompositeDisposable = CompositeDisposable()
|
||||||
|
|
||||||
|
init {
|
||||||
|
NetworkConstraintObserver.getInstance(ApplicationDependencies.getApplication()).addListener(this)
|
||||||
|
disposables.add(
|
||||||
|
ApplicationDependencies.getSignalWebSocket().webSocketState
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { refresh() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun disablePushMessages() {
|
fun disablePushMessages() {
|
||||||
store.update { getState().copy(showProgressSpinner = true) }
|
store.update { getState().copy(showProgressSpinner = true) }
|
||||||
|
@ -58,21 +75,87 @@ class AdvancedPrivacySettingsViewModel(
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setCensorshipCircumventionEnabled(enabled: Boolean) {
|
||||||
|
SignalStore.settings().setCensorshipCircumventionEnabled(enabled)
|
||||||
|
ApplicationDependencies.resetNetworkConnectionsAfterProxyChange()
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
store.update { getState().copy(showProgressSpinner = it.showProgressSpinner) }
|
store.update { getState().copy(showProgressSpinner = it.showProgressSpinner) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getState() = AdvancedPrivacySettingsState(
|
override fun onNetworkChanged() {
|
||||||
isPushEnabled = SignalStore.account().isRegistered,
|
refresh()
|
||||||
alwaysRelayCalls = TextSecurePreferences.isTurnOnly(ApplicationDependencies.getApplication()),
|
}
|
||||||
showSealedSenderStatusIcon = TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(
|
|
||||||
ApplicationDependencies.getApplication()
|
override fun onCleared() {
|
||||||
),
|
NetworkConstraintObserver.getInstance(ApplicationDependencies.getApplication()).removeListener(this)
|
||||||
allowSealedSenderFromAnyone = TextSecurePreferences.isUniversalUnidentifiedAccess(
|
disposables.dispose()
|
||||||
ApplicationDependencies.getApplication()
|
}
|
||||||
),
|
|
||||||
false
|
private fun getState(): AdvancedPrivacySettingsState {
|
||||||
)
|
val censorshipCircumventionState = getCensorshipCircumventionState()
|
||||||
|
|
||||||
|
return AdvancedPrivacySettingsState(
|
||||||
|
isPushEnabled = SignalStore.account().isRegistered,
|
||||||
|
alwaysRelayCalls = TextSecurePreferences.isTurnOnly(ApplicationDependencies.getApplication()),
|
||||||
|
censorshipCircumventionState = censorshipCircumventionState,
|
||||||
|
censorshipCircumventionEnabled = getCensorshipCircumventionEnabled(censorshipCircumventionState),
|
||||||
|
showSealedSenderStatusIcon = TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(
|
||||||
|
ApplicationDependencies.getApplication()
|
||||||
|
),
|
||||||
|
allowSealedSenderFromAnyone = TextSecurePreferences.isUniversalUnidentifiedAccess(
|
||||||
|
ApplicationDependencies.getApplication()
|
||||||
|
),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCensorshipCircumventionState(): CensorshipCircumventionState {
|
||||||
|
val countryCode: Int = PhoneNumberFormatter.getLocalCountryCode()
|
||||||
|
val isCountryCodeCensoredByDefault: Boolean = ApplicationDependencies.getSignalServiceNetworkAccess().isCountryCodeCensoredByDefault(countryCode)
|
||||||
|
val enabledState: SettingsValues.CensorshipCircumventionEnabled = SignalStore.settings().censorshipCircumventionEnabled
|
||||||
|
val hasInternet: Boolean = NetworkConstraint.isMet(ApplicationDependencies.getApplication())
|
||||||
|
val websocketConnected: Boolean = ApplicationDependencies.getSignalWebSocket().webSocketState.firstOrError().blockingGet() == WebSocketConnectionState.CONNECTED
|
||||||
|
|
||||||
|
return when {
|
||||||
|
SignalStore.internalValues().allowChangingCensorshipSetting() -> {
|
||||||
|
CensorshipCircumventionState.AVAILABLE
|
||||||
|
}
|
||||||
|
isCountryCodeCensoredByDefault && enabledState == SettingsValues.CensorshipCircumventionEnabled.DISABLED -> {
|
||||||
|
CensorshipCircumventionState.AVAILABLE_MANUALLY_DISABLED
|
||||||
|
}
|
||||||
|
isCountryCodeCensoredByDefault -> {
|
||||||
|
CensorshipCircumventionState.AVAILABLE_AUTOMATICALLY_ENABLED
|
||||||
|
}
|
||||||
|
!hasInternet && enabledState != SettingsValues.CensorshipCircumventionEnabled.ENABLED -> {
|
||||||
|
CensorshipCircumventionState.UNAVAILABLE_NO_INTERNET
|
||||||
|
}
|
||||||
|
websocketConnected && enabledState != SettingsValues.CensorshipCircumventionEnabled.ENABLED -> {
|
||||||
|
CensorshipCircumventionState.UNAVAILABLE_CONNECTED
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
CensorshipCircumventionState.AVAILABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCensorshipCircumventionEnabled(state: CensorshipCircumventionState): Boolean {
|
||||||
|
return when (state) {
|
||||||
|
CensorshipCircumventionState.UNAVAILABLE_CONNECTED,
|
||||||
|
CensorshipCircumventionState.UNAVAILABLE_NO_INTERNET,
|
||||||
|
CensorshipCircumventionState.AVAILABLE_MANUALLY_DISABLED -> {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
CensorshipCircumventionState.AVAILABLE_AUTOMATICALLY_ENABLED -> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
SignalStore.settings().censorshipCircumventionEnabled == SettingsValues.CensorshipCircumventionEnabled.ENABLED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class Event {
|
enum class Event {
|
||||||
DISABLE_PUSH_FAILED
|
DISABLE_PUSH_FAILED
|
||||||
|
|
|
@ -19,7 +19,7 @@ public final class InternalValues extends SignalStoreValues {
|
||||||
public static final String GV2_DISABLE_AUTOMIGRATE_INITIATION = "internal.gv2.disable_automigrate_initiation";
|
public static final String GV2_DISABLE_AUTOMIGRATE_INITIATION = "internal.gv2.disable_automigrate_initiation";
|
||||||
public static final String GV2_DISABLE_AUTOMIGRATE_NOTIFICATION = "internal.gv2.disable_automigrate_notification";
|
public static final String GV2_DISABLE_AUTOMIGRATE_NOTIFICATION = "internal.gv2.disable_automigrate_notification";
|
||||||
public static final String RECIPIENT_DETAILS = "internal.recipient_details";
|
public static final String RECIPIENT_DETAILS = "internal.recipient_details";
|
||||||
public static final String FORCE_CENSORSHIP = "internal.force_censorship";
|
public static final String ALLOW_CENSORSHIP_SETTING = "internal.force_censorship";
|
||||||
public static final String FORCE_BUILT_IN_EMOJI = "internal.force_built_in_emoji";
|
public static final String FORCE_BUILT_IN_EMOJI = "internal.force_built_in_emoji";
|
||||||
public static final String REMOVE_SENDER_KEY_MINIMUM = "internal.remove_sender_key_minimum";
|
public static final String REMOVE_SENDER_KEY_MINIMUM = "internal.remove_sender_key_minimum";
|
||||||
public static final String DELAY_RESENDS = "internal.delay_resends";
|
public static final String DELAY_RESENDS = "internal.delay_resends";
|
||||||
|
@ -84,10 +84,10 @@ public final class InternalValues extends SignalStoreValues {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force the app to behave as if it is in a country where Signal is censored.
|
* Allow changing the censorship circumvention setting regardless of network status.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean forcedCensorship() {
|
public synchronized boolean allowChangingCensorshipSetting() {
|
||||||
return FeatureFlags.internalUser() && getBoolean(FORCE_CENSORSHIP, false);
|
return FeatureFlags.internalUser() && getBoolean(ALLOW_CENSORSHIP_SETTING, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -65,6 +65,7 @@ public final class SettingsValues extends SignalStoreValues {
|
||||||
private static final String DEFAULT_SMS = "settings.default_sms";
|
private static final String DEFAULT_SMS = "settings.default_sms";
|
||||||
private static final String UNIVERSAL_EXPIRE_TIMER = "settings.universal.expire.timer";
|
private static final String UNIVERSAL_EXPIRE_TIMER = "settings.universal.expire.timer";
|
||||||
private static final String SENT_MEDIA_QUALITY = "settings.sentMediaQuality";
|
private static final String SENT_MEDIA_QUALITY = "settings.sentMediaQuality";
|
||||||
|
private static final String CENSORSHIP_CIRCUMVENTION_ENABLED = "settings.censorshipCircumventionEnabled";
|
||||||
|
|
||||||
private final SingleLiveEvent<String> onConfigurationSettingChanged = new SingleLiveEvent<>();
|
private final SingleLiveEvent<String> onConfigurationSettingChanged = new SingleLiveEvent<>();
|
||||||
|
|
||||||
|
@ -390,6 +391,14 @@ public final class SettingsValues extends SignalStoreValues {
|
||||||
return SentMediaQuality.fromCode(getInteger(SENT_MEDIA_QUALITY, SentMediaQuality.STANDARD.getCode()));
|
return SentMediaQuality.fromCode(getInteger(SENT_MEDIA_QUALITY, SentMediaQuality.STANDARD.getCode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NonNull CensorshipCircumventionEnabled getCensorshipCircumventionEnabled() {
|
||||||
|
return CensorshipCircumventionEnabled.deserialize(getInteger(CENSORSHIP_CIRCUMVENTION_ENABLED, CensorshipCircumventionEnabled.DEFAULT.serialize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCensorshipCircumventionEnabled(boolean enabled) {
|
||||||
|
putInteger(CENSORSHIP_CIRCUMVENTION_ENABLED, enabled ? CensorshipCircumventionEnabled.ENABLED.serialize() : CensorshipCircumventionEnabled.DISABLED.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
private @Nullable Uri getUri(@NonNull String key) {
|
private @Nullable Uri getUri(@NonNull String key) {
|
||||||
String uri = getString(key, "");
|
String uri = getString(key, "");
|
||||||
|
|
||||||
|
@ -399,4 +408,27 @@ public final class SettingsValues extends SignalStoreValues {
|
||||||
return Uri.parse(uri);
|
return Uri.parse(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum CensorshipCircumventionEnabled {
|
||||||
|
DEFAULT(0), ENABLED(1), DISABLED(2);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
CensorshipCircumventionEnabled(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CensorshipCircumventionEnabled deserialize(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0: return DEFAULT;
|
||||||
|
case 1: return ENABLED;
|
||||||
|
case 2: return DISABLED;
|
||||||
|
default: throw new IllegalArgumentException("Bad value: " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int serialize() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,344 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.push;
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.BuildConfig;
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
import org.thoughtcrime.securesms.net.CustomDns;
|
|
||||||
import org.thoughtcrime.securesms.net.DeprecatedClientPreventionInterceptor;
|
|
||||||
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor;
|
|
||||||
import org.thoughtcrime.securesms.net.RemoteDeprecationDetectorInterceptor;
|
|
||||||
import org.thoughtcrime.securesms.net.SequentialDns;
|
|
||||||
import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor;
|
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
|
||||||
import org.whispersystems.signalservice.api.push.TrustStore;
|
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl;
|
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalCdshUrl;
|
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl;
|
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl;
|
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl;
|
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import okhttp3.CipherSuite;
|
|
||||||
import okhttp3.ConnectionSpec;
|
|
||||||
import okhttp3.Dns;
|
|
||||||
import okhttp3.Interceptor;
|
|
||||||
import okhttp3.TlsVersion;
|
|
||||||
|
|
||||||
public class SignalServiceNetworkAccess {
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static final String TAG = Log.tag(SignalServiceNetworkAccess.class);
|
|
||||||
|
|
||||||
public static final Dns DNS = new SequentialDns(Dns.SYSTEM, new CustomDns("1.1.1.1"));
|
|
||||||
|
|
||||||
private static final String COUNTRY_CODE_EGYPT = "+20";
|
|
||||||
private static final String COUNTRY_CODE_UAE = "+971";
|
|
||||||
private static final String COUNTRY_CODE_OMAN = "+968";
|
|
||||||
private static final String COUNTRY_CODE_QATAR = "+974";
|
|
||||||
private static final String COUNTRY_CODE_IRAN = "+98";
|
|
||||||
private static final String COUNTRY_CODE_CUBA = "+53";
|
|
||||||
private static final String COUNTRY_CODE_UZBEKISTAN = "+998";
|
|
||||||
|
|
||||||
private static final String SERVICE_REFLECTOR_HOST = "europe-west1-signal-cdn-reflector.cloudfunctions.net";
|
|
||||||
private static final String SERVICE_FASTLY_HOST = "textsecure-service.whispersystems.org.global.prod.fastly.net";
|
|
||||||
private static final String STORAGE_FASTLY_HOST = "storage.signal.org.global.prod.fastly.net";
|
|
||||||
private static final String CDN_FASTLY_HOST = "cdn.signal.org.global.prod.fastly.net";
|
|
||||||
private static final String CDN2_FASTLY_HOST = "cdn2.signal.org.global.prod.fastly.net";
|
|
||||||
private static final String DIRECTORY_FASTLY_HOST = "api.directory.signal.org.global.prod.fastly.net";
|
|
||||||
private static final String KBS_FASTLY_HOST = "api.backup.signal.org.global.prod.fastly.net";
|
|
||||||
|
|
||||||
private static final ConnectionSpec GMAPS_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
|
||||||
.tlsVersions(TlsVersion.TLS_1_2)
|
|
||||||
.cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
|
||||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
||||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA)
|
|
||||||
.supportsTlsExtensions(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static final ConnectionSpec GMAIL_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
|
||||||
.tlsVersions(TlsVersion.TLS_1_2)
|
|
||||||
.cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
||||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA)
|
|
||||||
.supportsTlsExtensions(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static final ConnectionSpec PLAY_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
|
||||||
.tlsVersions(TlsVersion.TLS_1_2)
|
|
||||||
.cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
||||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA)
|
|
||||||
.supportsTlsExtensions(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static final ConnectionSpec APP_CONNECTION_SPEC = ConnectionSpec.MODERN_TLS;
|
|
||||||
|
|
||||||
private final Map<String, SignalServiceConfiguration> censorshipConfiguration;
|
|
||||||
private final String[] censoredCountries;
|
|
||||||
private final SignalServiceConfiguration uncensoredConfiguration;
|
|
||||||
|
|
||||||
public SignalServiceNetworkAccess(Context context) {
|
|
||||||
|
|
||||||
final TrustStore trustStore = new DomainFrontingTrustStore(context);
|
|
||||||
final SignalServiceUrl baseGoogleService = new SignalServiceUrl("https://www.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalServiceUrl baseAndroidService = new SignalServiceUrl("https://android.clients.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC);
|
|
||||||
final SignalServiceUrl mapsOneAndroidService = new SignalServiceUrl("https://clients3.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalServiceUrl mapsTwoAndroidService = new SignalServiceUrl("https://clients4.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalServiceUrl mailAndroidService = new SignalServiceUrl("https://inbox.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalServiceUrl egyptGoogleService = new SignalServiceUrl("https://www.google.com.eg/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalServiceUrl uaeGoogleService = new SignalServiceUrl("https://www.google.ae/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalServiceUrl omanGoogleService = new SignalServiceUrl("https://www.google.com.om/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalServiceUrl qatarGoogleService = new SignalServiceUrl("https://www.google.com.qa/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalServiceUrl uzbekistanGoogleService = new SignalServiceUrl("https://www.google.co.uz/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
|
|
||||||
final SignalCdnUrl baseGoogleCdn = new SignalCdnUrl("https://www.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl baseAndroidCdn = new SignalCdnUrl("https://android.clients.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl mapsOneAndroidCdn = new SignalCdnUrl("https://clients3.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl mapsTwoAndroidCdn = new SignalCdnUrl("https://clients4.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl mailAndroidCdn = new SignalCdnUrl("https://inbox.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl egyptGoogleCdn = new SignalCdnUrl("https://www.google.com.eg/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl uaeGoogleCdn = new SignalCdnUrl("https://www.google.ae/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl omanGoogleCdn = new SignalCdnUrl("https://www.google.com.om/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl qatarGoogleCdn = new SignalCdnUrl("https://www.google.com.qa/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl uzbekistanGoogleCdn = new SignalCdnUrl("https://www.google.co.uz/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
|
|
||||||
final SignalCdnUrl baseGoogleCdn2 = new SignalCdnUrl("https://www.google.com/cdn2", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl baseAndroidCdn2 = new SignalCdnUrl("https://android.clients.google.com/cdn2", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl mapsOneAndroidCdn2 = new SignalCdnUrl("https://clients3.google.com/cdn2", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl mapsTwoAndroidCdn2 = new SignalCdnUrl("https://clients4.google.com/cdn2", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl mailAndroidCdn2 = new SignalCdnUrl("https://inbox.google.com/cdn2", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl egyptGoogleCdn2 = new SignalCdnUrl("https://www.google.com.eg/cdn2", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl uaeGoogleCdn2 = new SignalCdnUrl("https://www.google.ae/cdn2", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl omanGoogleCdn2 = new SignalCdnUrl("https://www.google.com.om/cdn2", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl qatarGoogleCdn2 = new SignalCdnUrl("https://www.google.com.qa/cdn2", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalCdnUrl uzbekistanGoogleCdn2 = new SignalCdnUrl("https://www.google.co.uz/cdn2", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
|
|
||||||
final SignalContactDiscoveryUrl baseGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalContactDiscoveryUrl baseAndroidDiscovery = new SignalContactDiscoveryUrl("https://android.clients.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC);
|
|
||||||
final SignalContactDiscoveryUrl mapsOneAndroidDiscovery = new SignalContactDiscoveryUrl("https://clients3.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalContactDiscoveryUrl mapsTwoAndroidDiscovery = new SignalContactDiscoveryUrl("https://clients4.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalContactDiscoveryUrl mailAndroidDiscovery = new SignalContactDiscoveryUrl("https://inbox.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalContactDiscoveryUrl egyptGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.eg/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalContactDiscoveryUrl uaeGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.ae/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalContactDiscoveryUrl omanGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.om/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalContactDiscoveryUrl qatarGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.qa/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalContactDiscoveryUrl uzbekistanGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.co.uz/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
|
|
||||||
final SignalKeyBackupServiceUrl baseGoogleKbs = new SignalKeyBackupServiceUrl("https://www.google.com/backup", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalKeyBackupServiceUrl baseAndroidKbs = new SignalKeyBackupServiceUrl("https://android.clients.google.com/backup", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC);
|
|
||||||
final SignalKeyBackupServiceUrl mapsOneAndroidKbs = new SignalKeyBackupServiceUrl("https://clients3.google.com/backup", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalKeyBackupServiceUrl mapsTwoAndroidKbs = new SignalKeyBackupServiceUrl("https://clients4.google.com/backup", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalKeyBackupServiceUrl mailAndroidKbs = new SignalKeyBackupServiceUrl("https://inbox.google.com/backup", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalKeyBackupServiceUrl egyptGoogleKbs = new SignalKeyBackupServiceUrl("https://www.google.com.eg/backup", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalKeyBackupServiceUrl uaeGoogleKbs = new SignalKeyBackupServiceUrl("https://www.google.ae/backup", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalKeyBackupServiceUrl omanGoogleKbs = new SignalKeyBackupServiceUrl("https://www.google.com.om/backup", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalKeyBackupServiceUrl qatarGoogleKbs = new SignalKeyBackupServiceUrl("https://www.google.com.qa/backup", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalKeyBackupServiceUrl uzbekistanGoogleKbs = new SignalKeyBackupServiceUrl("https://www.google.com.qa/backup", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
|
|
||||||
final SignalStorageUrl baseGoogleStorage = new SignalStorageUrl("https://www.google.com/storage", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalStorageUrl baseAndroidStorage = new SignalStorageUrl("https://android.clients.google.com/storage", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC);
|
|
||||||
final SignalStorageUrl mapsOneAndroidStorage = new SignalStorageUrl("https://clients3.google.com/storage", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalStorageUrl mapsTwoAndroidStorage = new SignalStorageUrl("https://clients4.google.com/storage", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC);
|
|
||||||
final SignalStorageUrl mailAndroidStorage = new SignalStorageUrl("https://inbox.google.com/storage", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalStorageUrl egyptGoogleStorage = new SignalStorageUrl("https://www.google.com.eg/storage", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalStorageUrl uaeGoogleStorage = new SignalStorageUrl("https://www.google.ae/storage", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalStorageUrl omanGoogleStorage = new SignalStorageUrl("https://www.google.com.om/storage", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalStorageUrl qatarGoogleStorage = new SignalStorageUrl("https://www.google.com.qa/storage", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
final SignalStorageUrl uzbekistanGoogleStorage = new SignalStorageUrl("https://www.google.com.qa/storage", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC);
|
|
||||||
|
|
||||||
final String[] fastUrls = {"https://cdn.sstatic.net", "https://github.githubassets.com", "https://pinterest.com", "https://open.scdn.co", "https://www.redditstatic.com"};
|
|
||||||
|
|
||||||
final List<Interceptor> interceptors = Arrays.asList(new StandardUserAgentInterceptor(),
|
|
||||||
new RemoteDeprecationDetectorInterceptor(),
|
|
||||||
new DeprecatedClientPreventionInterceptor(),
|
|
||||||
DeviceTransferBlockingInterceptor.getInstance());
|
|
||||||
final Optional<Dns> dns = Optional.of(DNS);
|
|
||||||
|
|
||||||
final byte[] zkGroupServerPublicParams;
|
|
||||||
|
|
||||||
try {
|
|
||||||
zkGroupServerPublicParams = Base64.decode(BuildConfig.ZKGROUP_SERVER_PUBLIC_PARAMS);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.censorshipConfiguration = new HashMap<String, SignalServiceConfiguration>() {{
|
|
||||||
|
|
||||||
put(COUNTRY_CODE_EGYPT, new SignalServiceConfiguration(new SignalServiceUrl[] {egyptGoogleService, baseGoogleService, baseAndroidService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService},
|
|
||||||
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {egyptGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn, mailAndroidCdn},
|
|
||||||
new SignalCdnUrl[] {egyptGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2, mailAndroidCdn2}),
|
|
||||||
new SignalContactDiscoveryUrl[] {egyptGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery},
|
|
||||||
new SignalKeyBackupServiceUrl[] {egyptGoogleKbs, baseGoogleKbs, baseAndroidKbs, mapsOneAndroidKbs, mapsTwoAndroidKbs, mailAndroidKbs},
|
|
||||||
new SignalStorageUrl[] {egyptGoogleStorage, baseGoogleStorage, baseAndroidStorage, mapsOneAndroidStorage, mapsTwoAndroidStorage, mailAndroidStorage},
|
|
||||||
new SignalCdshUrl[] {new SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, new SignalServiceTrustStore(context))},
|
|
||||||
interceptors,
|
|
||||||
dns,
|
|
||||||
Optional.absent(),
|
|
||||||
zkGroupServerPublicParams));
|
|
||||||
|
|
||||||
put(COUNTRY_CODE_UAE, new SignalServiceConfiguration(new SignalServiceUrl[] {uaeGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService},
|
|
||||||
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {uaeGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
|
|
||||||
new SignalCdnUrl[] {uaeGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2}),
|
|
||||||
new SignalContactDiscoveryUrl[] {uaeGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery},
|
|
||||||
new SignalKeyBackupServiceUrl[] {uaeGoogleKbs, baseGoogleKbs, baseAndroidKbs, mapsOneAndroidKbs, mapsTwoAndroidKbs, mailAndroidKbs},
|
|
||||||
new SignalStorageUrl[] {uaeGoogleStorage, baseGoogleStorage, baseAndroidStorage, mapsOneAndroidStorage, mapsTwoAndroidStorage, mailAndroidStorage},
|
|
||||||
new SignalCdshUrl[] {new SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, new SignalServiceTrustStore(context))},
|
|
||||||
interceptors,
|
|
||||||
dns,
|
|
||||||
Optional.absent(),
|
|
||||||
zkGroupServerPublicParams));
|
|
||||||
|
|
||||||
put(COUNTRY_CODE_OMAN, new SignalServiceConfiguration(new SignalServiceUrl[] {omanGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService},
|
|
||||||
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {omanGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
|
|
||||||
new SignalCdnUrl[] {omanGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2}),
|
|
||||||
new SignalContactDiscoveryUrl[] {omanGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery},
|
|
||||||
new SignalKeyBackupServiceUrl[] {omanGoogleKbs, baseGoogleKbs, baseAndroidKbs, mapsOneAndroidKbs, mapsTwoAndroidKbs, mailAndroidKbs},
|
|
||||||
new SignalStorageUrl[] {omanGoogleStorage, baseGoogleStorage, baseAndroidStorage, mapsOneAndroidStorage, mapsTwoAndroidStorage, mailAndroidStorage},
|
|
||||||
new SignalCdshUrl[] {new SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, new SignalServiceTrustStore(context))},
|
|
||||||
interceptors,
|
|
||||||
dns,
|
|
||||||
Optional.absent(),
|
|
||||||
zkGroupServerPublicParams));
|
|
||||||
|
|
||||||
|
|
||||||
put(COUNTRY_CODE_QATAR, new SignalServiceConfiguration(new SignalServiceUrl[] {qatarGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService},
|
|
||||||
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {qatarGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
|
|
||||||
new SignalCdnUrl[] {qatarGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2}),
|
|
||||||
new SignalContactDiscoveryUrl[] {qatarGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery},
|
|
||||||
new SignalKeyBackupServiceUrl[] {qatarGoogleKbs, baseGoogleKbs, baseAndroidKbs, mapsOneAndroidKbs, mapsTwoAndroidKbs, mailAndroidKbs},
|
|
||||||
new SignalStorageUrl[] {qatarGoogleStorage, baseGoogleStorage, baseAndroidStorage, mapsOneAndroidStorage, mapsTwoAndroidStorage, mailAndroidStorage},
|
|
||||||
new SignalCdshUrl[] {new SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, new SignalServiceTrustStore(context))},
|
|
||||||
interceptors,
|
|
||||||
dns,
|
|
||||||
Optional.absent(),
|
|
||||||
zkGroupServerPublicParams));
|
|
||||||
|
|
||||||
put(COUNTRY_CODE_IRAN, new SignalServiceConfiguration(Stream.of(fastUrls).map(url -> new SignalServiceUrl(url, SERVICE_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalServiceUrl[]::new),
|
|
||||||
makeSignalCdnUrlMapFor(Stream.of(fastUrls).map(url -> new SignalCdnUrl(url, CDN_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalCdnUrl[]::new),
|
|
||||||
Stream.of(fastUrls).map(url -> new SignalCdnUrl(url, CDN2_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalCdnUrl[]::new)),
|
|
||||||
Stream.of(fastUrls).map(url -> new SignalContactDiscoveryUrl(url, DIRECTORY_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalContactDiscoveryUrl[]::new),
|
|
||||||
Stream.of(fastUrls).map(url -> new SignalKeyBackupServiceUrl(url, KBS_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalKeyBackupServiceUrl[]::new),
|
|
||||||
Stream.of(fastUrls).map(url -> new SignalStorageUrl(url, STORAGE_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalStorageUrl[]::new),
|
|
||||||
new SignalCdshUrl[] {new SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, new SignalServiceTrustStore(context))},
|
|
||||||
interceptors,
|
|
||||||
dns,
|
|
||||||
Optional.absent(),
|
|
||||||
zkGroupServerPublicParams));
|
|
||||||
|
|
||||||
put(COUNTRY_CODE_CUBA, new SignalServiceConfiguration(Stream.of(fastUrls).map(url -> new SignalServiceUrl(url, SERVICE_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalServiceUrl[]::new),
|
|
||||||
makeSignalCdnUrlMapFor(Stream.of(fastUrls).map(url -> new SignalCdnUrl(url, CDN_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalCdnUrl[]::new),
|
|
||||||
Stream.of(fastUrls).map(url -> new SignalCdnUrl(url, CDN2_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalCdnUrl[]::new)),
|
|
||||||
Stream.of(fastUrls).map(url -> new SignalContactDiscoveryUrl(url, DIRECTORY_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalContactDiscoveryUrl[]::new),
|
|
||||||
Stream.of(fastUrls).map(url -> new SignalKeyBackupServiceUrl(url, KBS_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalKeyBackupServiceUrl[]::new),
|
|
||||||
Stream.of(fastUrls).map(url -> new SignalStorageUrl(url, STORAGE_FASTLY_HOST, new DomainFrontingDigicertTrustStore(context), APP_CONNECTION_SPEC)).toArray(SignalStorageUrl[]::new),
|
|
||||||
new SignalCdshUrl[] {new SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, new SignalServiceTrustStore(context))},
|
|
||||||
interceptors,
|
|
||||||
dns,
|
|
||||||
Optional.absent(),
|
|
||||||
zkGroupServerPublicParams));
|
|
||||||
|
|
||||||
put(COUNTRY_CODE_UZBEKISTAN, new SignalServiceConfiguration(new SignalServiceUrl[] {uzbekistanGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService},
|
|
||||||
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {uzbekistanGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
|
|
||||||
new SignalCdnUrl[] {uzbekistanGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2}),
|
|
||||||
new SignalContactDiscoveryUrl[] {uzbekistanGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery},
|
|
||||||
new SignalKeyBackupServiceUrl[] {uzbekistanGoogleKbs, baseGoogleKbs, baseAndroidKbs, mapsOneAndroidKbs, mapsTwoAndroidKbs, mailAndroidKbs},
|
|
||||||
new SignalStorageUrl[] {uzbekistanGoogleStorage, baseGoogleStorage, baseAndroidStorage, mapsOneAndroidStorage, mapsTwoAndroidStorage, mailAndroidStorage},
|
|
||||||
new SignalCdshUrl[] {new SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, new SignalServiceTrustStore(context))},
|
|
||||||
interceptors,
|
|
||||||
dns,
|
|
||||||
Optional.absent(),
|
|
||||||
zkGroupServerPublicParams));
|
|
||||||
}};
|
|
||||||
|
|
||||||
this.uncensoredConfiguration = new SignalServiceConfiguration(new SignalServiceUrl[] {new SignalServiceUrl(BuildConfig.SIGNAL_URL, new SignalServiceTrustStore(context))},
|
|
||||||
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {new SignalCdnUrl(BuildConfig.SIGNAL_CDN_URL, new SignalServiceTrustStore(context))},
|
|
||||||
new SignalCdnUrl[] {new SignalCdnUrl(BuildConfig.SIGNAL_CDN2_URL, new SignalServiceTrustStore(context))}),
|
|
||||||
new SignalContactDiscoveryUrl[] {new SignalContactDiscoveryUrl(BuildConfig.SIGNAL_CONTACT_DISCOVERY_URL, new SignalServiceTrustStore(context))},
|
|
||||||
new SignalKeyBackupServiceUrl[] { new SignalKeyBackupServiceUrl(BuildConfig.SIGNAL_KEY_BACKUP_URL, new SignalServiceTrustStore(context)) },
|
|
||||||
new SignalStorageUrl[] {new SignalStorageUrl(BuildConfig.STORAGE_URL, new SignalServiceTrustStore(context))},
|
|
||||||
new SignalCdshUrl[] {new SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, new SignalServiceTrustStore(context))},
|
|
||||||
interceptors,
|
|
||||||
dns,
|
|
||||||
SignalStore.proxy().isProxyEnabled() ? Optional.of(SignalStore.proxy().getProxy()) : Optional.absent(),
|
|
||||||
zkGroupServerPublicParams);
|
|
||||||
|
|
||||||
this.censoredCountries = this.censorshipConfiguration.keySet().toArray(new String[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignalServiceConfiguration getConfiguration() {
|
|
||||||
String localNumber = SignalStore.account().getE164();
|
|
||||||
return getConfiguration(localNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignalServiceConfiguration getConfiguration(@Nullable String localNumber) {
|
|
||||||
if (localNumber == null || SignalStore.proxy().isProxyEnabled()) {
|
|
||||||
return this.uncensoredConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SignalStore.internalValues().forcedCensorship()) {
|
|
||||||
return this.censorshipConfiguration.get(COUNTRY_CODE_IRAN);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String censoredRegion : this.censoredCountries) {
|
|
||||||
if (localNumber.startsWith(censoredRegion)) {
|
|
||||||
return this.censorshipConfiguration.get(censoredRegion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.uncensoredConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCensored() {
|
|
||||||
return getConfiguration() != this.uncensoredConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCensored(String number) {
|
|
||||||
return getConfiguration(number) != this.uncensoredConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<Integer, SignalCdnUrl[]> makeSignalCdnUrlMapFor(SignalCdnUrl[] cdn0Urls, SignalCdnUrl[] cdn2Urls) {
|
|
||||||
Map<Integer, SignalCdnUrl[]> result = new HashMap<>();
|
|
||||||
result.put(0, cdn0Urls);
|
|
||||||
result.put(2, cdn2Urls);
|
|
||||||
return Collections.unmodifiableMap(result);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,279 @@
|
||||||
|
package org.thoughtcrime.securesms.push
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import okhttp3.CipherSuite
|
||||||
|
import okhttp3.ConnectionSpec
|
||||||
|
import okhttp3.Dns
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.TlsVersion
|
||||||
|
import org.signal.core.util.logging.Log
|
||||||
|
import org.thoughtcrime.securesms.BuildConfig
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SettingsValues
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.net.CustomDns
|
||||||
|
import org.thoughtcrime.securesms.net.DeprecatedClientPreventionInterceptor
|
||||||
|
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor
|
||||||
|
import org.thoughtcrime.securesms.net.RemoteDeprecationDetectorInterceptor
|
||||||
|
import org.thoughtcrime.securesms.net.SequentialDns
|
||||||
|
import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor
|
||||||
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||||
|
import org.thoughtcrime.securesms.util.Base64
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional
|
||||||
|
import org.whispersystems.signalservice.api.push.TrustStore
|
||||||
|
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl
|
||||||
|
import org.whispersystems.signalservice.internal.configuration.SignalCdshUrl
|
||||||
|
import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl
|
||||||
|
import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl
|
||||||
|
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
|
||||||
|
import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl
|
||||||
|
import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a [SignalServiceConfiguration] to be used with our service layer.
|
||||||
|
* If you're looking for a place to start, look at [getConfiguration].
|
||||||
|
*/
|
||||||
|
class SignalServiceNetworkAccess(context: Context) {
|
||||||
|
companion object {
|
||||||
|
private val TAG = Log.tag(SignalServiceNetworkAccess::class.java)
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val DNS: Dns = SequentialDns(Dns.SYSTEM, CustomDns("1.1.1.1"))
|
||||||
|
|
||||||
|
private const val COUNTRY_CODE_EGYPT = 20
|
||||||
|
private const val COUNTRY_CODE_UAE = 971
|
||||||
|
private const val COUNTRY_CODE_OMAN = 968
|
||||||
|
private const val COUNTRY_CODE_QATAR = 974
|
||||||
|
private const val COUNTRY_CODE_IRAN = 98
|
||||||
|
private const val COUNTRY_CODE_CUBA = 53
|
||||||
|
private const val COUNTRY_CODE_UZBEKISTAN = 998
|
||||||
|
private const val COUNTRY_CODE_UKRAINE = 380
|
||||||
|
|
||||||
|
private const val G_HOST = "europe-west1-signal-cdn-reflector.cloudfunctions.net"
|
||||||
|
private const val F_SERVICE_HOST = "textsecure-service.whispersystems.org.global.prod.fastly.net"
|
||||||
|
private const val F_STORAGE_HOST = "storage.signal.org.global.prod.fastly.net"
|
||||||
|
private const val F_CDN_HOST = "cdn.signal.org.global.prod.fastly.net"
|
||||||
|
private const val F_CDN2_HOST = "cdn2.signal.org.global.prod.fastly.net"
|
||||||
|
private const val F_DIRECTORY_HOST = "api.directory.signal.org.global.prod.fastly.net"
|
||||||
|
private const val F_KBS_HOST = "api.backup.signal.org.global.prod.fastly.net"
|
||||||
|
|
||||||
|
private val GMAPS_CONNECTION_SPEC = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||||
|
.tlsVersions(TlsVersion.TLS_1_2)
|
||||||
|
.cipherSuites(
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA
|
||||||
|
)
|
||||||
|
.supportsTlsExtensions(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val GMAIL_CONNECTION_SPEC = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||||
|
.tlsVersions(TlsVersion.TLS_1_2)
|
||||||
|
.cipherSuites(
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA
|
||||||
|
)
|
||||||
|
.supportsTlsExtensions(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val PLAY_CONNECTION_SPEC = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||||
|
.tlsVersions(TlsVersion.TLS_1_2)
|
||||||
|
.cipherSuites(
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA
|
||||||
|
)
|
||||||
|
.supportsTlsExtensions(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val APP_CONNECTION_SPEC = ConnectionSpec.MODERN_TLS
|
||||||
|
}
|
||||||
|
|
||||||
|
private val serviceTrustStore: TrustStore = SignalServiceTrustStore(context)
|
||||||
|
private val gTrustStore: TrustStore = DomainFrontingTrustStore(context)
|
||||||
|
private val fTrustStore: TrustStore = DomainFrontingDigicertTrustStore(context)
|
||||||
|
|
||||||
|
private val interceptors: List<Interceptor> = listOf(
|
||||||
|
StandardUserAgentInterceptor(),
|
||||||
|
RemoteDeprecationDetectorInterceptor(),
|
||||||
|
DeprecatedClientPreventionInterceptor(),
|
||||||
|
DeviceTransferBlockingInterceptor.getInstance()
|
||||||
|
)
|
||||||
|
|
||||||
|
private val zkGroupServerPublicParams: ByteArray = try {
|
||||||
|
Base64.decode(BuildConfig.ZKGROUP_SERVER_PUBLIC_PARAMS)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw AssertionError(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val baseGHostConfigs: List<HostConfig> = listOf(
|
||||||
|
HostConfig("https://www.google.com", G_HOST, GMAIL_CONNECTION_SPEC),
|
||||||
|
HostConfig("https://android.clients.google.com", G_HOST, PLAY_CONNECTION_SPEC),
|
||||||
|
HostConfig("https://clients3.google.com", G_HOST, GMAPS_CONNECTION_SPEC),
|
||||||
|
HostConfig("https://clients4.google.com", G_HOST, GMAPS_CONNECTION_SPEC),
|
||||||
|
HostConfig("https://inbox.google.com", G_HOST, GMAIL_CONNECTION_SPEC),
|
||||||
|
)
|
||||||
|
|
||||||
|
private val fUrls = arrayOf("https://cdn.sstatic.net", "https://github.githubassets.com", "https://pinterest.com", "https://open.scdn.co", "https://www.redditstatic.com")
|
||||||
|
|
||||||
|
private val fConfig: SignalServiceConfiguration = SignalServiceConfiguration(
|
||||||
|
fUrls.map { SignalServiceUrl(it, F_SERVICE_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||||
|
mapOf(
|
||||||
|
0 to fUrls.map { SignalCdnUrl(it, F_CDN_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||||
|
2 to fUrls.map { SignalCdnUrl(it, F_CDN2_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||||
|
),
|
||||||
|
fUrls.map { SignalContactDiscoveryUrl(it, F_DIRECTORY_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||||
|
fUrls.map { SignalKeyBackupServiceUrl(it, F_KBS_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||||
|
fUrls.map { SignalStorageUrl(it, F_STORAGE_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||||
|
arrayOf(SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, serviceTrustStore)),
|
||||||
|
interceptors,
|
||||||
|
Optional.of(DNS),
|
||||||
|
Optional.absent(),
|
||||||
|
zkGroupServerPublicParams
|
||||||
|
)
|
||||||
|
|
||||||
|
private val censorshipConfiguration: Map<Int, SignalServiceConfiguration> = mapOf(
|
||||||
|
COUNTRY_CODE_EGYPT to buildGConfiguration(
|
||||||
|
listOf(HostConfig("https://www.google.com.eg", G_HOST, GMAIL_CONNECTION_SPEC)) + baseGHostConfigs,
|
||||||
|
),
|
||||||
|
COUNTRY_CODE_UAE to buildGConfiguration(
|
||||||
|
listOf(HostConfig("https://www.google.ae", G_HOST, GMAIL_CONNECTION_SPEC)) + baseGHostConfigs,
|
||||||
|
),
|
||||||
|
COUNTRY_CODE_OMAN to buildGConfiguration(
|
||||||
|
listOf(HostConfig("https://www.google.com.om", G_HOST, GMAIL_CONNECTION_SPEC)) + baseGHostConfigs,
|
||||||
|
),
|
||||||
|
COUNTRY_CODE_QATAR to buildGConfiguration(
|
||||||
|
listOf(HostConfig("https://www.google.com.qa", G_HOST, GMAIL_CONNECTION_SPEC)) + baseGHostConfigs,
|
||||||
|
),
|
||||||
|
COUNTRY_CODE_UZBEKISTAN to buildGConfiguration(
|
||||||
|
listOf(HostConfig("https://www.google.co.uz", G_HOST, GMAIL_CONNECTION_SPEC)) + baseGHostConfigs,
|
||||||
|
),
|
||||||
|
COUNTRY_CODE_UKRAINE to buildGConfiguration(
|
||||||
|
listOf(HostConfig("https://www.google.com.ua", G_HOST, GMAIL_CONNECTION_SPEC)) + baseGHostConfigs,
|
||||||
|
),
|
||||||
|
COUNTRY_CODE_IRAN to fConfig,
|
||||||
|
COUNTRY_CODE_CUBA to fConfig,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val defaultCensoredConfiguration: SignalServiceConfiguration = buildGConfiguration(baseGHostConfigs)
|
||||||
|
|
||||||
|
private val defaultCensoredCountryCodes: Set<Int> = setOf(
|
||||||
|
COUNTRY_CODE_EGYPT,
|
||||||
|
COUNTRY_CODE_UAE,
|
||||||
|
COUNTRY_CODE_OMAN,
|
||||||
|
COUNTRY_CODE_QATAR,
|
||||||
|
COUNTRY_CODE_IRAN,
|
||||||
|
COUNTRY_CODE_CUBA,
|
||||||
|
COUNTRY_CODE_UZBEKISTAN,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val uncensoredConfiguration: SignalServiceConfiguration = SignalServiceConfiguration(
|
||||||
|
arrayOf(SignalServiceUrl(BuildConfig.SIGNAL_URL, serviceTrustStore)),
|
||||||
|
mapOf(
|
||||||
|
0 to arrayOf(SignalCdnUrl(BuildConfig.SIGNAL_CDN_URL, serviceTrustStore)),
|
||||||
|
2 to arrayOf(SignalCdnUrl(BuildConfig.SIGNAL_CDN2_URL, serviceTrustStore))
|
||||||
|
),
|
||||||
|
arrayOf(SignalContactDiscoveryUrl(BuildConfig.SIGNAL_CONTACT_DISCOVERY_URL, serviceTrustStore)),
|
||||||
|
arrayOf(SignalKeyBackupServiceUrl(BuildConfig.SIGNAL_KEY_BACKUP_URL, serviceTrustStore)),
|
||||||
|
arrayOf(SignalStorageUrl(BuildConfig.STORAGE_URL, serviceTrustStore)),
|
||||||
|
arrayOf(SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, serviceTrustStore)),
|
||||||
|
interceptors,
|
||||||
|
Optional.of(DNS),
|
||||||
|
if (SignalStore.proxy().isProxyEnabled) Optional.of(SignalStore.proxy().proxy) else Optional.absent(),
|
||||||
|
zkGroupServerPublicParams
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getConfiguration(): SignalServiceConfiguration {
|
||||||
|
return getConfiguration(SignalStore.account().e164)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getConfiguration(localNumber: String?): SignalServiceConfiguration {
|
||||||
|
if (localNumber == null || SignalStore.proxy().isProxyEnabled) {
|
||||||
|
return uncensoredConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
val countryCode: Int = PhoneNumberFormatter.getLocalCountryCode()
|
||||||
|
|
||||||
|
return when (SignalStore.settings().censorshipCircumventionEnabled) {
|
||||||
|
SettingsValues.CensorshipCircumventionEnabled.ENABLED -> {
|
||||||
|
censorshipConfiguration[countryCode] ?: defaultCensoredConfiguration
|
||||||
|
}
|
||||||
|
SettingsValues.CensorshipCircumventionEnabled.DISABLED -> {
|
||||||
|
uncensoredConfiguration
|
||||||
|
}
|
||||||
|
SettingsValues.CensorshipCircumventionEnabled.DEFAULT -> {
|
||||||
|
if (defaultCensoredCountryCodes.contains(countryCode)) {
|
||||||
|
censorshipConfiguration[countryCode] ?: defaultCensoredConfiguration
|
||||||
|
} else {
|
||||||
|
uncensoredConfiguration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isCensored(): Boolean {
|
||||||
|
return isCensored(SignalStore.account().e164)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isCensored(number: String?): Boolean {
|
||||||
|
return getConfiguration(number) != uncensoredConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isCountryCodeCensoredByDefault(countryCode: Int): Boolean {
|
||||||
|
return defaultCensoredCountryCodes.contains(countryCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildGConfiguration(
|
||||||
|
hostConfigs: List<HostConfig>
|
||||||
|
): SignalServiceConfiguration {
|
||||||
|
val serviceUrls: Array<SignalServiceUrl> = hostConfigs.map { SignalServiceUrl("${it.baseUrl}/service", it.host, gTrustStore, it.connectionSpec) }.toTypedArray()
|
||||||
|
val cdnUrls: Array<SignalCdnUrl> = hostConfigs.map { SignalCdnUrl("${it.baseUrl}/cdn", it.host, gTrustStore, it.connectionSpec) }.toTypedArray()
|
||||||
|
val cdn2Urls: Array<SignalCdnUrl> = hostConfigs.map { SignalCdnUrl("${it.baseUrl}/cdn2", it.host, gTrustStore, it.connectionSpec) }.toTypedArray()
|
||||||
|
val cdsUrls: Array<SignalContactDiscoveryUrl> = hostConfigs.map { SignalContactDiscoveryUrl("${it.baseUrl}/directory", it.host, gTrustStore, it.connectionSpec) }.toTypedArray()
|
||||||
|
val kbsUrls: Array<SignalKeyBackupServiceUrl> = hostConfigs.map { SignalKeyBackupServiceUrl("${it.baseUrl}/backup", it.host, gTrustStore, it.connectionSpec) }.toTypedArray()
|
||||||
|
val storageUrls: Array<SignalStorageUrl> = hostConfigs.map { SignalStorageUrl("${it.baseUrl}/storage", it.host, gTrustStore, it.connectionSpec) }.toTypedArray()
|
||||||
|
val cdshUrls: Array<SignalCdshUrl> = listOf(SignalCdshUrl(BuildConfig.SIGNAL_CDSH_URL, serviceTrustStore)).toTypedArray()
|
||||||
|
|
||||||
|
return SignalServiceConfiguration(
|
||||||
|
serviceUrls,
|
||||||
|
mapOf(
|
||||||
|
0 to cdnUrls,
|
||||||
|
2 to cdn2Urls
|
||||||
|
),
|
||||||
|
cdsUrls,
|
||||||
|
kbsUrls,
|
||||||
|
storageUrls,
|
||||||
|
cdshUrls,
|
||||||
|
interceptors,
|
||||||
|
Optional.of(DNS),
|
||||||
|
Optional.absent(),
|
||||||
|
zkGroupServerPublicParams
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class HostConfig(val baseUrl: String, val host: String, val connectionSpec: ConnectionSpec)
|
||||||
|
}
|
|
@ -152,6 +152,16 @@
|
||||||
<string name="CameraContacts_invite_a_contact_to_join_signal">Invite a contact to join Signal</string>
|
<string name="CameraContacts_invite_a_contact_to_join_signal">Invite a contact to join Signal</string>
|
||||||
<string name="CameraContacts__menu_search">Search</string>
|
<string name="CameraContacts__menu_search">Search</string>
|
||||||
|
|
||||||
|
<!-- Censorship Circumvention Megaphone -->
|
||||||
|
<!-- Title for an alert that shows at the bottom of the chat list letting people know that circumvention is no longer needed -->
|
||||||
|
<string name="CensorshipCircumventionMegaphone_turn_off_censorship_circumvention">Turn off censorship circumvention?</string>
|
||||||
|
<!-- Body for an alert that shows at the bottom of the chat list letting people know that circumvention is no longer needed -->
|
||||||
|
<string name="CensorshipCircumventionMegaphone_you_can_now_connect_to_the_signal_service">You can now connect to the Signal service directly for a better experience.</string>
|
||||||
|
<!-- Action to prompt the user to disable circumvention since it is no longer needed -->
|
||||||
|
<string name="CensorshipCircumventionMegaphone_turn_off">Turn off</string>
|
||||||
|
<!-- Action to prompt the user to dismiss the alert at the bottom of the chat list -->
|
||||||
|
<string name="CensorshipCircumventionMegaphone_no_thanks">No thanks</string>
|
||||||
|
|
||||||
<!-- ClearProfileActivity -->
|
<!-- ClearProfileActivity -->
|
||||||
<string name="ClearProfileActivity_remove">Remove</string>
|
<string name="ClearProfileActivity_remove">Remove</string>
|
||||||
<string name="ClearProfileActivity_remove_profile_photo">Remove profile photo?</string>
|
<string name="ClearProfileActivity_remove_profile_photo">Remove profile photo?</string>
|
||||||
|
@ -2536,6 +2546,19 @@
|
||||||
<string name="preferences_chats__message_text_size">Message font size</string>
|
<string name="preferences_chats__message_text_size">Message font size</string>
|
||||||
<string name="preferences_events__contact_joined_signal">Contact joined Signal</string>
|
<string name="preferences_events__contact_joined_signal">Contact joined Signal</string>
|
||||||
<string name="preferences_notifications__priority">Priority</string>
|
<string name="preferences_notifications__priority">Priority</string>
|
||||||
|
<!-- Heading for the 'censorship circumvention' section of privacy preferences -->
|
||||||
|
<string name="preferences_communication__category_censorship_circumvention">Censorship circumvention</string>
|
||||||
|
<!-- Title of the 'censorship circumvention' toggle switch -->
|
||||||
|
<string name="preferences_communication__censorship_circumvention">Censorship circumvention</string>
|
||||||
|
<string name="preferences_communication__censorship_circumvention_if_enabled_signal_will_attempt_to_circumvent_censorship">If enabled, Signal will attempt to circumvent censorship. Do not enable this feature unless you are in a location where Signal is censored.</string>
|
||||||
|
<!-- Summary text for 'censorship circumvention' toggle. Indicates that we automatically enabled it because we believe you're in a censored country -->
|
||||||
|
<string name="preferences_communication__censorship_circumvention_has_been_activated_based_on_your_accounts_phone_number">Censorship circumvention has been activated based on your account\'s phone number.</string>
|
||||||
|
<!-- Summary text for 'censorship circumvention' toggle. Indicates that you disabled it even though we believe you're in a censored country -->
|
||||||
|
<string name="preferences_communication__censorship_circumvention_you_have_manually_disabled">You have manually disabled censorship circumvention.</string>
|
||||||
|
<!-- Summary text for 'censorship circumvention' toggle. Indicates that you cannot use it because you're already connected to the Signal service -->
|
||||||
|
<string name="preferences_communication__censorship_circumvention_is_not_necessary_you_are_already_connected">Censorship circumvention is not necessary; you are already connected to the Signal service.</string>
|
||||||
|
<!-- Summary text for 'censorship circumvention' toggle. Indicates that you cannot use it because you're not connected to the internet -->
|
||||||
|
<string name="preferences_communication__censorship_circumvention_can_only_be_activated_when_connected_to_the_internet">Censorship circumvention can only be activated when connected to the internet.</string>
|
||||||
<string name="preferences_communication__category_sealed_sender">Sealed Sender</string>
|
<string name="preferences_communication__category_sealed_sender">Sealed Sender</string>
|
||||||
<string name="preferences_communication__sealed_sender_display_indicators">Display indicators</string>
|
<string name="preferences_communication__sealed_sender_display_indicators">Display indicators</string>
|
||||||
<string name="preferences_communication__sealed_sender_display_indicators_description">Show a status icon when you select "Message details" on messages that were delivered using sealed sender.</string>
|
<string name="preferences_communication__sealed_sender_display_indicators_description">Show a status icon when you select "Message details" on messages that were delivered using sealed sender.</string>
|
||||||
|
@ -2600,8 +2623,8 @@
|
||||||
<string name="preferences__internal_force_storage_service_sync" translatable="false">Overwrite remote data</string>
|
<string name="preferences__internal_force_storage_service_sync" translatable="false">Overwrite remote data</string>
|
||||||
<string name="preferences__internal_force_storage_service_sync_description" translatable="false">Forces remote storage to match the local device state.</string>
|
<string name="preferences__internal_force_storage_service_sync_description" translatable="false">Forces remote storage to match the local device state.</string>
|
||||||
<string name="preferences__internal_network" translatable="false">Network</string>
|
<string name="preferences__internal_network" translatable="false">Network</string>
|
||||||
<string name="preferences__internal_force_censorship" translatable="false">Force censorship</string>
|
<string name="preferences__internal_allow_censorship_toggle" translatable="false">Allow censorship circumvention toggle</string>
|
||||||
<string name="preferences__internal_force_censorship_description" translatable="false">Force the app to behave as if it is in a country where Signal is censored.</string>
|
<string name="preferences__internal_allow_censorship_toggle_description" translatable="false">Allow changing the censorship circumvention toggle regardless of network connectivity.</string>
|
||||||
<string name="preferences__internal_conversations_and_shortcuts" translatable="false">Conversations and Shortcuts</string>
|
<string name="preferences__internal_conversations_and_shortcuts" translatable="false">Conversations and Shortcuts</string>
|
||||||
<string name="preferences__internal_emoji" translatable="false">Emoji</string>
|
<string name="preferences__internal_emoji" translatable="false">Emoji</string>
|
||||||
<string name="preferences__internal_use_built_in_emoji_set" translatable="false">Use built-in emoji set</string>
|
<string name="preferences__internal_use_built_in_emoji_set" translatable="false">Use built-in emoji set</string>
|
||||||
|
|
Ładowanie…
Reference in New Issue