-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathBuildFlags.swift
More file actions
281 lines (219 loc) · 8.84 KB
/
BuildFlags.swift
File metadata and controls
281 lines (219 loc) · 8.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
//
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
import LibSignalClient
enum FeatureBuild: Int, Comparable {
case dev
case `internal`
case beta
case production
static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.rawValue < rhs.rawValue
}
}
private let build = FeatureBuild.current
// MARK: -
/// By centralizing feature flags here and documenting their rollout plan,
/// it's easier to review which feature flags are in play.
public enum BuildFlags {
public static let choochoo = build <= .internal
public static let failDebug = build <= .internal
public static let linkedPhones = build <= .internal
public static let isPrerelease = build <= .beta
public static let shouldUseTestIntervals = build <= .beta
public enum Backups {
/// This is also controlled via remote-config.
/// - SeeAlso ``RemoteConfig/backupsMegaphone``.
public static let showMegaphones = build <= .beta
public static let showOptimizeMedia = build <= .dev
public static let restoreFailOnAnyError = build <= .beta
public static let detailedBenchLogging = build <= .internal
public static let archiveErrorDisplay = build <= .internal
public static let avoidAppAttestForDevs = build <= .dev
public static let avoidStoreKitForTesters = build <= .beta
public static let mediaErrorDisplay = build <= .beta
public static let useLowerDefaultListMediaRefreshInterval = build <= .beta
}
public static let callQualitySurvey = true
static let netBuildVariant: Net.BuildVariant = build <= .beta ? .beta : .production
// Turn this off after all still-registered clients have run this
// migration. That should happen by 2026-05-27. Then, delete all the code
// that's now dead because this is false.
public static let decodeDeprecatedPreKeys = true
// Turn this off after all still-registered clients have run this
// migration. That should happen by 2026-08-04. Then, delete all the code
// that's now dead because this is false.
public static let migrateDeprecatedSessions = true
public static let serviceIdBinaryProvisioning = true
public static let serviceIdBinaryConstantOverhead = !serviceIdStrings || (build <= .internal)
public static let serviceIdBinaryVariableOverhead = !serviceIdStrings || (build <= .dev)
public static let serviceIdBinaryOneOf = !serviceIdStrings
public static let serviceIdStrings = false
public enum PinnedMessages {
public static let send = true
public static let receive = true
}
public enum MemberLabel {
public static let display = true
public static let send = build <= .beta
}
public enum KeyTransparency {
public static let enabled = build <= .dev
public static let conservativeSelfCheck = build <= .internal
}
public static let pollOneOnOneSend = build <= .internal
public enum AdminDelete {
public static let receive = true
public static let send = build <= .internal
}
}
// MARK: -
extension BuildFlags {
public static var buildVariantString: String? {
// Leaving this internal only for now. If we ever move this to
// HelpSettings we need to localize these strings
guard DebugFlags.internalSettings else {
owsFailDebug("Incomplete implementation. Needs localization")
return nil
}
let buildFlagString: String?
switch build {
case .dev:
buildFlagString = LocalizationNotNeeded("Dev")
case .internal:
buildFlagString = LocalizationNotNeeded("Internal")
case .beta:
buildFlagString = LocalizationNotNeeded("Beta")
case .production:
// Production can be inferred from the lack of flag
buildFlagString = nil
}
let configuration: String? = {
#if DEBUG
LocalizationNotNeeded("Debug")
#elseif TESTABLE_BUILD
LocalizationNotNeeded("Testable")
#else
// RELEASE can be inferred from the lack of configuration.
nil
#endif
}()
return [buildFlagString, configuration]
.compactMap { $0 }
.joined(separator: " — ")
.nilIfEmpty
}
}
// MARK: -
/// Flags that we'll leave in the code base indefinitely that are helpful for
/// development should go here, rather than cluttering up BuildFlags.
public enum DebugFlags {
public static let internalLogging = build <= .internal
public static let betaLogging = build <= .beta
public static let testPopulationErrorAlerts = build <= .beta
public static let audibleErrorLogging = build <= .internal
public static let internalSettings = build <= .internal
public static let internalMegaphoneEligible = build <= .internal
public static let verboseNotificationLogging = build <= .internal
public static let deviceTransferVerboseProgressLogging = build <= .internal
public static let messageDetailsExtraInfo = build <= .internal
public static let exposeCensorshipCircumvention = build <= .internal
public static let extraDebugLogs = build <= .internal
public static let messageSendsFail = TestableFlag(
false,
title: LocalizationNotNeeded("Message Sends Fail"),
details: LocalizationNotNeeded("All outgoing message sends will fail."),
)
public static let callingUseTestSFU = TestableFlag(
false,
title: LocalizationNotNeeded("Calling: Use Test SFU"),
details: LocalizationNotNeeded("Group calls will connect to sfu.test.voip.signal.org."),
)
public static let callingNeverRelay = TestableFlag(
false,
title: LocalizationNotNeeded("Calling: Never use relay"),
details: LocalizationNotNeeded("1:1 calls will not connect to a TURN server (remote party may still use TURN)."),
)
public static let callingForceVp9Off = TestableFlag(
false,
title: LocalizationNotNeeded("Calling: Never use VP9"),
details: LocalizationNotNeeded("1:1 calls will never use VP9 (overrides remote config)."),
)
public static let callingForceVp9On = TestableFlag(
false,
title: LocalizationNotNeeded("Calling: Always offer VP9"),
details: LocalizationNotNeeded("1:1 calls will always offer VP9 (overrides remote config and \"Never use VP9\")."),
)
public static let delayedMessageResend = TestableFlag(
false,
title: LocalizationNotNeeded("Delayed message resend"),
details: LocalizationNotNeeded("Waits 10s before responding to a resend request."),
)
public static let fastPlaceholderExpiration = TestableFlag(
false,
title: LocalizationNotNeeded("Early placeholder expiration"),
details: LocalizationNotNeeded("Shortens the valid window for message resend+recovery."),
toggleHandler: { _ in
SSKEnvironment.shared.messageDecrypterRef.cleanUpExpiredPlaceholders()
},
)
public static func allTestableFlags() -> [TestableFlag] {
return [
callingUseTestSFU,
callingNeverRelay,
callingForceVp9Off,
callingForceVp9On,
delayedMessageResend,
fastPlaceholderExpiration,
messageSendsFail,
]
}
}
// MARK: -
public class TestableFlag {
private let defaultValue: Bool
private let flag: AtomicBool
public let title: String
public let details: String
public let toggleHandler: ((Bool) -> Void)?
fileprivate init(
_ defaultValue: Bool,
title: String,
details: String,
toggleHandler: ((Bool) -> Void)? = nil,
) {
self.defaultValue = defaultValue
self.title = title
self.details = details
self.flag = AtomicBool(defaultValue, lock: .sharedGlobal)
self.toggleHandler = toggleHandler
// Normally we'd store the observer here and remove it in deinit.
// But TestableFlags are always static; they don't *get* deinitialized except in testing.
NotificationCenter.default.addObserver(
forName: Self.ResetAllTestableFlagsNotification,
object: nil,
queue: nil,
) { [weak self] _ in
guard let self else { return }
self.set(self.defaultValue)
}
}
public func get() -> Bool {
guard build <= .internal else {
return defaultValue
}
return flag.get()
}
public func set(_ value: Bool) {
flag.set(value)
toggleHandler?(value)
}
@objc
private func switchDidChange(_ sender: UISwitch) {
set(sender.isOn)
}
public var switchSelector: Selector { #selector(switchDidChange(_:)) }
public static let ResetAllTestableFlagsNotification = NSNotification.Name("ResetAllTestableFlags")
}