Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add double background pixel with timestamps of possible interfering events #3749

Merged
merged 12 commits into from
Dec 20, 2024
10 changes: 10 additions & 0 deletions Core/Pixel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,16 @@

public static let appState = "state"
public static let appEvent = "event"

public static let firstBackgroundTimestamp = "firstBackgroundTimestamp"
public static let secondBackgroundTimestamp = "secondBackgroundTimestamp"
public static let didReceiveMemoryWarningTimestamp = "didReceiveMemoryWarningTimestamp"
public static let didReceiveMXPayloadTimestamp = "didReceiveMXPayloadTimestamp"
public static let didReceiveUNNotification = "didReceiveUNNotification"
public static let didStartRemoteMessagingClientBackgroundTask = "didStartRemoteMessagingClientBackgroundTask"
public static let didStartAppConfigurationFetchBackgroundTask = "didStartAppConfigurationFetchBackgroundTask"
public static let didPerformFetchTimestamp = "didPerformFetchTimestamp"
public static let numberOfBackgrounds = "numberOfBackgrounds"
}

public struct PixelValues {
Expand Down Expand Up @@ -272,7 +282,7 @@
allowedQueryReservedCharacters: allowedQueryReservedCharacters,
headers: headers)
let request = APIRequest(configuration: configuration, urlSession: .session(useMainThreadCallbackQueue: true))
request.fetch { _, error in

Check warning on line 285 in Core/Pixel.swift

View workflow job for this annotation

GitHub Actions / Make Release Build

'fetch(completion:)' is deprecated: Please use 'APIService' instead.

Check warning on line 285 in Core/Pixel.swift

View workflow job for this annotation

GitHub Actions / Make Release Build

'fetch(completion:)' is deprecated: Please use 'APIService' instead.

Check warning on line 285 in Core/Pixel.swift

View workflow job for this annotation

GitHub Actions / Unit Tests

'fetch(completion:)' is deprecated: Please use 'APIService' instead.
Logger.general.debug("Pixel fired \(pixelName, privacy: .public) \(params, privacy: .public)")
onComplete(error)
}
Expand Down
2 changes: 2 additions & 0 deletions Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ extension Pixel {

// MARK: Lifecycle
case appDidTransitionToUnexpectedState
case appDidConsecutivelyBackground
}

}
Expand Down Expand Up @@ -1886,6 +1887,7 @@ extension Pixel.Event {

// MARK: Lifecycle
case .appDidTransitionToUnexpectedState: return "m_debug_app-did-transition-to-unexpected-state"
case .appDidConsecutivelyBackground: return "m_debug_app-did-consecutively-background"

}
}
Expand Down
3 changes: 3 additions & 0 deletions DuckDuckGo/AppConfigurationFetch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class AppConfigurationFetch {
return Date().timeIntervalSince(Self.lastConfigurationRefreshDate) > Constants.minimumConfigurationRefreshInterval
}

static var didStartBackgroundTaskTimestamp: Date?

enum BackgroundRefreshCompletionStatus {

case expired
Expand Down Expand Up @@ -132,6 +134,7 @@ class AppConfigurationFetch {

static func registerBackgroundRefreshTaskHandler() {
BGTaskScheduler.shared.register(forTaskWithIdentifier: Constants.backgroundProcessingTaskIdentifier, using: nil) { (task) in
didStartBackgroundTaskTimestamp = Date()
guard shouldRefresh else {
task.setTaskCompleted(success: true)
scheduleBackgroundRefreshTask()
Expand Down
25 changes: 21 additions & 4 deletions DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ import os.log
Configuration.setURLProvider(AppConfigurationURLProvider())
}

crashCollection.startAttachingCrashLogMessages { pixelParameters, payloads, sendReport in
crashCollection.startAttachingCrashLogMessages { [weak self] pixelParameters, payloads, sendReport in
self?.didReceiveMXPayloadTimestamp = Date()
pixelParameters.forEach { params in
Pixel.fire(pixel: .dbCrashDetected, withAdditionalParameters: params, includedParameters: [])

Expand All @@ -196,11 +197,11 @@ import os.log

// Async dispatch because rootViewController may otherwise be nil here
DispatchQueue.main.async {
guard let viewController = self.window?.rootViewController else { return }
guard let viewController = self?.window?.rootViewController else { return }

let crashReportUploaderOnboarding = CrashCollectionOnboarding(appSettings: AppDependencyProvider.shared.appSettings)
crashReportUploaderOnboarding.presentOnboardingIfNeeded(for: payloads, from: viewController, sendReport: sendReport)
self.crashReportUploaderOnboarding = crashReportUploaderOnboarding
self?.crashReportUploaderOnboarding = crashReportUploaderOnboarding
}
}

Expand Down Expand Up @@ -917,7 +918,7 @@ import os.log
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

Logger.lifecycle.debug(#function)

didPerformFetchTimestamp = Date()
AppConfigurationFetch().start(isBackgroundFetch: true) { result in
switch result {
case .noData:
Expand Down Expand Up @@ -1164,6 +1165,21 @@ import os.log
UIApplication.shared.shortcutItems = nil
}
}

var didReceiveMemoryWarningTimestamp: Date?
func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
didReceiveMemoryWarningTimestamp = Date()
}
var didReceiveMXPayloadTimestamp: Date?
var didReceiveUNNotificationTimestamp: Date?
var didStartRemoteMessagingClientBackgroundTaskTimestamp: Date? {
remoteMessagingClient.didStartBackgroundTaskTimestamp
}
var didStartAppConfigurationFetchBackgroundTaskTimestamp: Date? {
AppConfigurationFetch.didStartBackgroundTaskTimestamp
}

var didPerformFetchTimestamp: Date?
}

extension AppDelegate: BlankSnapshotViewRecoveringDelegate {
Expand Down Expand Up @@ -1216,6 +1232,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
didReceiveUNNotificationTimestamp = Date()
if response.actionIdentifier == UNNotificationDefaultActionIdentifier {
let identifier = response.notification.request.identifier

Expand Down
12 changes: 4 additions & 8 deletions DuckDuckGo/AppLifecycle/AppStateTransitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ extension Background {
case .openURL:
return self
case .backgrounding:
return DoubleBackground()
return DoubleBackground(previousDidEnterBackgroundTimestamp: timestamp, counter: 0)
case .launching, .suspending:
return handleUnexpectedEvent(event)
}
Expand All @@ -102,15 +102,14 @@ extension Background {
extension DoubleBackground {

func apply(event: AppEvent) -> any AppState {
// report event so we know what events can be called at this moment, but do not let SM be stuck in this state just not to be flooded with these events
_ = handleUnexpectedEvent(event)

switch event {
case .activating(let application):
return Active(application: application)
case .suspending(let application):
return Inactive(application: application)
case .launching, .backgrounding, .openURL:
case .backgrounding(let application):
return DoubleBackground(previousDidEnterBackgroundTimestamp: currentDidEnterBackgroundTimestamp, counter: counter)
case .launching, .openURL:
return self
}

Expand All @@ -121,9 +120,6 @@ extension DoubleBackground {
extension InactiveBackground {

func apply(event: AppEvent) -> any AppState {
// report event so we know what events can be called at this moment, but do not let SM be stuck in this state just not to be flooded with these events
_ = handleUnexpectedEvent(event)

switch event {
case .activating(let application):
return Active(application: application)
Expand Down
60 changes: 59 additions & 1 deletion DuckDuckGo/AppLifecycle/AppStates/Background.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,73 @@
//

import UIKit
import Core

struct Background: AppState {

let timestamp = Date()

init(application: UIApplication) {

}

}

struct DoubleBackground: AppState {


private let dateFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime]
return formatter
}()

let currentDidEnterBackgroundTimestamp: Date
var counter: Int

init(previousDidEnterBackgroundTimestamp: Date, counter: Int) {
self.currentDidEnterBackgroundTimestamp = Date()
self.counter = counter + 1

var parameters = [
PixelParameters.firstBackgroundTimestamp: dateFormatter.string(from: previousDidEnterBackgroundTimestamp),
PixelParameters.secondBackgroundTimestamp: dateFormatter.string(from: currentDidEnterBackgroundTimestamp)
]

if counter < 5 {
parameters[PixelParameters.numberOfBackgrounds] = String(counter)
}

func isValid(timestamp: Date) -> Bool {
timestamp >= previousDidEnterBackgroundTimestamp && timestamp <= currentDidEnterBackgroundTimestamp
}

if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
if let didReceiveMemoryWarningTimestamp = appDelegate.didReceiveMemoryWarningTimestamp,
isValid(timestamp: didReceiveMemoryWarningTimestamp) {
parameters[PixelParameters.didReceiveMemoryWarningTimestamp] = dateFormatter.string(from: didReceiveMemoryWarningTimestamp)
}
if let didReceiveMXPayloadTimestamp = appDelegate.didReceiveMXPayloadTimestamp,
isValid(timestamp: didReceiveMXPayloadTimestamp) {
parameters[PixelParameters.didReceiveMXPayloadTimestamp] = dateFormatter.string(from: didReceiveMXPayloadTimestamp)
}
if let didReceiveUNNotificationTimestamp = appDelegate.didReceiveUNNotificationTimestamp,
isValid(timestamp: didReceiveUNNotificationTimestamp) {
parameters[PixelParameters.didReceiveUNNotification] = dateFormatter.string(from: didReceiveUNNotificationTimestamp)
}
if let didStartRemoteMessagingClientBackgroundTaskTimestamp = appDelegate.didStartRemoteMessagingClientBackgroundTaskTimestamp,
isValid(timestamp: didStartRemoteMessagingClientBackgroundTaskTimestamp) {
parameters[PixelParameters.didStartRemoteMessagingClientBackgroundTask] = dateFormatter.string(from: didStartRemoteMessagingClientBackgroundTaskTimestamp)
}
if let didStartAppConfigurationFetchBackgroundTaskTimestamp = appDelegate.didStartAppConfigurationFetchBackgroundTaskTimestamp,
isValid(timestamp: didStartAppConfigurationFetchBackgroundTaskTimestamp) {
parameters[PixelParameters.didStartAppConfigurationFetchBackgroundTask] = dateFormatter.string(from: didStartAppConfigurationFetchBackgroundTaskTimestamp)
}
if let didPerformFetchTimestamp = appDelegate.didPerformFetchTimestamp,
isValid(timestamp: didPerformFetchTimestamp) {
parameters[PixelParameters.didPerformFetchTimestamp] = dateFormatter.string(from: didPerformFetchTimestamp)
}
}
Pixel.fire(pixel: .appDidConsecutivelyBackground, withAdditionalParameters: parameters)
}

}
5 changes: 4 additions & 1 deletion DuckDuckGo/RemoteMessagingClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ final class RemoteMessagingClient: RemoteMessagingProcessing {
let store: RemoteMessagingStoring
let remoteMessagingAvailabilityProvider: RemoteMessagingAvailabilityProviding

var didStartBackgroundTaskTimestamp: Date?

convenience init(
bookmarksDatabase: CoreDataDatabase,
appSettings: AppSettings,
Expand Down Expand Up @@ -113,7 +115,8 @@ extension RemoteMessagingClient {
let remoteMessagingAvailabilityProvider = remoteMessagingAvailabilityProvider
let store = store

BGTaskScheduler.shared.register(forTaskWithIdentifier: Constants.backgroundRefreshTaskIdentifier, using: nil) { task in
BGTaskScheduler.shared.register(forTaskWithIdentifier: Constants.backgroundRefreshTaskIdentifier, using: nil) { [weak self] task in
self?.didStartBackgroundTaskTimestamp = Date()
guard Self.shouldRefresh else {
task.setTaskCompleted(success: true)
Self.scheduleBackgroundRefreshTask()
Expand Down
Loading