Skip to content

Commit e18ce70

Browse files
authoredMar 13, 2025
Bugfix FXIOS-11536 usage-deletion-request ping is missing the required usage profile_id metric field in some pings (#25282)
* FXIOS-11536 usage-deletion-request ping is missing the required usage.profile_id metric field in some pings * Move Glean init
1 parent 1d6023e commit e18ce70

File tree

4 files changed

+118
-33
lines changed

4 files changed

+118
-33
lines changed
 

‎focus-ios/Blockzilla.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
8A6445902BA3905100759319 /* disconnect-block-content.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A6445892BA3904100759319 /* disconnect-block-content.json */; };
7878
8A6445912BA3905100759319 /* disconnect-block-social.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A6445872BA3904100759319 /* disconnect-block-social.json */; };
7979
8A6445922BA3905100759319 /* web-fonts.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A6445842BA38B8600759319 /* web-fonts.json */; };
80+
8C0AB2292D817EC700E88035 /* ProfileIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C0AB2282D817EC700E88035 /* ProfileIdentifier.swift */; };
8081
8C1B5DEE2BC43C5E001B1964 /* SearchWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C1B5DEC2BC43C19001B1964 /* SearchWidgetView.swift */; };
8182
8C28008F2D49354400ED6D07 /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 8C28008E2D49354400ED6D07 /* Shared */; };
8283
8C2800912D49357C00ED6D07 /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 8C2800902D49357C00ED6D07 /* Shared */; };
@@ -688,6 +689,7 @@
688689
8A6445872BA3904100759319 /* disconnect-block-social.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-social.json"; path = "../../ContentBlockingLists/disconnect-block-social.json"; sourceTree = "<group>"; };
689690
8A6445882BA3904100759319 /* disconnect-block-advertising.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-advertising.json"; path = "../../ContentBlockingLists/disconnect-block-advertising.json"; sourceTree = "<group>"; };
690691
8A6445892BA3904100759319 /* disconnect-block-content.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-content.json"; path = "../../ContentBlockingLists/disconnect-block-content.json"; sourceTree = "<group>"; };
692+
8C0AB2282D817EC700E88035 /* ProfileIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileIdentifier.swift; sourceTree = "<group>"; };
691693
8C1B5DEC2BC43C19001B1964 /* SearchWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchWidgetView.swift; sourceTree = "<group>"; };
692694
8C75DF652D3E636A00924EAB /* TelemetryManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryManager.swift; sourceTree = "<group>"; };
693695
8C8906AB2D0B3E9F003734FE /* UsageProfileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsageProfileManager.swift; sourceTree = "<group>"; };
@@ -1904,6 +1906,7 @@
19041906
E4BF2DD61BACE8CA00DA9D68 /* AppDelegate.swift */,
19051907
8C8906AB2D0B3E9F003734FE /* UsageProfileManager.swift */,
19061908
8C90C3BD2D48F0DB00DDA361 /* GleanUsageReportingMetricsService.swift */,
1909+
8C0AB2282D817EC700E88035 /* ProfileIdentifier.swift */,
19071910
D3E2C8E91D9F0E6200DEBE3D /* BrowserViewController.swift */,
19081911
D3E3CA7C1DA827AD0079C94B /* HomeViewController.swift */,
19091912
C8D0305A27E89CC0000664DA /* InternalSettings */,
@@ -2668,6 +2671,7 @@
26682671
C8CE05072743FA89002057C6 /* UIAction+MenuAction.swift in Sources */,
26692672
163BFFE62125EE260007CEBC /* SiriShortcuts.swift in Sources */,
26702673
E5E4505028CF4575005A01F1 /* TipManager.swift in Sources */,
2674+
8C0AB2292D817EC700E88035 /* ProfileIdentifier.swift in Sources */,
26712675
16074B6D2114364C003B671F /* PhotonActionSheet.swift in Sources */,
26722676
E5E4504E28CF4548005A01F1 /* TipsPageViewController.swift in Sources */,
26732677
C8337DF62710898800093D42 /* TrackingProtectionState.swift in Sources */,

‎focus-ios/Blockzilla/AppDelegate.swift

+9-10
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,14 @@ extension AppDelegate {
365365
// MARK: - Telemetry & Tooling setup
366366
extension AppDelegate {
367367
func setupTelemetry() {
368+
let channel = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" ? "testflight" : "release"
369+
let configuration = Configuration(channel: channel)
370+
371+
Glean.shared.initialize(
372+
uploadEnabled: TelemetryManager.shared.isGleanEnabled,
373+
configuration: configuration,
374+
buildInfo: GleanMetrics.GleanBuild.info
375+
)
368376
let activeSearchEngine = SearchEngineManager(prefs: UserDefaults.standard).activeEngine
369377
let defaultSearchEngineProvider = activeSearchEngine.isCustom ? "custom" : activeSearchEngine.name
370378
GleanMetrics.Search.defaultEngine.set(defaultSearchEngineProvider)
@@ -386,20 +394,11 @@ extension AppDelegate {
386394
if TelemetryManager.shared.isNewTosEnabled {
387395
gleanUsageReportingMetricsService.start()
388396
} else {
389-
gleanUsageReportingMetricsService.unsetUsageProfileId()
397+
gleanUsageReportingMetricsService.lifecycleObserver.profileIdentifier.unsetUsageProfileId()
390398
}
391399

392400
Glean.shared.registerPings(GleanMetrics.Pings.shared)
393401

394-
let channel = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" ? "testflight" : "release"
395-
let configuration = Configuration(channel: channel)
396-
397-
Glean.shared.initialize(
398-
uploadEnabled: TelemetryManager.shared.isGleanEnabled,
399-
configuration: configuration,
400-
buildInfo: GleanMetrics.GleanBuild.info
401-
)
402-
403402
let url = URL(string: "firefox://", invalidCharacters: false)!
404403
// Send "at startup" telemetry
405404
GleanMetrics.Shortcuts.shortcutsOnHomeNumber.set(Int64(shortcutManager.shortcutsViewModels.count))

‎focus-ios/Blockzilla/GleanUsageReportingMetricsService.swift

+4-23
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ final class GleanUsageReporting: GleanUsageReportingApi {
6262

6363
class GleanLifecycleObserver {
6464
let gleanUsageReportingApi: GleanUsageReportingApi
65+
let profileIdentifier = ProfileIdentifier()
6566
private var isObserving = false
6667
private let notificationCenter: NotificationCenter
6768

@@ -136,7 +137,7 @@ class GleanLifecycleObserver {
136137
}
137138

138139
class GleanUsageReportingMetricsService {
139-
private var lifecycleObserver: GleanLifecycleObserver
140+
private(set) var lifecycleObserver: GleanLifecycleObserver
140141

141142
init(
142143
lifecycleObserver: GleanLifecycleObserver = GleanLifecycleObserver()
@@ -146,34 +147,14 @@ class GleanUsageReportingMetricsService {
146147

147148
func start() {
148149
lifecycleObserver.gleanUsageReportingApi.setEnabled(true)
149-
checkAndSetUsageProfileId()
150+
lifecycleObserver.profileIdentifier.checkAndSetProfileId()
150151
lifecycleObserver.startObserving()
151152
}
152153

153154
func stop() {
154155
lifecycleObserver.gleanUsageReportingApi.setEnabled(false)
155156
lifecycleObserver.stopObserving()
156157
lifecycleObserver.gleanUsageReportingApi.requestDataDeletion()
157-
unsetUsageProfileId()
158-
}
159-
160-
struct Constants {
161-
static let profileId = "profileId"
162-
static let canaryUUID = UUID(uuidString: "beefbeef-beef-beef-beef-beeefbeefbee")!
163-
}
164-
165-
func unsetUsageProfileId() {
166-
UserDefaults.standard.removeObject(forKey: Constants.profileId)
167-
GleanMetrics.Usage.profileId.set(Constants.canaryUUID)
168-
}
169-
170-
func checkAndSetUsageProfileId() {
171-
if let uuidString = UserDefaults.standard.string(forKey: Constants.profileId),
172-
let uuid = UUID(uuidString: uuidString) {
173-
GleanMetrics.Usage.profileId.set(uuid)
174-
} else {
175-
let uuid = GleanMetrics.Usage.profileId.generateAndSet()
176-
UserDefaults.standard.set(uuid.uuidString, forKey: Constants.profileId)
177-
}
158+
lifecycleObserver.profileIdentifier.unsetUsageProfileId()
178159
}
179160
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/
4+
5+
6+
import Foundation
7+
import Glean
8+
9+
// MARK: - ProfileIdentifier Implementation
10+
11+
class ProfileIdentifier {
12+
struct Constants {
13+
static let profileIdKey = "profileId"
14+
static let canaryUUID = UUID(uuidString: "beefbeef-beef-beef-beef-beeefbeefbee")!
15+
static let fileName = "profile_identifier.txt"
16+
}
17+
18+
// File URL for backup storage
19+
private var fileURL: URL {
20+
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
21+
return documentsDirectory.appendingPathComponent(Constants.profileIdKey)
22+
}
23+
24+
// Clear the profile ID from all storage locations
25+
func unsetUsageProfileId() {
26+
// Clear from UserDefaults
27+
UserDefaults.standard.removeObject(forKey: Constants.profileIdKey)
28+
29+
// Clear from file backup
30+
try? FileManager.default.removeItem(at: fileURL)
31+
32+
// Set canary UUID in metrics
33+
GleanMetrics.Usage.profileId.set(Constants.canaryUUID)
34+
}
35+
36+
// Check and set the profile ID from available storage locations
37+
func checkAndSetProfileId() {
38+
// Try to get from UserDefaults first
39+
if let uuidFromDefaults = getProfileIdFromUserDefaults() {
40+
// Found in UserDefaults, use it and ensure backup exists
41+
useAndBackupProfileId(uuidFromDefaults)
42+
return
43+
}
44+
45+
// Try to get from file backup
46+
if let uuidFromFile = getProfileIdFromFile() {
47+
// Found in file, use it and ensure it's in UserDefaults
48+
useAndBackupProfileId(uuidFromFile)
49+
return
50+
}
51+
52+
// No ID found, generate a new one and store it everywhere
53+
let newUUID = GleanMetrics.Usage.profileId.generateAndSet()
54+
storeProfileId(newUUID)
55+
}
56+
57+
// Helper to retrieve UUID from UserDefaults
58+
private func getProfileIdFromUserDefaults() -> UUID? {
59+
guard let uuidString = UserDefaults.standard.string(forKey: Constants.profileIdKey),
60+
let uuid = UUID(uuidString: uuidString) else {
61+
return nil
62+
}
63+
return uuid
64+
}
65+
66+
// Helper to retrieve UUID from file backup
67+
private func getProfileIdFromFile() -> UUID? {
68+
do {
69+
let data = try Data(contentsOf: fileURL)
70+
if let uuidString = String(data: data, encoding: .utf8),
71+
let uuid = UUID(uuidString: uuidString) {
72+
return uuid
73+
}
74+
} catch {
75+
// File doesn't exist or couldn't be read
76+
return nil
77+
}
78+
return nil
79+
}
80+
81+
// Store UUID in all locations
82+
private func storeProfileId(_ uuid: UUID) {
83+
// Store in UserDefaults
84+
UserDefaults.standard.set(uuid.uuidString, forKey: Constants.profileIdKey)
85+
86+
// Store in file backup
87+
do {
88+
try uuid.uuidString.write(to: fileURL, atomically: true, encoding: .utf8)
89+
} catch {
90+
print("Failed to write UUID to file: \(error)")
91+
}
92+
93+
// Set in metrics
94+
GleanMetrics.Usage.profileId.set(uuid)
95+
}
96+
97+
// Use an existing UUID and ensure it's backed up everywhere
98+
private func useAndBackupProfileId(_ uuid: UUID) {
99+
storeProfileId(uuid)
100+
}
101+
}

0 commit comments

Comments
 (0)
Failed to load comments.