GmsCore
GmsCore copied to clipboard
Support workspace accounts with basic device management
- Contains request code to Cryptauth enrollment that can satisfy the lockscreen requirement as to resolve the
DeviceManagementScreenlockRequirederror. - For manual testing, it is called when opening the account settings webview from the system account settings.
- Provides true lockscreen status in query.
- Automatically send query when all dependencies are met, and logs what is missing otherwise.
To do:
- Show appropriate error messages when user intervention is needed.
Resolves #896. Resolves #1726. Likely resolves #1838.
Just built your branch on GitHub actions. Using the debug build. Getting the following logcat when clicking on the account settings screen (only relevant parts filtered). Basically a short spinner appears when clicking and then it returns back to the previous screen. No account settings displayed, and so far could not get the DeviceManagementScreenlockRequired to disappear.
Feels like your code does not get executed (or because the intent returns too fast the deferred part does not get executed), maybe an error on my end.
D AccountSettings: Invoked with com.google.android.gms.accountsettings.SECURITY_SETTINGS and extras Bundle[{account=Supplier{VAL_PARCELABLE@24+228}}]
W ziparchive: Unable to open '/data/app/.../com.google.android.gms-...==/base.dm': No such file or directory
I ActivityTaskManager: Displayed com.google.android.gms/org.microg.gms.accountsettings.ui.MainActivity: +135ms
D GmsHttpFormClient: -- Request --
D GmsHttpFormClient: androidId=...
...
W AccountSettingsWebView: Failed to get weblogin auth.
W AccountSettingsWebView: java.io.IOException: Error=DeviceManagementScreenlockRequired
W AccountSettingsWebView: at org.microg.gms.common.HttpFormClient.request(HttpFormClient.java:96)
W AccountSettingsWebView: at org.microg.gms.auth.AuthRequest.getResponse(AuthRequest.java:258)
W AccountSettingsWebView: at org.microg.gms.auth.AuthManager.requestAuth(AuthManager.java:293)
W AccountSettingsWebView: at org.microg.gms.accountsettings.ui.WebViewHelper.openWebWithAccount(WebViewHelper.kt:109)
W AccountSettingsWebView: at org.microg.gms.accountsettings.ui.WebViewHelper.access$openWebWithAccount(WebViewHelper.kt:32)
...
D CoreBackPreview: Window{f7e791 u0 Splash Screen com.google.android.gms EXITING}: Setting back callback null
From your code, I expect there should be a debug statement like:
D SyncKeysRequest: -- Request --
...JSON...
@aximut Unfortunately I haven't been able to find the difference between your builds and my builds. On my end, it is working and outputs (for instance) the request as you expected:
SyncKeysRequest com.google.android.gms D -- Request --
{"applicationName":"com.google.android.gms","clientVersion":"1.0.0","syncSingleKeyRequests":[{"keyName":"PublicKey","keyHandles":"ZGV2aWNlX2tleQo="}],"clientMetadata":{"invocationReason":"NEW_ACCOUNT"},"clientAppMetadata":[…]}
Just started further investigation and apparently for me the function returns here:
if (!it.containsKey(GcmConstants.EXTRA_REGISTRATION_ID)) {
return null <--
}
I just rebuilt the main apk (com.google.android.gms, not the com.android.vending one). Is there anything else I need to update? Or might this be a problem with my workspace account?
I added the debug output as in this file and this is the adb log:
D SyncKeysRequest: STARTING GRPC REQUEST PHASE 1
D SyncKeysRequest: STARTING GRPC REQUEST PHASE 2
W MSBackupRestore: Unsupported class loader
W MSBackupRestore: Unsupported class loader
E system_server: Invalid class loader spec: =UnsupportedClassLoaderContext=
E PackageDexUsage: Unsupported context?
W ziparchive: Unable to open '/data/app/~~...==/com.google.android.gms-...==/base.dm': No such file or directory
I ActivityTaskManager: Displayed com.google.android.gms/org.microg.gms.accountsettings.ui.MainActivity: +548ms
Not sure if the class loader issues are related or not.
@aximut You will need to enable device checkin and Google Cloud Messaging before running the query. I will provide updates to get the functionality in a more use-ready state in the coming days.
I have device registration and GCM both enabled. For device registration, I can see my Android ID in the microG app. GCM I set to "confirm new apps".
@aximut From the code being called, enabling to "confirm new apps" while querying for an app not yet contained in the database (microG itself) will also yield no result at this moment.
Thanks! That was it. Now I see the code being fully executed.
D SyncKeysRequest: STARTING GRPC REQUEST PHASE 5
D SyncKeysRequest: STARTING GRPC REQUEST PHASE 6
D SyncKeysRequest: STARTING GRPC REQUEST PHASE 7
D SyncKeysRequest: -- Request --
D SyncKeysRequest: {"applicationName":"com.google.android.gms","clientVersion":"1.0.0","syncSingleKeyRequests":[{"keyName":"PublicKey","keyHandles":"ZGV2aWNlX2tleQo="}],"clientMetadata":{"invocationReason":"NEW_ACCOUNT"},"clientAppMetadata":"..."}
However, once I start a G app afterwards, I still get
D GmsHttpFormClient: -- Request --
D GmsHttpFormClient: androidId=...&app=com.google.android.apps.dynamite&client_sig=...
W GmsAuthManagerSvc: java.io.IOException: Error=DeviceManagementScreenlockRequired
For testing, I commented out all the rest of the web view code though, maybe that's the issue. Otherwise I'd have to setup the MITM interception again to see more details.
I've added functionality to automatically send the request if necessary (for instance, when a sync fails with DeviceManagementScreenlockRequired error) and then to re-send the original request if appropriate. If not all dependencies are met, it instead logs what is missing, like this:
GmsAccountErrorResolve com.google.android.gms W User intervention required! You need to ENABLE_GCM, ALLOW_MICROG_GCM, ENABLE_LOCKSCREEN.
ALLOW_MICROG_GCM means what we discussed above (it fails if a user disabled GCM for microG specifically, or requested to manually allow new apps).
The next step will be to add UI to guide users to resolve these problems.
For testing, I commented out all the rest of the web view code though, maybe that's the issue.
@aximut It shouldn't be an issue. After sending the query (and waiting its 500ms additional delay), it should work to sync Google apps and also to load the webview, which would otherwise close immediately. I don't know what the problem could be in your case.
Ok, then I'll try with the new version soon.
It seems the instance token + instance ID we are getting from GCM in https://github.com/microg/GmsCore/pull/2296/files#diff-3341dd523d63f6a4b7f5b44b9db7075c9a834e139fdc0fa945aafbb342942cceR48-R75 is somehow bad. The query works with it (which seems to indicate we are going in the right direction, as using a different sender constant for getting the instance ID – or no instance ID at all – would lead to a failing cryptauth request), but DeviceManagementScreenlockRequired keeps occurring even though the answer looks normal.
The same request with an instance ID taken from original GMS from the emulator works and leads to the account fully functioning.
I couldn't find out how original Google Play services generate their instance ID.
That would explain why the request went through on my device but I'm still facing the errors
For future investigation regarding the above problem:
- Instance ID generation should be attempted at client-side, following the pattern in the
play-services-iidimplementation, specifically: https://github.com/microg/GmsCore/blob/720823e5d40ce69890692350c549857d2671bf2b/play-services-iid/src/main/java/com/google/android/gms/iid/InstanceID.java#L274-L276 - Google Play services sometimes request the scope
DeviceKeyRequestwith sender/subtype/subscription121252257541. This sound relevant, but is always rejected by the server on grounds of being anInvalid scope.
Seemingly good senders / subtypes:
16502139086forCryptAuthV2EnrollmentAuthorizedEntityvia https://github.com/davgit/chromium/blob/f98299a83f66926792ef5f127a3f451325817df6/chromeos/services/device_sync/public/cpp/gcm_constants.cc#L13381449029288forCryptAuthGcmSenderIdvia https://github.com/davgit/chromium/blob/f98299a83f66926792ef5f127a3f451325817df6/chromeos/services/device_sync/public/cpp/gcm_constants.cc#L12C13-L12C33745476177629via Google Play services
More info:
121252257541is not even a valid sender302798585788leads to a token that leads to the cryptauth request being rejected on the grounds of containing an invalid argument.
It seems the instance token + instance ID we are getting from GCM in https://github.com/microg/GmsCore/pull/2296/files#diff-3341dd523d63f6a4b7f5b44b9db7075c9a834e139fdc0fa945aafbb342942cceR48-R75 is somehow bad.
The observations were correct, but the conclusion was false. Extensive tests show that:
- The behavior of the server that we were observing where copying the instance ID from a device with original GMS is likely the result of a server-side bug where it gets confused because instance ID and Android ID are mismatched.
- We need to complete the cryptauth flow by sending an EnrollKeys query, which constitutes the second half of the flow (as per https://github.com/chromium/chromium/blob/1de4545b5a22b1d290043b3bddd96a4cb3c730dd/chromeos/ash/services/device_sync/proto/cryptauth_enrollment.proto#L5-L17).
- It is sufficient to send a syntactically correct, but semantically empty EnrollKeys query, as long as it belongs to the SyncKeys query (identified per a random session token), even though this means not following the server's directives (which would tell the device that it should generate a key).
An implementation will follow very soon.
* We need to complete the cryptauth flow by sending an EnrollKeys query, which constitutes the second half of the flow (as per https://github.com/chromium/chromium/blob/1de4545b5a22b1d290043b3bddd96a4cb3c730dd/chromeos/ash/services/device_sync/proto/cryptauth_enrollment.proto#L5-L17).
Ok, this matches with what I observed and wrote in https://github.com/microg/GmsCore/issues/896#issuecomment-1848405154 Both requests were required when I tried it with the original app.
Interesting to hear that this is a bug on the server.
Ok, this matches with what I observed and wrote in https://github.com/microg/GmsCore/issues/896#issuecomment-1848405154 Both requests were required when I tried it with the original app.
Yes, the buggy behavior I described above really did point us in the wrong direction.
I have implemented the EnrollKeys step. @aximut If you want to try again, unlocking the device should now work with the latest changes on our basic-workspace branch.
I have implemented the EnrollKeys step. @aximut If you want to try again, unlocking the device should now work with the latest changes on our
basic-workspacebranch.
@fynngodau You are AMAZING!!! 🎉🎉🎉🎉🎉
It is working. Everything is working. 🥳🥳🥳🥳🥳 I can't believe it.
Wow, I can't count the years for how long I have been dealing with shabby workarounds to this, using everything in browser. Thank you so much!
So stoked. Made my ~~day~~ ~~week~~ month...
It seems the AccountManager already knows the logic of required user interaction, leaving the decision to the caller whether the required interaction should be done in the foreground immediately or whether to postpone it into a notification – which would be a good improvement.
When apps are instead using AuthManagerService, this wouldn't have any effect; however that service itself also seems to know of the same kind of logic. But if done at both places, like the account access permission prompt, we would further duplicate the code. I don't know if all this can be streamlined in some feasible way and don't want to make changes that have potential to break existing functionality.
There's an incomplete and preliminary attempt at moving towards returning an intent for the error case instead of posting to a notification ourselves at https://github.com/e-foundation/android_packages_apps_GmsCore/tree/basic-workspace-am-async. @mar-v-in what do you think?
For everyone, here's a screencast of the ux flow: https://gitlab.e.foundation/-/project/177/uploads/dbd0dbf91679dfc8610249019c1dc3e7/ux-flow-basic-workspace.mp4 (21.6 MB)
- I am already signed in to my account [email protected] and attempting to sync Google Calendar, which doesn't work as the account is not fully set up (i.e. the server does not know of any configured screenlock yet).
- Google Play services would send a notification in this situation as well.
- The flow guides me to create a screenlock. I choose pattern (creating the pattern itself is not shown on the video because Android prevents this from being visible on screenrecordings)
- After completing the flow, syncing would work
Merged manually to resolve conflicts. Thanks :)