Send Tab #676
Send Tab #676
Conversation
a70fedf
to
2375363
8f20af1
to
620a8ba
...xa-client/android/src/main/java/org/mozilla/fxaclient/internal/Device.kt
Outdated
Show resolved
Hide resolved
...xa-client/android/src/main/java/org/mozilla/fxaclient/internal/Device.kt
Outdated
Show resolved
Hide resolved
...t/android/src/main/java/org/mozilla/fxaclient/internal/FirefoxAccount.kt
Outdated
Show resolved
Hide resolved
| /** | ||
| * Ensure the current device "Send Tab" commands has been registered with the server. | ||
| * This method should be called once per "device lifetime" after the Sync Keys have been | ||
| * obtained and called again if they change. |
rfk
Feb 21, 2019
Member
It feels like we should be able to detect internally if the sync keys change (e.g. because our refreshToken gets revoked) so I wonder if there's anything we can do to make this easier on the calling code.
It feels like we should be able to detect internally if the sync keys change (e.g. because our refreshToken gets revoked) so I wonder if there's anything we can do to make this easier on the calling code.
eoger
Feb 21, 2019
Author
Contributor
I guess we could attempt a registration every time we see a Sync Key pass in front of us since there is only one way to get them, however we'd need a way for apps to opt-out of Send Tab and register the display-uri command (maybe with Config?).
I guess we could attempt a registration every time we see a Sync Key pass in front of us since there is only one way to get them, however we'd need a way for apps to opt-out of Send Tab and register the display-uri command (maybe with Config?).
...t/android/src/main/java/org/mozilla/fxaclient/internal/FirefoxAccount.kt
Outdated
Show resolved
Hide resolved
| rustCall { e -> | ||
| FxaClient.INSTANCE.fxa_ensure_send_tab_registered(validHandle(), e) | ||
| } | ||
| } |
rfk
Feb 21, 2019
Member
FWIW, I haven't given up hope that we'll one day figure out how to factor this out into a separate module rather than growing a method off the main FxAClient object, perhaps after we get more experience with shipping multi-component megazord setups on different platforms. But a big 👍 to getting it out the door this way to start with :-)
FWIW, I haven't given up hope that we'll one day figure out how to factor this out into a separate module rather than growing a method off the main FxAClient object, perhaps after we get more experience with shipping multi-component megazord setups on different platforms. But a big
8cb2077
to
95d3b03
|
Note: we need to re-register our device record when we drop our refresh token to craft a new one |
|
I'm working my way through a review of this, still in progress but got to stop for some meetings. Initial comments below. |
...ient/android/src/main/java/mozilla/appservices/fxaclient/AccountEvent.kt
Show resolved
Hide resolved
...fxa-client/android/src/main/java/mozilla/appservices/fxaclient/Device.kt
Outdated
Show resolved
Hide resolved
...nt/android/src/main/java/mozilla/appservices/fxaclient/FirefoxAccount.kt
Outdated
Show resolved
Hide resolved
| rustCall { e -> | ||
| LibFxAFFI.INSTANCE.fxa_set_push_subscription(this.handle.get(), endpoint, publicKey, authKey, e) | ||
| } | ||
| } |
rfk
Mar 11, 2019
Member
What should the caller do if the network request fails here? Do they need to remember that it failed and retry again at some later time? Let's discuss with @grigoryk at the next sync-up to ensure we have consistent expectations around how to handle failed device data updates.
What should the caller do if the network request fails here? Do they need to remember that it failed and retry again at some later time? Let's discuss with @grigoryk at the next sync-up to ensure we have consistent expectations around how to handle failed device data updates.
| * | ||
| * This performs network requests, and should not be used on the main thread. | ||
| * | ||
| * @return A collection of [AccountEvent] that should be handled by the caller. |
rfk
Mar 11, 2019
Member
Where should the caller look for documentation on the possible [AccountEvent] types and what actions it should take in response to them? Is this a doc that needs to exist somewhere in FxA?
Where should the caller look for documentation on the possible [AccountEvent] types and what actions it should take in response to them? Is this a doc that needs to exist somewhere in FxA?
...nt/android/src/main/java/mozilla/appservices/fxaclient/FirefoxAccount.kt
Show resolved
Hide resolved
...nt/android/src/main/java/mozilla/appservices/fxaclient/FirefoxAccount.kt
Outdated
Show resolved
Hide resolved
| /** | ||
| * Ensure the current device "Send Tab" commands has been registered with the server. | ||
| * This method should be called once per "device lifetime" after the Sync Keys have been | ||
| * obtained and called again if they change. |
rfk
Mar 11, 2019
Member
Is "device lifetime" a sufficiently-well-defined term that we can use it here, or should we try to be more specific? I wonder if we can explicitly enumerate the set of circumstances under which this should be called, e.g. "after successful signin", "after re-authenticating", maybe others as well?
Alternately, I believe that we can tell whether or not the send-tab command was registered from our internal state, would it be possible for us to take care of this re-registration automagically after device lifetime events?
Is "device lifetime" a sufficiently-well-defined term that we can use it here, or should we try to be more specific? I wonder if we can explicitly enumerate the set of circumstances under which this should be called, e.g. "after successful signin", "after re-authenticating", maybe others as well?
Alternately, I believe that we can tell whether or not the send-tab command was registered from our internal state, would it be possible for us to take care of this re-registration automagically after device lifetime events?
|
This is a big PR @eoger but it's looking good! Sorry it's taken me so long to go through it properly. I've left a bunch of comments about ways I think it could be improved, but they're mostly code-quality questions/concerns rather than functionality changes. From talking to @vladikoff, it sounds like https://devices2.dev.lcip.org/ should be ready for you to try out this code against the "for real" version of the device API changes, if you want to give it a spin. otherwise, I think our next step here is to get @grigoryk to take the API for a spin and see how we want to iterate it from there. |
| last_handled_command: Option<u64>, | ||
| // Remove serde(default) once we are V3. | ||
| #[serde(default)] | ||
| commands_data: HashMap<String, String>, |
rfk
Mar 12, 2019
Member
Given that you're storing the commands data in the local state here, what's the reason that we don't also want to store the other device-related info like name, type etc? ISTM that keeping a local copy would make it easier to do things like replace_device in a more robust manner.
Given that you're storing the commands data in the local state here, what's the reason that we don't also want to store the other device-related info like name, type etc? ISTM that keeping a local copy would make it easier to do things like replace_device in a more robust manner.
eoger
Mar 12, 2019
Author
Contributor
I'd like to avoid having to track locally what's registered on the server, but if we need it I'm not opposed to do it.
I'd like to avoid having to track locally what's registered on the server, but if we need it I'm not opposed to do it.
| &device_info.available_commands, | ||
| ) { | ||
| log::warn!("Device information restoration failed: {:?}", err); | ||
| } |
rfk
Mar 12, 2019
Member
So if this does error out (or equivalently, if we crashed in between deleting the refresh token and re-creating the device record) is there some way for the app to detect that it no longer has a device record and needs to re-register push endpoints etc?
So if this does error out (or equivalently, if we crashed in between deleting the refresh token and re-creating the device record) is there some way for the app to detect that it no longer has a device record and needs to re-register push endpoints etc?
eoger
Mar 12, 2019
Author
Contributor
I don't think it does, but I don't really expect this to be a problem. We can do regular re-registrations if we feel it becomes a problem later.
I don't think it does, but I don't really expect this to be a problem. We can do regular re-registrations if we feel it becomes a problem later.
| }; | ||
| let encrypted_payload: EncryptedSendTabPayload = serde_json::from_value(payload)?; | ||
| Ok((sender, encrypted_payload.decrypt(&send_tab_key)?)) | ||
| } |
rfk
Mar 12, 2019
Member
One API method that might be missing here, is a method to list all available send-tab targets.
IIUC the only way for the calling code to do this right now is to call get_devices and then iterate over each device, looking for the ones that have registered the open-uri command, remembering to skip itself, and maybe even checking that their open-uri command data is encrypted with the correct kid.
Can we (and should we?) hide that complexity behind a get_send_tab_target_devices() method of some kind, rather than requiring the calling code to know about the internal implementation details of the open-uri command?
An advantage of doing it this way is that we may be able to split the available targets into several categories:
- The device advertises send-tab and we're able to send to it.
- The device advertises send-tab, but we can't send to it for some reason (maybe it's command data is encrypted with an old key or something).
- The app could show these greyed-out or similar
- The device does not support send-tab at all
- Fennec would be in this list; maybe the app wants to show them greyed out or similar?
@grigoryk would love to get your thoughts on this one.
One API method that might be missing here, is a method to list all available send-tab targets.
IIUC the only way for the calling code to do this right now is to call get_devices and then iterate over each device, looking for the ones that have registered the open-uri command, remembering to skip itself, and maybe even checking that their open-uri command data is encrypted with the correct kid.
Can we (and should we?) hide that complexity behind a get_send_tab_target_devices() method of some kind, rather than requiring the calling code to know about the internal implementation details of the open-uri command?
An advantage of doing it this way is that we may be able to split the available targets into several categories:
- The device advertises send-tab and we're able to send to it.
- The device advertises send-tab, but we can't send to it for some reason (maybe it's command data is encrypted with an old key or something).
- The app could show these greyed-out or similar
- The device does not support send-tab at all
- Fennec would be in this list; maybe the app wants to show them greyed out or similar?
@grigoryk would love to get your thoughts on this one.
rfk
Apr 17, 2019
Member
This got a thumbs-up but doesn't look like we ended up going in that direction; future work, or did it not seem worth it in practice in the end?
This got a thumbs-up but doesn't look like we ended up going in that direction; future work, or did it not seem worth it in practice in the end?
eoger
Apr 18, 2019
Author
Contributor
Sorry I should have commented on that. @grigoryk and I settled on populating a capabilities set on every device, so that Android can do the filtering itself (and avoid having to grow a new getDevice method for each new command in the future).
Sorry I should have commented on that. @grigoryk and I settled on populating a capabilities set on every device, so that Android can do the filtering itself (and avoid having to grow a new getDevice method for each new command in the future).
|
One other general comment I have here is about lifecycle management. My impression of the current PR is that we're trying now to do much of that ourselves, and the application is going to be responsible for calling things like |
|
It's worth noting that my clients collection is basically accurate (It's missing one device but I guess I haven't turned that one on in long enough that, well, sure, it's fair that it's missing). (However, clients collection does more involved filtering than just last use time) |
@grigoryk @eoger There seems to be an issue with the generated keys and how they are persisted into state. Adding some logs here: 9d0bfce#diff-59b417af97d50deff45a82e0bbc98373R19 I noticed that every time I restarted the android app new keys are generated |
|
Following up on the keys and state, a-c's FxaAccountManager never registered a state persistence callback, and so we were dropping the SendTab state on the floor after an app restart. Introducing state persistence solves the problem. |
|
API changes requested by @grigoryk:
|
|
@thomcc left some comments on the code here in this review: #821 (review) (just want to make sure we don't lose it) |
5eadc02
to
47cdfee
|
Another API change requests: Instead of a single This simplifies the consumer side: this separates out a very particular action (setting initial name), so we don't have to worry about renaming our device accidentally by restarting an app. We have a separation in the fxa manager in a-c before "account restore" and "account logged-in", and this split maps well to that. It also gives us a clear migration path in the future should we need to change something on an app upgrade - the |
|
IIUC, it's not possible to declare current device's capabilities via the current API. That is, it's not possible to register an FxA device, but also opt-out of being a Send Tab target. Because of this, in my integration PR I've elected to not try to register a device if the application didn't explicitly state it wants to process events targeted at the device. But that doesn't quite seem right either, since I assume we always want to see connected devices in the FxA device manager, regardless of their capabilities. |
That they don't currently show up until you explicitly register a device record, is a bug in FxA which will be fixed as part of mozilla/fxa#466. I agree that you'll want to be able to register a device without necessarily registering for send-tab, but that shouldn't be a blocker for appearing in the FxA "devices and apps" list. |
147a375
to
7f869c2
|
@grigoryk just letting you know the APIs we discussed have been integrated to this PR. |
|
@eoger great, thanks! This looks good to me from the API perspective. I'll update my PRs and report back. |
This should be fixed in the next FxA release, new clients will be mobile. |
The API we have now requires specifying device type during initialization, which should be enough here as well. |
| /// Initalizes our own device, most of the time this will be called right after logging-in | ||
| /// for the first time. | ||
| #[no_mangle] | ||
| pub unsafe extern "C" fn fxa_initialize_device( |
rfk
Apr 17, 2019
Member
nit: document why this this needs to be unsafe?
nit: document why this this needs to be unsafe?
| get() = this | ||
|
|
||
| sealed class AccountEvent { | ||
| class TabReceived(val from: Device?, val entries: Array<TabData>) : AccountEvent() |
rfk
Apr 17, 2019
Member
+1 to TabsReceived FWIW
+1 to TabsReceived FWIW
| val lastAccessTime: Long?, | ||
| val capabilities: List<Capability> | ||
| ) { | ||
| enum class Capability { |
rfk
Apr 17, 2019
Member
I had a bit of a chuckle at this, because we used to call these "capabilities" on the server side until we needed more data and turned them into "commands".
If we end up removing support for non-megazord builds, does that clear a future path to separating the send-tab stuff out into its own distinct component, at which point we might not need "capability" flags like this inside the core FxA component? (For the future obviously, this seems fine for now).
I had a bit of a chuckle at this, because we used to call these "capabilities" on the server side until we needed more data and turned them into "commands".
If we end up removing support for non-megazord builds, does that clear a future path to separating the send-tab stuff out into its own distinct component, at which point we might not need "capability" flags like this inside the core FxA component? (For the future obviously, this seems fine for now).
eoger
Apr 18, 2019
•
Author
Contributor
I had a bit of a chuckle at this, because we used to call these "capabilities" on the server side until we needed more data and turned them into "commands".
Yup my rationale is that the clients don't need the Commands "values" as the UI only care about "capabilities", but I might be wrong in the future :)
If we end up removing support for non-megazord builds, does that clear a future path to separating the send-tab stuff out into its own distinct component, at which point we might not need "capability" flags like this inside the core FxA component?
Yes it might make it easier, we should look into that later this year.
I had a bit of a chuckle at this, because we used to call these "capabilities" on the server side until we needed more data and turned them into "commands".
Yup my rationale is that the clients don't need the Commands "values" as the UI only care about "capabilities", but I might be wrong in the future :)
If we end up removing support for non-megazord builds, does that clear a future path to separating the send-tab stuff out into its own distinct component, at which point we might not need "capability" flags like this inside the core FxA component?
Yes it might make it easier, we should look into that later this year.
...nt/android/src/main/java/mozilla/appservices/fxaclient/FirefoxAccount.kt
Outdated
Show resolved
Hide resolved
| }; | ||
| let encrypted_payload: EncryptedSendTabPayload = serde_json::from_value(payload)?; | ||
| Ok((sender, encrypted_payload.decrypt(&send_tab_key)?)) | ||
| } |
rfk
Apr 17, 2019
Member
This got a thumbs-up but doesn't look like we ended up going in that direction; future work, or did it not seem worth it in practice in the end?
This got a thumbs-up but doesn't look like we ended up going in that direction; future work, or did it not seem worth it in practice in the end?
|
Updated with latest @rfk review comments addressed, thanks! |
|
|
TODO