/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import Shared import Telemetry // // 'Unified Telemetry' is the name for Mozilla's telemetry system // class UnifiedTelemetry { init(profile: Profile) { NotificationCenter.default.addObserver(self, selector: #selector(uploadError(notification:)), name: Telemetry.notificationReportError, object: nil) let telemetryConfig = Telemetry.default.configuration telemetryConfig.appName = "Fennec" telemetryConfig.userDefaultsSuiteName = AppInfo.sharedContainerIdentifier telemetryConfig.dataDirectory = .documentDirectory telemetryConfig.updateChannel = AppConstants.BuildChannel.rawValue let sendUsageData = profile.prefs.boolForKey(AppConstants.PrefSendUsageData) ?? true telemetryConfig.isCollectionEnabled = sendUsageData telemetryConfig.isUploadEnabled = sendUsageData telemetryConfig.measureUserDefaultsSetting(forKey: "profile.blockPopups", withDefaultValue: true) telemetryConfig.measureUserDefaultsSetting(forKey: "profile.saveLogins", withDefaultValue: true) telemetryConfig.measureUserDefaultsSetting(forKey: "profile.showClipboardBar", withDefaultValue: false) telemetryConfig.measureUserDefaultsSetting(forKey: "profile.settings.closePrivateTabs", withDefaultValue: false) telemetryConfig.measureUserDefaultsSetting(forKey: "profile.ASPocketStoriesVisible", withDefaultValue: true) telemetryConfig.measureUserDefaultsSetting(forKey: "profile.ASBookmarkHighlightsVisible", withDefaultValue: true) telemetryConfig.measureUserDefaultsSetting(forKey: "profile.ASRecentHighlightsVisible", withDefaultValue: true) telemetryConfig.measureUserDefaultsSetting(forKey: "profile.prefkey.trackingprotection.enabled", withDefaultValue: "onInPrivateBrowsing") telemetryConfig.measureUserDefaultsSetting(forKey: "profile.prefkey.trackingprotection.strength", withDefaultValue: "basic") let prefs = profile.prefs Telemetry.default.beforeSerializePing(pingType: CorePingBuilder.PingType) { (inputDict) -> [String: Any?] in var outputDict = inputDict // make a mutable copy if let newTabChoice = prefs.stringForKey(NewTabAccessors.PrefKey) { outputDict["defaultNewTabExperience"] = newTabChoice as AnyObject? } if let chosenEmailClient = prefs.stringForKey(PrefsKeys.KeyMailToOption) { outputDict["defaultMailClient"] = chosenEmailClient as AnyObject? } return outputDict } Telemetry.default.beforeSerializePing(pingType: MobileEventPingBuilder.PingType) { (inputDict) -> [String : Any?] in var outputDict = inputDict var settings: [String : Any?] = inputDict["settings"] as? [String : Any?] ?? [:] let searchEngines = SearchEngines(prefs: profile.prefs, files: profile.files) settings["defaultSearchEngine"] = searchEngines.defaultEngine.engineID ?? "custom" outputDict["settings"] = settings return outputDict } Telemetry.default.add(pingBuilderType: CorePingBuilder.self) Telemetry.default.add(pingBuilderType: MobileEventPingBuilder.self) } @objc func uploadError(notification: NSNotification) { guard !DeviceInfo.isSimulator(), let error = notification.userInfo?["error"] as? NSError else { return } Sentry.shared.send(message: "Upload Error", tag: SentryTag.unifiedTelemetry, severity: .info, description: error.debugDescription) } } // Enums for Event telemetry. extension UnifiedTelemetry { public enum EventCategory: String { case action = "action" } public enum EventMethod: String { case add = "add" case background = "background" case change = "change" case delete = "delete" case foreground = "foreground" case open = "open" case scan = "scan" case tap = "tap" case view = "view" } public enum EventObject: String { case app = "app" case bookmark = "bookmark" case bookmarksPanel = "bookmarks-panel" case qrCodeText = "qr-code-text" case qrCodeURL = "qr-code-url" case readerModeCloseButton = "reader-mode-close-button" case readerModeOpenButton = "reader-mode-open-button" case readingListItem = "reading-list-item" case setting = "setting" } public enum EventValue: String { case activityStream = "activity-stream" case appMenu = "app-menu" case awesomebarResults = "awesomebar-results" case bookmarksPanel = "bookmarks-panel" case homePanelTabButton = "home-panel-tab-button" case markAsRead = "mark-as-read" case markAsUnread = "mark-as-unread" case pageActionMenu = "page-action-menu" case readerModeToolbar = "reader-mode-toolbar" case readingListPanel = "reading-list-panel" case shareExtension = "share-extension" case shareMenu = "share-menu" } public static func recordEvent(category: EventCategory, method: EventMethod, object: EventObject, value: EventValue, extras: [String : Any?]? = nil) { Telemetry.default.recordEvent(category: category.rawValue, method: method.rawValue, object: object.rawValue, value: value.rawValue, extras: extras) } public static func recordEvent(category: EventCategory, method: EventMethod, object: EventObject, value: String? = nil, extras: [String : Any?]? = nil) { Telemetry.default.recordEvent(category: category.rawValue, method: method.rawValue, object: object.rawValue, value: value, extras: extras) } }