Skip to content

Implement play-services-constellation#3359

Open
opstic wants to merge 18 commits intomicrog:masterfrom
opstic:constellation
Open

Implement play-services-constellation#3359
opstic wants to merge 18 commits intomicrog:masterfrom
opstic:constellation

Conversation

@opstic
Copy link
Copy Markdown

@opstic opstic commented Mar 24, 2026

This is PR 1 of 3 towards RCS support.
Related PRs: #3360, #3361
Related issue: #2994

Collectively these changes enable Google Messages to verify the phone number via UPI and retrieve the provisioning document.

In my testing, setup progresses through verification, provisioning and reaches a connected state in Google Messages.
The remaining failure occurs during Tachyon registration. One possible cause is that the final DroidGuard tachyon_registration challenge is not being satisfied correctly in my test environment.

Testing from people with environments that pass DroidGuard/Play Integrity would be appreciated, especially with logs/network captures if possible. Many thanks!

Description

This PR implements the Constellation service, including:

  • verifyPhoneNumberV1
  • verifyPhoneNumberSingleUse
  • verifyPhoneNumber
  • getIidToken
  • getPnvCapabilities

getIidToken and verifyPhoneNumber appear to be the main APIs Google Messages would call.

Most of the verification paths are hopefully implemented correctly, only the TS.43 path was tested.
Flashcall is excluded for now as it's implementation in GMS requires a hidden API in Android SDK.

The gRPC proto definitions and client implementation are also used by play-services-asterism (PR #3360)

Screenshots

Screenshot_20260324-000518_Messages Screenshot_20260324-000550_Messages

@mar-v-in
Copy link
Copy Markdown
Member

It will take a while for this to be reviewed, but I'm happy to finally see someone actually tackling the issue instead of having an LLM generate bullshit. 👏

@ale5000-git
Copy link
Copy Markdown
Member

@opstic
There is an error in the build:
error: Field verifications in com.google.android.gms.constellation.VerifyPhoneNumberResponse has unsupported type @org.jetbrains.annotations.NotNull com.google.android.gms.constellation.PhoneNumberVerification in @org.jetbrains.annotations.NotNull com.google.android.gms.constellation.PhoneNumberVerification[].

@mar-v-in
Copy link
Copy Markdown
Member

mar-v-in commented Mar 24, 2026

@opstic We generally try to sort things into two modules:

  • A library module, which includes all code needed to connect to the service, that is aidl, parcelables, potential client code, constants/enums and is pure Java (no Kotlin).
  • An implementation module, typically called -core, that has the relevant services and actual logic implementations. Here we often use Kotlin.

The error @ale5000-git mentioned is probably due to having safe parcelable classes in Kotlin (which was never supported or tested), meaning they have automatically generated annotations that shouldn't be there. The Kotlin code also somewhat looks like it was automatically converted from Java (which is not always 100% safe, as seen here).

For this API specifically, because it is an internal API of Google, you can also just add it to the play-services-api module which is the catchall for all APIs that don't have a corresponding official Google client library (meaning they don't show up in this list). The service side should then go in the play-services-core module.

@opstic
Copy link
Copy Markdown
Author

opstic commented Mar 24, 2026

@mar-v-in
I've debugged and found that the field.listItemType on that specific field resolves to annotated names, which would fail the getTypeElement call, and thus fail isParcelable, isInterface and come out unsupported.
This doesn't happen on my Windows host where it has it's full name of com.google.android.gms.constellation.PhoneNumberVerification without the annotation so it gets resolved correctly

The @JvmField were workarounds to avoid safe-parcel-processor using reflection to access the fields, I'll be converting these into Java then.

Also I'll restructure a bit to fit the sorting better.

@mar-v-in
Copy link
Copy Markdown
Member

@opstic The issue is that it sees the type as @org.jetbrains.annotations.NotNull com.google.android.gms.constellation.PhoneNumberVerification due to the additional annotation on the type. The Java preprocessor for safe parcelables only supports field annotations, not type annotations, but the Kotlin to JVM compiler seems to add the @org.jetbrains.annotations.NotNull annotation to the type. Not sure why this is different on your system than the CI build. I'm not sure if there is an easy way to make Kotlin compiler not add those, but even easier would be to just continue with API classes being in pure Java, because then you can just not have the annotation on the type and also use the Android version of the NonNull annotation rather than the Jetbrains/Kotlin variant.

@opstic
Copy link
Copy Markdown
Author

opstic commented Mar 24, 2026

@mar-v-in
The code is quite large and shoving it into play-services-core doesn't look too well to me, can I just do something like play-services-droidguard where there's a core folder inside that handles the service logic?
oops didn't read what you said carefully enough, I'll sort it out with play-services-constellation and play-services-constellation-core then

@opstic
Copy link
Copy Markdown
Author

opstic commented Mar 26, 2026

Okay, after some more testing I've determined it's a Java version issue. The annotations aren't passed in with Java 21 so it builds successfully.

I'm still really hoping to keep the parcelables in kotlin as it would be easier for me and the ergonomics feel better but if it's necessary to be in Java and/or updating CI Java to 21 isn't acceptable I'm happy to move it over as well.

I'll push the -core restructuring shortly.

@ale5000-git
Copy link
Copy Markdown
Member

ale5000-git commented Mar 26, 2026

updating CI Java to 21

I hope this will never happen since it will break compatibility with old Android versions.

@ale5000-git
Copy link
Copy Markdown
Member

ale5000-git commented Mar 26, 2026

@opstic
When you try the code locally on your pc I suggest to try a complete gradlew build.

IMPORTANT: Do not use gradle, but gradlew; since using gradle won't use the intended gradle version and maybe it will hide possible errors.

NOTE: The full build also include the lint phase; with just assemble you won't see lint errors.

PS: Thanks for the good works on the PR :-)

@opstic
Copy link
Copy Markdown
Author

opstic commented Mar 27, 2026

Small problem, I am cleaning up by looking at the lint results and I am pretty sure Google Messages is the oldest thing that calls this service, which only goes back to as far as Nougat for meaningful versions.

Any standard method to exclude this module entirely for SDKs under Nougat? I am not a fan of putting @RequireApi(21/26) in front of every function in the module.

Also I've debugged for RCS in Nougat Google Messages, it would first compare if the mnc_mcc of the phenotypes match what's in the phone, then do standard self-contained OTP provisioning (instead of UPI) while only needing to call getIidToken.

But much of the code uses java.time.Instant including the actual protobuf structures, which is introduced in Oreo. So I'm wondering if we can ditch Nougat (and RCS support for Nougat) entirely and restrict to ≥ Oreo as that would result in much less annotations needed (If there's a way to split that cleanly).

@mar-v-in
Copy link
Copy Markdown
Member

I don't see us bumping the minimum API level to 26 any time soon. Google's Play Services has minimum API level of 21. We're currently at 19 and I plan to bump to 21 soonish to be able to make wider use of Compose.

Have you considered applying the annotation to the whole file (via @file:RequiresApi(26))? That's still going to be in a bunch of files, but at least not on every individual function.

@opstic
Copy link
Copy Markdown
Author

opstic commented Mar 27, 2026

Alright, that's much better than applying to each function at least.

Thanks for the quick response!

@opstic
Copy link
Copy Markdown
Author

opstic commented Mar 29, 2026

Alright, I think I'm done refactoring everything for now.

The play-services-asterism PR was also refactored to split out -core

Everything should be ready for review.

Also, here's a built debug APK artifact from CI that includes all the necessary changes together to make testing easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants