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

DBP expand xpc error pixels #2667

Merged
merged 6 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions DuckDuckGo/DBP/DBPHomeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,27 @@ public class DataBrokerProtectionPixelsHandler: EventMapping<DataBrokerProtectio
.secureVaultInitError(let error),
.secureVaultError(let error):
PixelKit.fire(DebugEvent(event, error: error))
case .ipcServerOptOutAllBrokersCompletion(error: let error),
.ipcServerScanAllBrokersCompletion(error: let error),
case .ipcServerStartSchedulerXPCError(error: let error),
.ipcServerStopSchedulerXPCError(error: let error),
.ipcServerScanAllBrokersXPCError(error: let error),
.ipcServerScanAllBrokersCompletedOnAgentWithError(error: let error),
.ipcServerScanAllBrokersCompletionCalledOnAppWithError(error: let error),
.ipcServerOptOutAllBrokersCompletion(error: let error),
.ipcServerRunQueuedOperationsCompletion(error: let error):
PixelKit.fire(DebugEvent(event, error: error))
PixelKit.fire(DebugEvent(event, error: error), frequency: .dailyAndCount, includeAppVersionParameter: true)
case .ipcServerStartSchedulerCalledByApp,
.ipcServerStartSchedulerReceivedByAgent,
.ipcServerStopSchedulerCalledByApp,
.ipcServerStopSchedulerReceivedByAgent,
.ipcServerScanAllBrokersAttemptedToCallWithoutLoginItemPermissions,
.ipcServerScanAllBrokersAttemptedToCallInWrongDirectory,
.ipcServerScanAllBrokersCalledByApp,
.ipcServerScanAllBrokersReceivedByAgent,
.ipcServerScanAllBrokersCompletedOnAgentWithoutError,
.ipcServerScanAllBrokersCompletionCalledOnAppWithoutError,
.ipcServerScanAllBrokersInterruptedOnAgent,
.ipcServerScanAllBrokersCompletionCalledOnAppAfterInterruption:
PixelKit.fire(event, frequency: .dailyAndCount, includeAppVersionParameter: true)
case .parentChildMatches,
.optOutStart,
.optOutEmailGenerate,
Expand All @@ -180,10 +197,7 @@ public class DataBrokerProtectionPixelsHandler: EventMapping<DataBrokerProtectio
.backgroundAgentRunOperationsAndStartSchedulerIfPossibleNoSavedProfile,
.backgroundAgentRunOperationsAndStartSchedulerIfPossibleRunQueuedOperationsCallbackStartScheduler,
.backgroundAgentStartedStoppingDueToAnotherInstanceRunning,
.ipcServerStartScheduler,
.ipcServerStopScheduler,
.ipcServerOptOutAllBrokers,
.ipcServerScanAllBrokers,
.ipcServerRunQueuedOperations,
.ipcServerRunAllOperations,
.scanSuccess,
Expand Down
7 changes: 6 additions & 1 deletion DuckDuckGo/DBP/DataBrokerProtectionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ public final class DataBrokerProtectionManager {
return dataManager
}()

private lazy var ipcClient = DataBrokerProtectionIPCClient(machServiceName: Bundle.main.dbpBackgroundAgentBundleId, pixelHandler: pixelHandler)
private lazy var ipcClient: DataBrokerProtectionIPCClient = {
let loginItemStatusChecker = LoginItem.dbpBackgroundAgent
return DataBrokerProtectionIPCClient(machServiceName: Bundle.main.dbpBackgroundAgentBundleId,
pixelHandler: pixelHandler,
loginItemStatusChecker: loginItemStatusChecker)
}()

lazy var scheduler: DataBrokerProtectionLoginItemScheduler = {

Expand Down
25 changes: 25 additions & 0 deletions DuckDuckGo/DBP/LoginItem+DataBrokerProtection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import Foundation
import LoginItems
import DataBrokerProtection

#if DBP

Expand All @@ -27,4 +28,28 @@ extension LoginItem {

}

extension LoginItem: DBPLoginItemStatusChecker {

public func doesHaveNecessaryPermissions() -> Bool {
return status != .requiresApproval
}

public func isInCorrectDirectory() -> Bool {
guard let appPath = Bundle.main.resourceURL?.deletingLastPathComponent() else { return false }
let dirPaths = NSSearchPathForDirectoriesInDomains(.applicationDirectory, .localDomainMask, true)
for path in dirPaths {
let filePath: URL
if #available(macOS 13.0, *) {
filePath = URL(filePath: path)
} else {
filePath = URL(fileURLWithPath: path)
}
if appPath.absoluteString.hasPrefix(filePath.absoluteString) {
return true
}
}
return false
}
}

#endif
17 changes: 13 additions & 4 deletions DuckDuckGoDBPBackgroundAgent/IPCServiceManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ extension IPCServiceManager: IPCServerInterface {
}

func startScheduler(showWebView: Bool) {
pixelHandler.fire(.ipcServerStartScheduler)
pixelHandler.fire(.ipcServerStartSchedulerReceivedByAgent)
scheduler.startScheduler(showWebView: showWebView)
}

func stopScheduler() {
pixelHandler.fire(.ipcServerStopScheduler)
pixelHandler.fire(.ipcServerStopSchedulerReceivedByAgent)
scheduler.stopScheduler()
}

Expand All @@ -86,9 +86,18 @@ extension IPCServiceManager: IPCServerInterface {

func scanAllBrokers(showWebView: Bool,
completion: @escaping ((DataBrokerProtectionSchedulerErrorCollection?) -> Void)) {
pixelHandler.fire(.ipcServerScanAllBrokers)
pixelHandler.fire(.ipcServerScanAllBrokersReceivedByAgent)
scheduler.scanAllBrokers(showWebView: showWebView) { errors in
self.pixelHandler.fire(.ipcServerScanAllBrokersCompletion(error: errors?.oneTimeError))
if let error = errors?.oneTimeError {
switch error {
case DataBrokerProtectionSchedulerError.operationsInterrupted:
self.pixelHandler.fire(.ipcServerScanAllBrokersInterruptedOnAgent)
default:
self.pixelHandler.fire(.ipcServerScanAllBrokersCompletedOnAgentWithError(error: error))
}
} else {
self.pixelHandler.fire(.ipcServerScanAllBrokersCompletedOnAgentWithoutError)
}
completion(errors)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public protocol IPCClientInterface: AnyObject {
func schedulerStatusChanges(_ status: DataBrokerProtectionSchedulerStatus)
}

public protocol DBPLoginItemStatusChecker {
func doesHaveNecessaryPermissions() -> Bool
func isInCorrectDirectory() -> Bool
}

/// This is the XPC interface with parameters that can be packed properly
@objc
protocol XPCClientInterface: NSObjectProtocol {
Expand All @@ -36,6 +41,7 @@ protocol XPCClientInterface: NSObjectProtocol {
public final class DataBrokerProtectionIPCClient: NSObject {

private let pixelHandler: EventMapping<DataBrokerProtectionPixels>
private let loginItemStatusChecker: DBPLoginItemStatusChecker

// MARK: - XPC Communication

Expand All @@ -52,8 +58,9 @@ public final class DataBrokerProtectionIPCClient: NSObject {

// MARK: - Initializers

public init(machServiceName: String, pixelHandler: EventMapping<DataBrokerProtectionPixels>) {
public init(machServiceName: String, pixelHandler: EventMapping<DataBrokerProtectionPixels>, loginItemStatusChecker: DBPLoginItemStatusChecker) {
self.pixelHandler = pixelHandler
self.loginItemStatusChecker = loginItemStatusChecker
let clientInterface = NSXPCInterface(with: XPCClientInterface.self)
let serverInterface = NSXPCInterface(with: XPCServerInterface.self)

Expand Down Expand Up @@ -94,20 +101,22 @@ extension DataBrokerProtectionIPCClient: IPCServerInterface {
}

public func startScheduler(showWebView: Bool) {
self.pixelHandler.fire(.ipcServerStartScheduler)
self.pixelHandler.fire(.ipcServerStartSchedulerCalledByApp)
xpc.execute(call: { server in
server.startScheduler(showWebView: showWebView)
}, xpcReplyErrorHandler: { _ in
}, xpcReplyErrorHandler: { error in
self.pixelHandler.fire(.ipcServerStartSchedulerXPCError(error: error))
// Intentional no-op as there's no completion block
// If you add a completion block, please remember to call it here too!
})
}

public func stopScheduler() {
self.pixelHandler.fire(.ipcServerStopScheduler)
self.pixelHandler.fire(.ipcServerStopSchedulerCalledByApp)
xpc.execute(call: { server in
server.stopScheduler()
}, xpcReplyErrorHandler: { _ in
}, xpcReplyErrorHandler: { error in
self.pixelHandler.fire(.ipcServerStopSchedulerXPCError(error: error))
// Intentional no-op as there's no completion block
// If you add a completion block, please remember to call it here too!
})
Expand All @@ -129,14 +138,40 @@ extension DataBrokerProtectionIPCClient: IPCServerInterface {

public func scanAllBrokers(showWebView: Bool,
completion: @escaping ((DataBrokerProtectionSchedulerErrorCollection?) -> Void)) {
self.pixelHandler.fire(.ipcServerScanAllBrokers)
self.pixelHandler.fire(.ipcServerScanAllBrokersCalledByApp)

guard loginItemStatusChecker.doesHaveNecessaryPermissions() else {
self.pixelHandler.fire(.ipcServerScanAllBrokersAttemptedToCallWithoutLoginItemPermissions)
let errors = DataBrokerProtectionSchedulerErrorCollection(oneTimeError: DataBrokerProtectionSchedulerError.loginItemDoesNotHaveNecessaryPermissions)
completion(errors)
return
}

guard loginItemStatusChecker.isInCorrectDirectory() else {
self.pixelHandler.fire(.ipcServerScanAllBrokersAttemptedToCallInWrongDirectory)
let errors = DataBrokerProtectionSchedulerErrorCollection(oneTimeError: DataBrokerProtectionSchedulerError.appInWrongDirectory)
completion(errors)
return
}

xpc.execute(call: { server in
server.scanAllBrokers(showWebView: showWebView) { errors in
self.pixelHandler.fire(.ipcServerScanAllBrokersCompletion(error: errors?.oneTimeError))
if let error = errors?.oneTimeError {
let nsError = error as NSError
let interruptedError = DataBrokerProtectionSchedulerError.operationsInterrupted as NSError
if nsError.domain == interruptedError.domain,
nsError.code == interruptedError.code {
self.pixelHandler.fire(.ipcServerScanAllBrokersCompletionCalledOnAppAfterInterruption)
} else {
self.pixelHandler.fire(.ipcServerScanAllBrokersCompletionCalledOnAppWithError(error: error))
}
} else {
self.pixelHandler.fire(.ipcServerScanAllBrokersCompletionCalledOnAppWithoutError)
}
completion(errors)
}
}, xpcReplyErrorHandler: { error in
self.pixelHandler.fire(.ipcServerScanAllBrokersCompletion(error: error))
self.pixelHandler.fire(.ipcServerScanAllBrokersXPCError(error: error))
completion(DataBrokerProtectionSchedulerErrorCollection(oneTimeError: error))
})
}
Expand Down
Loading
Loading