-
Notifications
You must be signed in to change notification settings - Fork 14
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
Rework DBP XPC interface #2758
Rework DBP XPC interface #2758
Conversation
@@ -0,0 +1,100 @@ | |||
// | |||
// DataBrokerProtectionLoginItemInterface.swift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Despite what GitHub thinks, this class isn't really new, just renamed (although it has changed a bit to give it an explicit protocol)
func profileSaved() { | ||
enableLoginItem() | ||
ipcClient.profileSaved { error in | ||
// TODO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to implement pixels here. It's my next item on my todo list, but could take a while so I don't think the PR should wait for it since it's not merging to main anyway
} | ||
|
||
public func runOperationsAndStartSchedulerIfPossible() { | ||
public func agentFinishedLaunching() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also changed this to be more event based, so it's all the manager making the decisions
return | ||
} | ||
|
||
scheduler.runQueuedOperations(showWebView: false) { [weak self] _ in | ||
self?.pixelHandler.fire(.backgroundAgentRunOperationsAndStartSchedulerIfPossibleRunQueuedOperationsCallbackStartScheduler) | ||
self?.scheduler.startScheduler() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's some more work to do in this completion, but it should all change with the final PR so pointless doing it here
// This is now unusused as we decided the web UI shouldn't issue commands directly | ||
// The background agent itself instead decides to start scans based on events | ||
// This should be removed once we can remove it from the web side | ||
return true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth noting this always returned true anyway unless the scan delegate was nil
} | ||
|
||
@objc | ||
public class DataBrokerProtectionAgentErrorCollection: NSObject, NSSecureCoding { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bits not new, but newly in its own file and renamed
}) | ||
} | ||
|
||
public func getDebugMetadata() async -> DBPBackgroundAgentMetadata? { | ||
await withCheckedContinuation { continuation in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought this might be a bad idea across the XPC boundary, but netP is also doing the same thing
@@ -75,40 +75,18 @@ public final class DBPBackgroundAgentMetadata: NSObject, NSSecureCoding { | |||
|
|||
/// This protocol describes the server-side IPC interface for controlling the tunnel | |||
/// | |||
public protocol IPCServerInterface: AnyObject { | |||
public protocol IPCServerInterface: AnyObject, DataBrokerProtectionAgentDebugCommands { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm slightly on the fence about this protocol conformance versus duplicating it again, I don't want to make it more confusing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the conformance is fine 👍🏼
func getDebugMetadata(completion: @escaping (DBPBackgroundAgentMetadata?) -> Void) | ||
func profileSaved(xpcMessageReceivedCompletion: @escaping (Error?) -> Void) | ||
func dataDeleted(xpcMessageReceivedCompletion: @escaping (Error?) -> Void) | ||
func appLaunched(xpcMessageReceivedCompletion: @escaping (Error?) -> Void) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are declared again rather than conforming to the AppEvents protocol to have the xpcMessageReceivedCompletion, and to avoid having it in the AppEvents protocol.
|
||
/// Opens a browser window with the specified domain | ||
/// | ||
func openBrowser(domain: String) | ||
|
||
func startManualScan(showWebView: Bool) | ||
func runQueuedOperations(showWebView: Bool) | ||
func runAllOptOuts(showWebView: Bool) | ||
func getDebugMetadata(completion: @escaping (DBPBackgroundAgentMetadata?) -> Void) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are declared again because this version of getDebugMetadata has to use a completion to go inside xpc.execute, but the higher level interfaces hide this and use a more modern async
func register() { | ||
serverDelegate?.register() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The register method this was calling was empty
|
||
extension DataBrokerProtectionAgentManager: DataBrokerProtectionAgentAppEvents { | ||
|
||
public func profileSaved() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to unit test that when certain events happen the correct scheduler method is called, but given the next PR is gonna change all that, I don't think it's worth doing until the next PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM @THISISDINOSAUR, nice work! Validated that background agent is started on fresh launch. Left a few comments, main one being that I think we should add tests for the DataBrokerProtectionAgentManager
.
func getDebugMetadata() async -> DBPBackgroundAgentMetadata? | ||
} | ||
|
||
public protocol DataBrokerProtectionAgentInterface: AnyObject, DataBrokerProtectionAgentAppEvents, DataBrokerProtectionAgentDebugCommands { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on my understanding of intention, I find the name DataBrokerProtectionAgentInterface
a bit misleading/confusing. On one level we refer to the ‘background agent’ as the ‘agent’. However, this interface is used on both sides of the app -> agent communication. Do we think then maybe something like DataBrokerProtectionClientServerInterface
….or DataBrokerProtectionAppAgentInterface
…?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's worse since it's strictly one directional (completions or return types not withstanding). It could be DataBrokerProtectionAppToAgentInterface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more I think about it, I think I do prefer that, since it is specific to the app (what with the appLaunched event and that)
public static var supportsSecureCoding: Bool { | ||
return true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor, and not new code maybe, but while we are here this could be:
public static let supportsSecureCoding = true
Just to remove a line or two.
public protocol DataBrokerProtectionAgentDebugCommands { | ||
func openBrowser(domain: String) | ||
func startManualScan(showWebView: Bool) | ||
func runQueuedOperations(showWebView: Bool) | ||
func runAllOptOuts(showWebView: Bool) | ||
func getDebugMetadata() async -> DBPBackgroundAgentMetadata? | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned on MM, I was also adding something similar based on previous suggestions, and the approach I came up with using enum
cases as debug actions, as follows:
enum DataBrokerDebugCommand {
case startOptOutOperations(showWebView: Bool,
operationDependencies: DataBrokerOperationDependencies,
completion: ((DataBrokerProtectionSchedulerErrorCollection?) -> Void)?)
}
protocol DataBrokerDebugCommandExecutor {
func execute(_ command: DataBrokerDebugCommand)
}
Any desire to go the enum route? The API it provides is succinct. Just my preference, not a strong opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not possible because it to be able to cross the XPC boundary which means it needs to be representable in Objective-C, i.e. can't have associated values. It's what I originally tried but it didn't work
It might be possible to have the higher level interface be an enum and then have a translation layer in the middle, but I don't think it would be worth the complexity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah, or course. Apologies, you already highlighted this. I was trying for something similar, but only on the agent side, and so the boundary issue slipped my mind. Agree, translation layer etc. not worth the complexity.
@@ -24,7 +24,6 @@ import XPCHelper | |||
/// This protocol describes the server-side IPC interface for controlling the tunnel | |||
/// | |||
public protocol IPCClientInterface: AnyObject { | |||
func schedulerStatusChanges(_ status: DataBrokerProtectionSchedulerStatus) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming these empty interfaces will be removed as some point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we still need to give the xpc server something. At the very least our own generic XPCClient requires it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regardless of if we need it or not, I think I prefer explicitly declaring that the interface in the other direction is empty.
@@ -75,40 +75,18 @@ public final class DBPBackgroundAgentMetadata: NSObject, NSSecureCoding { | |||
|
|||
/// This protocol describes the server-side IPC interface for controlling the tunnel | |||
/// | |||
public protocol IPCServerInterface: AnyObject { | |||
public protocol IPCServerInterface: AnyObject, DataBrokerProtectionAgentDebugCommands { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the conformance is fine 👍🏼
@@ -22,24 +22,29 @@ import BrowserServicesKit | |||
import DataBrokerProtection | |||
import PixelKit | |||
|
|||
public final class DataBrokerProtectionBackgroundManager { | |||
public final class DataBrokerProtectionAgentManager { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should take the time now to make this type testable. It’s currently a singleton with lazy vars, which is not ideal for dependency injection. Does it need to be a singleton? I see it is only referenced once in DuckDuckGoDBPBackgroundAgentAppDelegate
. Can we remove the singleton behavior, make the init public, and then inject what is needed to be able to mock dependencies? I think we should be testing that the DataBrokerProtectionAgentAppEvents
perform the right actions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somehow I missed your comment above re: testing DataBrokerProtectionAgentAppEvents
, and so if we are gonna test in next PR that’s fine with me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah agreed on testing the app events in that way (#2758 (comment)). I'm indifferent to if we do that particular groundwork in this PR or the next, although think writing the actual tests should wait for the next one since the expected behaviour will change significantly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing. Indifferent on order of work also. Whatever is most pragmatic to you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great 👍🏼
74e390d
into
feature/pir-stabilization
Task/Issue URL: https://app.asana.com/0/1199230911884351/1207338264132623/f Tech Design URL: https://app.asana.com/0/481882893211075/1207174884557414/f CC: **Description**: This PR has the effect of mergeing four different PRs into main: #2777 #2758 #2757 #2743 This covers significant changes to the XPC interface and how the main app uses it, the background manager, the scheduler, and the processor. See individual PRs for details. There's no code changes here that haven't been reviewed separately as part of those PRs, so it's up to you how you want to review it code wise. The more important step at this stage is manual testing. **Steps to test this PR**: 1. Because this is a fairly significant set of changes, we need to be as thorough as we can in testing DBP generally works and really try to break it. Because of this, I'm keen that anyone testing things of their own ways to break it before trying the steps below. 2. Edit/add profile data, and before scans finish, edit it again. Scans should start afresh, and you should see an interrupted pixel fire. Check that you don't get a scans completed notification 3. Edit/add profile data, and before scans finish, close the app, and reopen it. The same set of manual scans should still continue, and a blocked pixel should fire. 4. With profile data already existing but initial scans having finished, close the app and reopen it. Scheduled scans should run. 5. With profile data already existing, and with the app closed, launch the background agent, scheduled scans should run 6. Edit/add profile data, put the laptop to sleep/lock it/restart it, check that scans continue as expected. 7. Check that the background agent activity lines up with the UI, I.e. the progress bar and when we sent the scans completed notification is accurate <!-- Tagging instructions If this PR isn't ready to be merged for whatever reason it should be marked with the `DO NOT MERGE` label (particularly if it's a draft) If it's pending Product Review/PFR, please add the `Pending Product Review` label. If at any point it isn't actively being worked on/ready for review/otherwise moving forward (besides the above PR/PFR exception) strongly consider closing it (or not opening it in the first place). If you decide not to close it, make sure it's labelled to make it clear the PRs state and comment with more information. --> --- ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --------- Co-authored-by: Pete Smith <[email protected]>
Task/Issue URL: https://app.asana.com/0/0/1207231867963535/f
Tech Design URL: https://app.asana.com/0/0/1207198317317333/f
CC:
Description:
It still needs the pixels for #9, including clean up of the old pixels
Steps to test this PR:
Internal references:
Pull Request Review Checklist
Software Engineering Expectations
Technical Design Template
Pull Request Documentation