From 5991207cfe744103d6e7c14b40c62ca8184e615a Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Tue, 24 Mar 2026 01:26:55 -0400
Subject: [PATCH 01/31] Implement `play-services-constellation`
---
play-services-constellation/build.gradle | 73 ++
.../src/main/AndroidManifest.xml | 17 +
.../gms/constellation/GetIidTokenRequest.aidl | 3 +
.../constellation/GetIidTokenResponse.aidl | 3 +
.../GetPnvCapabilitiesRequest.aidl | 3 +
.../GetPnvCapabilitiesResponse.aidl | 3 +
.../gms/constellation/PhoneNumberInfo.aidl | 3 +
.../VerifyPhoneNumberRequest.aidl | 3 +
.../VerifyPhoneNumberResponse.aidl | 3 +
.../internal/IConstellationApiService.aidl | 39 +
.../internal/IConstellationCallbacks.aidl | 15 +
.../android/gms/constellation/GetIidToken.kt | 43 +
.../gms/constellation/GetPnvCapabilities.kt | 87 ++
.../gms/constellation/PhoneNumberInfo.kt | 28 +
.../gms/constellation/VerifyPhoneNumber.kt | 95 ++
.../microg/gms/constellation/AuthManager.kt | 102 ++
.../constellation/ConstellationApiService.kt | 124 ++
.../constellation/ConstellationStateStore.kt | 180 +++
.../org/microg/gms/constellation/GServices.kt | 33 +
.../microg/gms/constellation/GetIidToken.kt | 35 +
.../gms/constellation/GetPnvCapabilities.kt | 120 ++
.../GetPnvCapabilitiesApiPhenotype.kt | 5 +
.../constellation/GetVerifiedPhoneNumbers.kt | 137 +++
.../gms/constellation/IidTokenPhenotypes.kt | 9 +
.../org/microg/gms/constellation/RpcClient.kt | 47 +
.../gms/constellation/VerificationMappings.kt | 134 +++
.../VerificationSettingsPhenotypes.kt | 6 +
.../gms/constellation/VerifyPhoneNumber.kt | 346 ++++++
.../VerifyPhoneNumberApiPhenotypes.kt | 37 +
.../proto/builders/ClientInfoBuilder.kt | 173 +++
.../proto/builders/CommonBuilders.kt | 70 ++
.../proto/builders/GaiaInfoBuilder.kt | 74 ++
.../proto/builders/RequestBuildContext.kt | 20 +
.../proto/builders/SyncRequestBuilder.kt | 190 +++
.../proto/builders/TelephonyInfoBuilder.kt | 143 +++
.../verification/CarrierIdVerifier.kt | 84 ++
.../verification/ChallengeProcessor.kt | 152 +++
.../verification/MoSmsVerifier.kt | 210 ++++
.../verification/MtSmsVerifier.kt | 95 ++
.../verification/RegisteredSmsVerifier.kt | 159 +++
.../verification/Ts43Verifier.kt | 431 +++++++
.../verification/ts43/EapAkaService.kt | 211 ++++
.../verification/ts43/Fips186Prf.kt | 108 ++
.../ts43/ServiceEntitlementExtension.kt | 200 ++++
.../src/main/proto/constellation.proto | 1025 +++++++++++++++++
play-services-core/build.gradle | 1 +
.../src/main/AndroidManifest.xml | 11 +-
.../google/android/gms/iid/InstanceID.java | 20 +-
settings.gradle | 1 +
49 files changed, 5108 insertions(+), 3 deletions(-)
create mode 100644 play-services-constellation/build.gradle
create mode 100644 play-services-constellation/src/main/AndroidManifest.xml
create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenRequest.aidl
create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenResponse.aidl
create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.aidl
create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.aidl
create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/PhoneNumberInfo.aidl
create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberRequest.aidl
create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberResponse.aidl
create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationApiService.aidl
create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationCallbacks.aidl
create mode 100644 play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetIidToken.kt
create mode 100644 play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
create mode 100644 play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt
create mode 100644 play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/AuthManager.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationApiService.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationStateStore.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GServices.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetIidToken.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilities.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilitiesApiPhenotype.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetVerifiedPhoneNumbers.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/IidTokenPhenotypes.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/RpcClient.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationMappings.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationSettingsPhenotypes.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumber.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumberApiPhenotypes.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/ClientInfoBuilder.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/CommonBuilders.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/GaiaInfoBuilder.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/RequestBuildContext.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/SyncRequestBuilder.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/TelephonyInfoBuilder.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/CarrierIdVerifier.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ChallengeProcessor.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MoSmsVerifier.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MtSmsVerifier.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/RegisteredSmsVerifier.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/Ts43Verifier.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/EapAkaService.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/Fips186Prf.kt
create mode 100644 play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/ServiceEntitlementExtension.kt
create mode 100644 play-services-constellation/src/main/proto/constellation.proto
diff --git a/play-services-constellation/build.gradle b/play-services-constellation/build.gradle
new file mode 100644
index 0000000000..2301c04063
--- /dev/null
+++ b/play-services-constellation/build.gradle
@@ -0,0 +1,73 @@
+/*
+ * SPDX-FileCopyrightText: 2026, microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'maven-publish'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-parcelize'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'signing'
+
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.squareup.wire:wire-gradle-plugin:4.9.3'
+ }
+}
+apply plugin: 'com.squareup.wire'
+
+android {
+ namespace "org.microg.gms.constellation"
+
+ compileSdkVersion androidCompileSdk
+ buildToolsVersion "$androidBuildVersionTools"
+
+ buildFeatures {
+ aidl = true
+ }
+
+ defaultConfig {
+ versionName version
+ minSdkVersion androidMinSdk
+ targetSdkVersion androidTargetSdk
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = 1.8
+ }
+}
+
+wire {
+ kotlin {
+ rpcRole = 'client'
+ rpcCallStyle = 'suspending'
+ }
+}
+
+apply from: '../gradle/publish-android.gradle'
+
+description = 'microG service implementation for play-services-constellation'
+
+dependencies {
+ implementation project(':play-services-base-core')
+ implementation project(':play-services-iid')
+ implementation project(':play-services-auth-base')
+
+ implementation project(':play-services-droidguard')
+ implementation project(':play-services-tasks-ktx')
+
+ api 'com.squareup.wire:wire-runtime:4.9.3'
+ api 'com.squareup.wire:wire-grpc-client:4.9.3'
+ api 'com.squareup.okhttp3:okhttp:4.12.0'
+
+ kapt project(":safe-parcel-processor")
+}
diff --git a/play-services-constellation/src/main/AndroidManifest.xml b/play-services-constellation/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..bf92ddcaf3
--- /dev/null
+++ b/play-services-constellation/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+
+
+
diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenRequest.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenRequest.aidl
new file mode 100644
index 0000000000..4621cec67a
--- /dev/null
+++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenRequest.aidl
@@ -0,0 +1,3 @@
+package com.google.android.gms.constellation;
+
+parcelable GetIidTokenRequest;
diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenResponse.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenResponse.aidl
new file mode 100644
index 0000000000..832407aa6e
--- /dev/null
+++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenResponse.aidl
@@ -0,0 +1,3 @@
+package com.google.android.gms.constellation;
+
+parcelable GetIidTokenResponse;
diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.aidl
new file mode 100644
index 0000000000..ffd7ecad31
--- /dev/null
+++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.aidl
@@ -0,0 +1,3 @@
+package com.google.android.gms.constellation;
+
+parcelable GetPnvCapabilitiesRequest;
\ No newline at end of file
diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.aidl
new file mode 100644
index 0000000000..1b318c7ec7
--- /dev/null
+++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.aidl
@@ -0,0 +1,3 @@
+package com.google.android.gms.constellation;
+
+parcelable GetPnvCapabilitiesResponse;
\ No newline at end of file
diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/PhoneNumberInfo.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/PhoneNumberInfo.aidl
new file mode 100644
index 0000000000..b6889c7bd4
--- /dev/null
+++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/PhoneNumberInfo.aidl
@@ -0,0 +1,3 @@
+package com.google.android.gms.constellation;
+
+parcelable PhoneNumberInfo;
diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberRequest.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberRequest.aidl
new file mode 100644
index 0000000000..f5ca759758
--- /dev/null
+++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberRequest.aidl
@@ -0,0 +1,3 @@
+package com.google.android.gms.constellation;
+
+parcelable VerifyPhoneNumberRequest;
diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberResponse.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberResponse.aidl
new file mode 100644
index 0000000000..31eb20049c
--- /dev/null
+++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberResponse.aidl
@@ -0,0 +1,3 @@
+package com.google.android.gms.constellation;
+
+parcelable VerifyPhoneNumberResponse;
diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationApiService.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationApiService.aidl
new file mode 100644
index 0000000000..912acc7371
--- /dev/null
+++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationApiService.aidl
@@ -0,0 +1,39 @@
+package com.google.android.gms.constellation.internal;
+
+import com.google.android.gms.common.api.ApiMetadata;
+import com.google.android.gms.constellation.internal.IConstellationCallbacks;
+import com.google.android.gms.constellation.GetIidTokenRequest;
+import com.google.android.gms.constellation.GetPnvCapabilitiesRequest;
+import com.google.android.gms.constellation.VerifyPhoneNumberRequest;
+
+interface IConstellationApiService {
+ void verifyPhoneNumberV1(
+ IConstellationCallbacks cb,
+ in Bundle bundle,
+ in ApiMetadata apiMetadata
+ );
+
+ void verifyPhoneNumberSingleUse(
+ IConstellationCallbacks cb,
+ in Bundle bundle,
+ in ApiMetadata apiMetadata
+ );
+
+ void verifyPhoneNumber(
+ IConstellationCallbacks cb,
+ in VerifyPhoneNumberRequest request,
+ in ApiMetadata apiMetadata
+ );
+
+ void getIidToken(
+ IConstellationCallbacks cb,
+ in GetIidTokenRequest request,
+ in ApiMetadata apiMetadata
+ );
+
+ void getPnvCapabilities(
+ IConstellationCallbacks cb,
+ in GetPnvCapabilitiesRequest request,
+ in ApiMetadata apiMetadata
+ );
+}
diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationCallbacks.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationCallbacks.aidl
new file mode 100644
index 0000000000..74ce532b86
--- /dev/null
+++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationCallbacks.aidl
@@ -0,0 +1,15 @@
+package com.google.android.gms.constellation.internal;
+
+import com.google.android.gms.common.api.ApiMetadata;
+import com.google.android.gms.common.api.Status;
+import com.google.android.gms.constellation.GetIidTokenResponse;
+import com.google.android.gms.constellation.GetPnvCapabilitiesResponse;
+import com.google.android.gms.constellation.PhoneNumberInfo;
+import com.google.android.gms.constellation.VerifyPhoneNumberResponse;
+
+oneway interface IConstellationCallbacks {
+ void onPhoneNumberVerified(in Status status, in List phoneNumbers, in ApiMetadata apiMetadata);
+ void onPhoneNumberVerificationsCompleted(in Status status, in VerifyPhoneNumberResponse response, in ApiMetadata apiMetadata);
+ void onIidTokenGenerated(in Status status, in GetIidTokenResponse response, in ApiMetadata apiMetadata);
+ void onGetPnvCapabilitiesCompleted(in Status status, in GetPnvCapabilitiesResponse response, in ApiMetadata apiMetadata);
+}
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetIidToken.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetIidToken.kt
new file mode 100644
index 0000000000..53760184ab
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetIidToken.kt
@@ -0,0 +1,43 @@
+package com.google.android.gms.constellation
+
+import android.os.Parcel
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Constructor
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter
+
+@SafeParcelable.Class
+data class GetIidTokenRequest @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val projectNumber: Long?
+) : AbstractSafeParcelable() {
+ override fun writeToParcel(out: Parcel, flags: Int) {
+ CREATOR.writeToParcel(this, out, flags)
+ }
+
+ companion object {
+ @JvmField
+ val CREATOR: SafeParcelableCreatorAndWriter =
+ findCreator(GetIidTokenRequest::class.java)
+ }
+}
+
+@SafeParcelable.Class
+data class GetIidTokenResponse @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val iidToken: String,
+ @JvmField @Param(2) @Field(2) val fid: String,
+ @JvmField @Param(3) @Field(value = 3, type = "byte[]") val signature: ByteArray?,
+ @JvmField @Param(4) @Field(4) val timestamp: Long
+) : AbstractSafeParcelable() {
+
+ override fun writeToParcel(out: Parcel, flags: Int) {
+ CREATOR.writeToParcel(this, out, flags)
+ }
+
+ companion object {
+ @JvmField
+ val CREATOR: SafeParcelableCreatorAndWriter =
+ findCreator(GetIidTokenResponse::class.java)
+ }
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
new file mode 100644
index 0000000000..4598d98bb1
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
@@ -0,0 +1,87 @@
+package com.google.android.gms.constellation
+
+import android.os.Parcel
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Constructor
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
+
+@SafeParcelable.Class
+data class GetPnvCapabilitiesRequest @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val policyId: String,
+ @JvmField @Param(2) @Field(2) val verificationTypes: List,
+ @JvmField @Param(3) @Field(3) val simSlotIndices: List
+) : AbstractSafeParcelable() {
+ override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
+
+ companion object {
+ @JvmField
+ val CREATOR = findCreator(GetPnvCapabilitiesRequest::class.java)
+ }
+}
+
+@SafeParcelable.Class
+data class GetPnvCapabilitiesResponse @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val simCapabilities: List
+) : AbstractSafeParcelable() {
+ override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
+
+ companion object {
+ @JvmField
+ val CREATOR = findCreator(GetPnvCapabilitiesResponse::class.java)
+ }
+}
+
+@SafeParcelable.Class
+data class SimCapability @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val slotValue: Int,
+ @JvmField @Param(2) @Field(2) val subscriberIdDigest: String,
+ @JvmField @Param(3) @Field(3) val carrierId: Int,
+ @JvmField @Param(4) @Field(4) val operatorName: String,
+ @JvmField @Param(5) @Field(5) val verificationCapabilities: List
+) : AbstractSafeParcelable() {
+ override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
+
+ companion object {
+ @JvmField
+ val CREATOR = findCreator(SimCapability::class.java)
+ }
+}
+
+@SafeParcelable.Class
+data class VerificationCapability @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val verificationMethod: Int,
+ @JvmField @Param(2) @Field(2) val statusValue: Int // Enums are typically passed as Ints in SafeParcelable
+) : AbstractSafeParcelable() {
+ constructor(verificationMethod: Int, status: VerificationStatus) : this(
+ verificationMethod,
+ status.value
+ )
+
+ val status: VerificationStatus
+ get() = VerificationStatus.fromInt(statusValue)
+
+ override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
+
+ companion object {
+ @JvmField
+ val CREATOR = findCreator(VerificationCapability::class.java)
+ }
+}
+
+enum class VerificationStatus(val value: Int) {
+ SUPPORTED(1),
+ UNSUPPORTED_CARRIER(2),
+ UNSUPPORTED_API_VERSION(3),
+ UNSUPPORTED_SIM_NOT_READY(4);
+
+ companion object {
+ fun fromInt(value: Int): VerificationStatus = when (value) {
+ 2 -> UNSUPPORTED_CARRIER
+ 3 -> UNSUPPORTED_API_VERSION
+ 4 -> UNSUPPORTED_SIM_NOT_READY
+ else -> SUPPORTED
+ }
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt
new file mode 100644
index 0000000000..459a05b3cd
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt
@@ -0,0 +1,28 @@
+package com.google.android.gms.constellation
+
+import android.os.Bundle
+import android.os.Parcel
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Constructor
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter
+
+@SafeParcelable.Class
+data class PhoneNumberInfo @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val version: Int,
+ @JvmField @Param(2) @Field(2) val phoneNumber: String?,
+ @JvmField @Param(3) @Field(3) val verificationTime: Long,
+ @JvmField @Param(4) @Field(4) val extras: Bundle?
+) : AbstractSafeParcelable() {
+
+ override fun writeToParcel(out: Parcel, flags: Int) {
+ CREATOR.writeToParcel(this, out, flags)
+ }
+
+ companion object {
+ @JvmField
+ val CREATOR = findCreator(PhoneNumberInfo::class.java)
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
new file mode 100644
index 0000000000..e2f159a44c
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
@@ -0,0 +1,95 @@
+package com.google.android.gms.constellation
+
+import android.os.Bundle
+import android.os.Parcel
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Constructor
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter
+import org.microg.gms.constellation.proto.VerificationMethod
+
+@SafeParcelable.Class
+data class VerifyPhoneNumberRequest @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val policyId: String,
+ @JvmField @Param(2) @Field(2) val timeout: Long,
+ @JvmField @Param(3) @Field(3) val idTokenRequest: IdTokenRequest,
+ @JvmField @Param(4) @Field(4) val extras: Bundle,
+ @JvmField @Param(5) @Field(5) val targetedSims: List,
+ @JvmField @Param(6) @Field(6) val silent: Boolean,
+ @JvmField @Param(7) @Field(7) val apiVersion: Int,
+ @JvmField @Param(8) @Field(8) val verificationTypes: List
+) : AbstractSafeParcelable() {
+ val verificationMethods: List
+ get() = verificationTypes.mapNotNull { VerificationMethod.fromValue(it) }
+
+ override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
+
+ companion object {
+ @JvmField
+ val CREATOR = findCreator(VerifyPhoneNumberRequest::class.java)
+ }
+}
+
+@SafeParcelable.Class
+data class IdTokenRequest @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val idToken: String,
+ @JvmField @Param(2) @Field(2) val subscriberHash: String
+) : AbstractSafeParcelable() {
+
+ override fun writeToParcel(out: Parcel, flags: Int) {
+ CREATOR.writeToParcel(this, out, flags)
+ }
+
+ companion object {
+ @JvmField
+ val CREATOR: SafeParcelableCreatorAndWriter =
+ findCreator(IdTokenRequest::class.java)
+ }
+}
+
+@SafeParcelable.Class
+data class ImsiRequest @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val imsi: String,
+ @JvmField @Param(2) @Field(2) val phoneNumberHint: String
+) : AbstractSafeParcelable() {
+ override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
+
+ companion object {
+ @JvmField
+ val CREATOR = findCreator(ImsiRequest::class.java)
+ }
+}
+
+@SafeParcelable.Class
+data class VerifyPhoneNumberResponse @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val verifications: Array,
+ @JvmField @Param(2) @Field(2) val extras: Bundle
+) : AbstractSafeParcelable() {
+ override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
+
+ companion object {
+ @JvmField
+ val CREATOR = findCreator(VerifyPhoneNumberResponse::class.java)
+ }
+}
+
+@SafeParcelable.Class
+data class PhoneNumberVerification @Constructor constructor(
+ @JvmField @Param(1) @Field(1) val phoneNumber: String?,
+ @JvmField @Param(2) @Field(2) val timestampMillis: Long,
+ @JvmField @Param(3) @Field(3) val verificationMethod: Int,
+ @JvmField @Param(4) @Field(4) val simSlot: Int,
+ @JvmField @Param(5) @Field(5) val verificationToken: String?,
+ @JvmField @Param(6) @Field(6) val extras: Bundle?,
+ @JvmField @Param(7) @Field(7) val verificationStatus: Int,
+ @JvmField @Param(8) @Field(8) val retryAfterSeconds: Long
+) : AbstractSafeParcelable() {
+ override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
+
+ companion object {
+ @JvmField
+ val CREATOR = findCreator(PhoneNumberVerification::class.java)
+ }
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/AuthManager.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/AuthManager.kt
new file mode 100644
index 0000000000..148d585ed3
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/AuthManager.kt
@@ -0,0 +1,102 @@
+package org.microg.gms.constellation
+
+import android.content.Context
+import android.util.Base64
+import androidx.core.content.edit
+import com.google.android.gms.iid.InstanceID
+import com.squareup.wire.Instant
+import java.nio.charset.StandardCharsets
+import java.security.KeyFactory
+import java.security.KeyPair
+import java.security.KeyPairGenerator
+import java.security.Signature
+import java.security.spec.PKCS8EncodedKeySpec
+import java.security.spec.X509EncodedKeySpec
+
+class AuthManager(context: Context) {
+ private val context = context.applicationContext
+
+ companion object {
+ private const val PREFS_NAME = "constellation_prefs"
+ private const val KEY_PRIVATE = "private_key"
+ private const val KEY_PUBLIC = "public_key"
+
+ @Volatile
+ private var instance: AuthManager? = null
+
+ fun get(context: Context): AuthManager {
+ val existing = instance
+ if (existing != null) return existing
+
+ return synchronized(this) {
+ instance ?: AuthManager(context).also { instance = it }
+ }
+ }
+ }
+
+ private val sharedPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
+
+ // GMS signing format: {iidToken}:{seconds}:{nanos}
+ fun signIidToken(iidToken: String): Pair {
+ val now = System.currentTimeMillis()
+ val timestamp = Instant.ofEpochMilli(now)
+ val content = "$iidToken:${timestamp.epochSecond}:${timestamp.nano}"
+ return sign(content) to timestamp
+ }
+
+ fun getIidToken(projectNumber: String? = null): String {
+ return try {
+ val sender = projectNumber ?: IidTokenPhenotypes.DEFAULT_PROJECT_NUMBER.toString()
+ InstanceID.getInstance(context).getToken(sender, "GCM")
+ } catch (_: Exception) {
+ ""
+ }
+ }
+
+ fun getOrCreateKeyPair(): KeyPair {
+ val privateKeyStr = sharedPrefs.getString(KEY_PRIVATE, null)
+ val publicKeyStr = sharedPrefs.getString(KEY_PUBLIC, null)
+
+ if (privateKeyStr != null && publicKeyStr != null) {
+ try {
+ val kf = KeyFactory.getInstance("EC")
+ val privateKey = kf.generatePrivate(
+ PKCS8EncodedKeySpec(Base64.decode(privateKeyStr, Base64.DEFAULT))
+ )
+ val publicKey = kf.generatePublic(
+ X509EncodedKeySpec(Base64.decode(publicKeyStr, Base64.DEFAULT))
+ )
+ return KeyPair(publicKey, privateKey)
+ } catch (_: Exception) {
+ // Fall through to regeneration on failure
+ }
+ }
+
+ val kpg = KeyPairGenerator.getInstance("EC")
+ kpg.initialize(256)
+ val kp = kpg.generateKeyPair()
+
+ sharedPrefs.edit {
+ putString(KEY_PRIVATE, Base64.encodeToString(kp.private.encoded, Base64.NO_WRAP))
+ putString(KEY_PUBLIC, Base64.encodeToString(kp.public.encoded, Base64.NO_WRAP))
+ }
+
+ return kp
+ }
+
+ fun sign(content: String): ByteArray {
+ return try {
+ val kp = getOrCreateKeyPair()
+ val signature = Signature.getInstance("SHA256withECDSA")
+ signature.initSign(kp.private)
+ signature.update(content.toByteArray(StandardCharsets.UTF_8))
+ signature.sign()
+ } catch (_: Exception) {
+ ByteArray(0)
+ }
+ }
+
+ fun getFid(): String {
+ return InstanceID.getInstance(context).id
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationApiService.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationApiService.kt
new file mode 100644
index 0000000000..a6829f734b
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationApiService.kt
@@ -0,0 +1,124 @@
+package org.microg.gms.constellation
+
+import android.content.Context
+import android.os.Bundle
+import android.util.Log
+import com.google.android.gms.common.Feature
+import com.google.android.gms.common.api.ApiMetadata
+import com.google.android.gms.common.internal.ConnectionInfo
+import com.google.android.gms.common.internal.GetServiceRequest
+import com.google.android.gms.common.internal.IGmsCallbacks
+import com.google.android.gms.constellation.GetIidTokenRequest
+import com.google.android.gms.constellation.GetPnvCapabilitiesRequest
+import com.google.android.gms.constellation.VerifyPhoneNumberRequest
+import com.google.android.gms.constellation.internal.IConstellationApiService
+import com.google.android.gms.constellation.internal.IConstellationCallbacks
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+import org.microg.gms.BaseService
+import org.microg.gms.common.GmsService
+import org.microg.gms.common.PackageUtils
+
+private const val TAG = "C11NApiService"
+
+class ConstellationApiService : BaseService(TAG, GmsService.CONSTELLATION) {
+ private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
+
+ override fun handleServiceRequest(
+ callback: IGmsCallbacks?,
+ request: GetServiceRequest?,
+ service: GmsService?
+ ) {
+ val packageName = PackageUtils.getAndCheckCallingPackage(this, request?.packageName)
+ if (!PackageUtils.isGooglePackage(this, packageName)) {
+ throw SecurityException("$packageName is not a Google package")
+ }
+ callback!!.onPostInitCompleteWithConnectionInfo(
+ 0,
+ ConstellationApiServiceImpl(this, packageName, serviceScope).asBinder(),
+ ConnectionInfo().apply {
+ features = arrayOf(
+ Feature("asterism_consent", 3),
+ Feature("one_time_verification", 1),
+ Feature("carrier_auth", 1),
+ Feature("verify_phone_number", 2),
+ Feature("get_iid_token", 1),
+ Feature("get_pnv_capabilities", 1),
+ Feature("ts43", 1),
+ Feature("verify_phone_number_local_read", 1)
+ )
+ })
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ serviceScope.cancel("ConstellationApiService destroyed")
+ }
+}
+
+class ConstellationApiServiceImpl(
+ private val context: Context,
+ private val packageName: String?,
+ private val serviceScope: CoroutineScope
+) : IConstellationApiService.Stub() {
+ override fun verifyPhoneNumberV1(
+ cb: IConstellationCallbacks?,
+ bundle: Bundle?,
+ apiMetadata: ApiMetadata?
+ ) {
+ Log.i(
+ TAG,
+ "verifyPhoneNumberV1(): mode=${bundle?.getInt("verification_mode")}, policy=${
+ bundle?.getString("policy_id")
+ }"
+ )
+ if (cb == null || bundle == null) return
+ serviceScope.launch { handleVerifyPhoneNumberV1(context, cb, bundle, packageName) }
+ }
+
+ override fun verifyPhoneNumberSingleUse(
+ cb: IConstellationCallbacks?,
+ bundle: Bundle?,
+ apiMetadata: ApiMetadata?
+ ) {
+ Log.i(TAG, "verifyPhoneNumberSingleUse()")
+ if (cb == null || bundle == null) return
+ serviceScope.launch { handleVerifyPhoneNumberSingleUse(context, cb, bundle, packageName) }
+ }
+
+ override fun verifyPhoneNumber(
+ cb: IConstellationCallbacks?,
+ request: VerifyPhoneNumberRequest?,
+ apiMetadata: ApiMetadata?
+ ) {
+ Log.i(
+ TAG,
+ "verifyPhoneNumber(): apiVersion=${request?.apiVersion}, policy=${request?.policyId}"
+ )
+ if (cb == null || request == null) return
+ serviceScope.launch { handleVerifyPhoneNumberRequest(context, cb, request, packageName) }
+ }
+
+ override fun getIidToken(
+ cb: IConstellationCallbacks?,
+ request: GetIidTokenRequest?,
+ apiMetadata: ApiMetadata?,
+ ) {
+ Log.i(TAG, "getIidToken(): $request")
+ if (cb == null || request == null) return
+ serviceScope.launch { handleGetIidToken(context, cb, request) }
+ }
+
+ override fun getPnvCapabilities(
+ cb: IConstellationCallbacks?,
+ request: GetPnvCapabilitiesRequest?,
+ apiMetadata: ApiMetadata?,
+ ) {
+ Log.i(TAG, "getPnvCapabilities(): $request")
+ if (cb == null || request == null) return
+ serviceScope.launch { handleGetPnvCapabilities(context, cb, request) }
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationStateStore.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationStateStore.kt
new file mode 100644
index 0000000000..5c6f07bd08
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationStateStore.kt
@@ -0,0 +1,180 @@
+package org.microg.gms.constellation
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.Base64
+import androidx.core.content.edit
+import com.squareup.wire.Instant
+import okio.ByteString.Companion.toByteString
+import org.microg.gms.constellation.proto.DroidguardToken
+import org.microg.gms.constellation.proto.ProceedResponse
+import org.microg.gms.constellation.proto.Consent
+import org.microg.gms.constellation.proto.ConsentSource
+import org.microg.gms.constellation.proto.ConsentVersion
+import org.microg.gms.constellation.proto.ServerTimestamp
+import org.microg.gms.constellation.proto.SyncResponse
+import org.microg.gms.constellation.proto.VerificationToken
+
+private const val STATE_PREFS_NAME = "constellation_prefs"
+private const val TOKEN_PREFS_NAME = "com.google.android.gms.constellation"
+private const val KEY_VERIFICATION_TOKENS = "verification_tokens_v1"
+private const val KEY_DROIDGUARD_TOKEN = "droidguard_token"
+private const val KEY_DROIDGUARD_TOKEN_TTL = "droidguard_token_ttl"
+private const val KEY_NEXT_SYNC_TIMESTAMP_MS = "next_sync_timestamp_in_millis"
+private const val KEY_PUBLIC_KEY_ACKED = "is_public_key_acked"
+private const val KEY_PNVR_NOTICE_CONSENT = "pnvr_notice_consent"
+private const val KEY_PNVR_NOTICE_SOURCE = "pnvr_notice_source"
+private const val KEY_PNVR_NOTICE_VERSION = "pnvr_notice_version"
+private const val KEY_PNVR_NOTICE_UPDATED_AT_MS = "pnvr_notice_updated_at_ms"
+
+object ConstellationStateStore {
+ fun loadVerificationTokens(context: Context): List {
+ val prefs = tokenPrefs(context)
+ val serialized = prefs.getString(KEY_VERIFICATION_TOKENS, null) ?: return emptyList()
+ return serialized.split(",").mapNotNull { entry ->
+ val parts = entry.split("|", limit = 2)
+ if (parts.size != 2) return@mapNotNull null
+ val tokenBytes = runCatching {
+ Base64.decode(parts[0], Base64.DEFAULT).toByteString()
+ }.getOrNull() ?: return@mapNotNull null
+ val expirationMillis = parts[1].toLongOrNull() ?: return@mapNotNull null
+ if (expirationMillis <= System.currentTimeMillis()) return@mapNotNull null
+ VerificationToken(
+ token = tokenBytes,
+ expiration_time = Instant.ofEpochMilli(expirationMillis)
+ )
+ }
+ }
+
+ fun storeSyncResponse(context: Context, response: SyncResponse) {
+ storeVerificationTokens(context, response.verification_tokens)
+ storeDroidGuardToken(context, response.droidguard_token)
+ storeNextSyncTime(context, response.next_sync_time)
+ }
+
+ fun storeProceedResponse(context: Context, response: ProceedResponse) {
+ storeDroidGuardToken(context, response.droidguard_token)
+ storeNextSyncTime(context, response.next_sync_time)
+ }
+
+ fun loadDroidGuardToken(context: Context): String? {
+ val statePrefs = statePrefs(context)
+ var token = statePrefs.getString(KEY_DROIDGUARD_TOKEN, null)
+ var expiration = statePrefs.getLong(KEY_DROIDGUARD_TOKEN_TTL, 0L)
+
+ if (token.isNullOrBlank() || expiration == 0L) {
+ val legacyPrefs = tokenPrefs(context)
+ val legacyToken = legacyPrefs.getString(KEY_DROIDGUARD_TOKEN, null)
+ val legacyExpiration = legacyPrefs.getLong(KEY_DROIDGUARD_TOKEN_TTL, 0L)
+ if (!legacyToken.isNullOrBlank() && legacyExpiration > 0L) {
+ statePrefs.edit {
+ putString(KEY_DROIDGUARD_TOKEN, legacyToken)
+ putLong(KEY_DROIDGUARD_TOKEN_TTL, legacyExpiration)
+ }
+ token = legacyToken
+ expiration = legacyExpiration
+ }
+ }
+
+ if (!token.isNullOrBlank() && expiration > System.currentTimeMillis()) {
+ return token
+ }
+
+ if (!token.isNullOrBlank() || expiration > 0L) {
+ clearDroidGuardToken(context)
+ }
+ return null
+ }
+
+ private fun storeCachedDroidGuardToken(
+ context: Context,
+ token: String,
+ expirationMillis: Long
+ ) {
+ if (token.isBlank() || expirationMillis <= System.currentTimeMillis()) return
+ statePrefs(context).edit {
+ putString(KEY_DROIDGUARD_TOKEN, token)
+ putLong(KEY_DROIDGUARD_TOKEN_TTL, expirationMillis)
+ }
+ }
+
+ fun clearDroidGuardToken(context: Context) {
+ statePrefs(context).edit {
+ remove(KEY_DROIDGUARD_TOKEN)
+ remove(KEY_DROIDGUARD_TOKEN_TTL)
+ }
+ tokenPrefs(context).edit {
+ remove(KEY_DROIDGUARD_TOKEN)
+ remove(KEY_DROIDGUARD_TOKEN_TTL)
+ }
+ }
+
+ fun isPublicKeyAcked(context: Context): Boolean {
+ return statePrefs(context).getBoolean(KEY_PUBLIC_KEY_ACKED, false)
+ }
+
+ @SuppressLint("ApplySharedPref")
+ fun setPublicKeyAcked(context: Context, acked: Boolean) {
+ statePrefs(context).edit { putBoolean(KEY_PUBLIC_KEY_ACKED, acked) }
+ }
+
+ fun loadPnvrNoticeConsent(context: Context): Consent {
+ val value = statePrefs(context).getInt(KEY_PNVR_NOTICE_CONSENT, Consent.CONSENT_UNKNOWN.value)
+ return Consent.fromValue(value) ?: Consent.CONSENT_UNKNOWN
+ }
+
+ fun storePnvrNotice(
+ context: Context,
+ consent: Consent,
+ source: ConsentSource,
+ version: ConsentVersion
+ ) {
+ statePrefs(context).edit {
+ putInt(KEY_PNVR_NOTICE_CONSENT, consent.value)
+ putInt(KEY_PNVR_NOTICE_SOURCE, source.value)
+ putInt(KEY_PNVR_NOTICE_VERSION, version.value)
+ putLong(KEY_PNVR_NOTICE_UPDATED_AT_MS, System.currentTimeMillis())
+ }
+ }
+
+ private fun storeVerificationTokens(context: Context, tokens: List) {
+ if (tokens.isEmpty()) return
+ val filtered = tokens.filter {
+ (it.expiration_time?.toEpochMilli() ?: 0L) > System.currentTimeMillis()
+ }
+ if (filtered.isEmpty()) return
+
+ val serialized = filtered.joinToString(",") { token ->
+ val encoded = Base64.encodeToString(token.token.toByteArray(), Base64.NO_WRAP)
+ val expiration = token.expiration_time?.toEpochMilli() ?: 0L
+ "$encoded|$expiration"
+ }
+ tokenPrefs(context).edit { putString(KEY_VERIFICATION_TOKENS, serialized) }
+ }
+
+ private fun storeDroidGuardToken(context: Context, token: DroidguardToken?) {
+ val tokenValue = token?.token?.takeIf { it.isNotEmpty() } ?: return
+ val expiration = token.ttl?.toEpochMilli() ?: return
+ storeCachedDroidGuardToken(context, tokenValue, expiration)
+ }
+
+ private fun storeNextSyncTime(context: Context, timestamp: ServerTimestamp?) {
+ val nextSyncDelayMillis = timestamp?.let(::nextSyncDelayMillis) ?: return
+ statePrefs(context).edit {
+ // GMS stores the next sync deadline as an absolute wall-clock timestamp
+ putLong(KEY_NEXT_SYNC_TIMESTAMP_MS, System.currentTimeMillis() + nextSyncDelayMillis)
+ }
+ }
+
+ private fun nextSyncDelayMillis(timestamp: ServerTimestamp): Long {
+ val serverMillis = timestamp.timestamp?.toEpochMilli() ?: 0L
+ val localMillis = timestamp.now?.toEpochMilli() ?: 0L
+ return serverMillis - localMillis
+ }
+
+ private fun statePrefs(context: Context) =
+ context.getSharedPreferences(STATE_PREFS_NAME, Context.MODE_PRIVATE)
+
+ private fun tokenPrefs(context: Context) =
+ context.getSharedPreferences(TOKEN_PREFS_NAME, Context.MODE_PRIVATE)
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GServices.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GServices.kt
new file mode 100644
index 0000000000..ca5b6acbb9
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GServices.kt
@@ -0,0 +1,33 @@
+package org.microg.gms.constellation
+
+import android.content.ContentResolver
+import android.net.Uri
+import androidx.core.net.toUri
+
+// TODO: This is taken from vending-app, can we have a common client for GServices please?
+
+object GServices {
+ private val CONTENT_URI: Uri = "content://com.google.android.gsf.gservices".toUri()
+
+ fun getString(resolver: ContentResolver, key: String, defaultValue: String?): String? {
+ var result = defaultValue
+ val cursor = resolver.query(CONTENT_URI, null, null, arrayOf(key), null)
+ cursor?.use {
+ if (cursor.moveToNext()) {
+ result = cursor.getString(1)
+ }
+ }
+ return result
+ }
+
+ fun getLong(resolver: ContentResolver, key: String, defaultValue: Long): Long {
+ val result = getString(resolver, key, null)
+ if (result != null) {
+ try {
+ return result.toLong()
+ } catch (_: NumberFormatException) {
+ }
+ }
+ return defaultValue
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetIidToken.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetIidToken.kt
new file mode 100644
index 0000000000..08775f8b9f
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetIidToken.kt
@@ -0,0 +1,35 @@
+package org.microg.gms.constellation
+
+import android.content.Context
+import android.util.Log
+import com.google.android.gms.common.api.ApiMetadata
+import com.google.android.gms.common.api.Status
+import com.google.android.gms.constellation.GetIidTokenRequest
+import com.google.android.gms.constellation.GetIidTokenResponse
+import com.google.android.gms.constellation.internal.IConstellationCallbacks
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+private const val TAG = "GetIidToken"
+
+suspend fun handleGetIidToken(
+ context: Context,
+ callbacks: IConstellationCallbacks,
+ request: GetIidTokenRequest
+) = withContext(Dispatchers.IO) {
+ try {
+ val authManager = AuthManager.get(context)
+ val iidToken = authManager.getIidToken(request.projectNumber?.toString())
+ val fid = authManager.getFid()
+ val (signature, timestamp) = authManager.signIidToken(iidToken)
+
+ callbacks.onIidTokenGenerated(
+ Status.SUCCESS,
+ GetIidTokenResponse(iidToken, fid, signature, timestamp.toEpochMilli()),
+ ApiMetadata.DEFAULT
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "getIidToken failed", e)
+ callbacks.onIidTokenGenerated(Status.INTERNAL_ERROR, null, ApiMetadata.DEFAULT)
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilities.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilities.kt
new file mode 100644
index 0000000000..38fec9f3e8
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilities.kt
@@ -0,0 +1,120 @@
+package org.microg.gms.constellation
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.util.Base64
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.RequiresPermission
+import androidx.core.content.getSystemService
+import com.google.android.gms.common.api.ApiMetadata
+import com.google.android.gms.common.api.Status
+import com.google.android.gms.constellation.GetPnvCapabilitiesRequest
+import com.google.android.gms.constellation.GetPnvCapabilitiesResponse
+import com.google.android.gms.constellation.SimCapability
+import com.google.android.gms.constellation.VerificationCapability
+import com.google.android.gms.constellation.VerificationStatus
+import com.google.android.gms.constellation.internal.IConstellationCallbacks
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import java.security.MessageDigest
+
+private const val TAG = "GetPnvCapabilities"
+
+@SuppressLint("HardwareIds")
+@RequiresPermission(
+ allOf =
+ [
+ Manifest.permission.READ_PHONE_STATE,
+ "android.permission.READ_PRIVILEGED_PHONE_STATE"]
+)
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
+suspend fun handleGetPnvCapabilities(
+ context: Context,
+ callbacks: IConstellationCallbacks,
+ request: GetPnvCapabilitiesRequest
+) = withContext(Dispatchers.IO) {
+ try {
+ val baseTelephonyManager =
+ context.getSystemService()
+ ?: throw IllegalStateException("TelephonyManager unavailable")
+ val subscriptionManager =
+ context.getSystemService()
+ ?: throw IllegalStateException("SubscriptionManager unavailable")
+ val simCapabilities = subscriptionManager.activeSubscriptionInfoList
+ .orEmpty()
+ .filter { request.simSlotIndices.isEmpty() || it.simSlotIndex in request.simSlotIndices }
+ .map { info ->
+ val telephonyManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ baseTelephonyManager.createForSubscriptionId(info.subscriptionId)
+ } else {
+ baseTelephonyManager
+ }
+
+ val carrierId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ telephonyManager.simCarrierId
+ } else {
+ 0
+ }
+
+ // GMS hardcodes public verification method 9 for the Firebase PNV TS43 capability path.
+ val verificationCapabilities = if (9 in request.verificationTypes) {
+ listOf(
+ VerificationCapability(
+ 9,
+ when {
+ !GetPnvCapabilitiesApiPhenotype.FPNV_ALLOWED_CARRIER_IDS.contains(carrierId) ->
+ VerificationStatus.UNSUPPORTED_CARRIER
+
+ telephonyManager.simState != TelephonyManager.SIM_STATE_READY ->
+ VerificationStatus.UNSUPPORTED_SIM_NOT_READY
+
+ else -> VerificationStatus.SUPPORTED
+ }
+ )
+ )
+ } else {
+ emptyList()
+ }
+
+ // TODO: Reflection should be used to call telephonyManager.getSubscriberId(it.subscriptionId) for SDK < N
+ val subscriberIdDigest = MessageDigest.getInstance("SHA-256")
+ .digest(telephonyManager.subscriberId.orEmpty().toByteArray())
+ val subscriberIdDigestEncoded =
+ Base64.encodeToString(subscriberIdDigest, Base64.NO_WRAP)
+
+ SimCapability(
+ info.simSlotIndex,
+ subscriberIdDigestEncoded,
+ carrierId,
+ // TODO: SDK < N is TelephonyManager.getSimOperatorNameForSubscription
+ telephonyManager.simOperatorName.orEmpty(),
+ verificationCapabilities
+ )
+ }
+
+ callbacks.onGetPnvCapabilitiesCompleted(
+ Status.SUCCESS,
+ GetPnvCapabilitiesResponse(simCapabilities),
+ ApiMetadata.DEFAULT
+ )
+ } catch (e: SecurityException) {
+ Log.e(TAG, "getPnvCapabilities missing permission", e)
+ callbacks.onGetPnvCapabilitiesCompleted(
+ Status(5000),
+ GetPnvCapabilitiesResponse(emptyList()),
+ ApiMetadata.DEFAULT
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "getPnvCapabilities failed", e)
+ callbacks.onGetPnvCapabilitiesCompleted(
+ Status.INTERNAL_ERROR,
+ GetPnvCapabilitiesResponse(emptyList()),
+ ApiMetadata.DEFAULT
+ )
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilitiesApiPhenotype.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilitiesApiPhenotype.kt
new file mode 100644
index 0000000000..814fae3f1c
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilitiesApiPhenotype.kt
@@ -0,0 +1,5 @@
+package org.microg.gms.constellation
+
+object GetPnvCapabilitiesApiPhenotype {
+ val FPNV_ALLOWED_CARRIER_IDS = ArrayList()
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetVerifiedPhoneNumbers.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetVerifiedPhoneNumbers.kt
new file mode 100644
index 0000000000..b382bef5a8
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetVerifiedPhoneNumbers.kt
@@ -0,0 +1,137 @@
+package org.microg.gms.constellation
+
+import android.content.Context
+import android.os.Bundle
+import android.util.Log
+import com.google.android.gms.common.api.ApiMetadata
+import com.google.android.gms.common.api.Status
+import com.google.android.gms.constellation.PhoneNumberInfo
+import com.google.android.gms.constellation.PhoneNumberVerification
+import com.google.android.gms.constellation.VerifyPhoneNumberResponse
+import com.google.android.gms.constellation.internal.IConstellationCallbacks
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import okio.ByteString.Companion.toByteString
+import org.microg.gms.constellation.proto.GetVerifiedPhoneNumbersRequest
+import org.microg.gms.constellation.proto.GetVerifiedPhoneNumbersRequest.PhoneNumberSelection
+import org.microg.gms.constellation.proto.IIDTokenAuth
+import org.microg.gms.constellation.proto.TokenOption
+import org.microg.gms.constellation.proto.VerifiedPhoneNumber as RpcVerifiedPhoneNumber
+import java.util.UUID
+
+private const val TAG = "GetVerifiedPhoneNumbers"
+
+suspend fun handleGetVerifiedPhoneNumbers(
+ context: Context,
+ callbacks: IConstellationCallbacks,
+ bundle: Bundle
+) = withContext(Dispatchers.IO) {
+ try {
+ val phoneNumbers = fetchVerifiedPhoneNumbers(context, bundle).map { it.toPhoneNumberInfo() }
+
+ callbacks.onPhoneNumberVerified(Status.SUCCESS, phoneNumbers, ApiMetadata.DEFAULT)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error in GetVerifiedPhoneNumbers (read-only)", e)
+ callbacks.onPhoneNumberVerified(Status.INTERNAL_ERROR, emptyList(), ApiMetadata.DEFAULT)
+ }
+}
+
+internal suspend fun fetchVerifiedPhoneNumbers(
+ context: Context,
+ bundle: Bundle,
+ callingPackage: String = bundle.getString("calling_package") ?: context.packageName
+): List = withContext(Dispatchers.IO) {
+ val authManager = AuthManager.get(context)
+ val sessionId = UUID.randomUUID().toString()
+ val selections = extractPhoneNumberSelections(bundle)
+ val certificateHash = bundle.getString("certificate_hash") ?: ""
+ val tokenNonce = bundle.getString("token_nonce") ?: ""
+
+ val iidToken = authManager.getIidToken()
+ val iidTokenAuth = if (VerifyPhoneNumberApiPhenotypes.ENABLE_CLIENT_SIGNATURE) {
+ val (signatureBytes, signTimestamp) = authManager.signIidToken(iidToken)
+ IIDTokenAuth(
+ iid_token = iidToken,
+ client_sign = signatureBytes.toByteString(),
+ sign_timestamp = signTimestamp
+ )
+ } else {
+ IIDTokenAuth(iid_token = iidToken)
+ }
+
+ val getRequest = GetVerifiedPhoneNumbersRequest(
+ session_id = sessionId,
+ iid_token_auth = iidTokenAuth,
+ phone_number_selections = selections,
+ token_option = TokenOption(
+ certificate_hash = certificateHash,
+ token_nonce = tokenNonce,
+ package_name = callingPackage
+ )
+ )
+
+ Log.d(TAG, "Calling GetVerifiedPhoneNumbers RPC (read-only mode)...")
+ val response = RpcClient.phoneNumberClient
+ .GetVerifiedPhoneNumbers()
+ .execute(getRequest)
+ Log.d(TAG, "GetVerifiedPhoneNumbers response: ${response.phone_numbers.size} numbers")
+ response.phone_numbers
+}
+
+internal fun List.toVerifyPhoneNumberResponse(): VerifyPhoneNumberResponse {
+ return VerifyPhoneNumberResponse(map { it.toPhoneNumberVerification() }.toTypedArray(), Bundle.EMPTY)
+}
+
+private fun RpcVerifiedPhoneNumber.toPhoneNumberInfo(): PhoneNumberInfo {
+ val extras = Bundle().apply {
+ if (id_token.isNotEmpty()) {
+ putString("id_token", id_token)
+ }
+ putInt("rcs_state", rcs_state.value)
+ }
+
+ return PhoneNumberInfo(
+ 1,
+ phone_number,
+ verification_time?.toEpochMilli() ?: 0L,
+ extras
+ )
+}
+
+private fun RpcVerifiedPhoneNumber.toPhoneNumberVerification(): PhoneNumberVerification {
+ val extras = Bundle().apply {
+ putInt("rcs_state", rcs_state.value)
+ }
+
+ // GMS read-only V2 leaves method/slot unset and returns a verified record directly.
+ return PhoneNumberVerification(
+ phone_number,
+ verification_time?.toEpochMilli() ?: 0L,
+ 0,
+ -1,
+ id_token.ifEmpty { null },
+ extras,
+ 1,
+ -1L
+ )
+}
+
+private fun extractPhoneNumberSelections(bundle: Bundle): List {
+ val selections = mutableListOf()
+ val selectionInts = bundle.getIntegerArrayList("phone_number_selection")
+
+ if (!selectionInts.isNullOrEmpty()) {
+ selections.addAll(selectionInts.mapNotNull { PhoneNumberSelection.fromValue(it) })
+ } else {
+ when (bundle.getString("rcs_read_option", "")) {
+ "READ_PROVISIONED" -> {
+ selections.add(PhoneNumberSelection.CONSTELLATION)
+ selections.add(PhoneNumberSelection.RCS)
+ }
+
+ "READ_PROVISIONED_ONLY" -> selections.add(PhoneNumberSelection.RCS)
+ else -> selections.add(PhoneNumberSelection.CONSTELLATION)
+ }
+ }
+ return selections
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/IidTokenPhenotypes.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/IidTokenPhenotypes.kt
new file mode 100644
index 0000000000..a700f12f2f
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/IidTokenPhenotypes.kt
@@ -0,0 +1,9 @@
+package org.microg.gms.constellation
+
+object IidTokenPhenotypes {
+ const val ASTERISM_PROJECT_NUMBER = 496232013492
+ const val DEFAULT_PROJECT_NUMBER = 496232013492
+ const val EXTERNAL_CONSENT_ACTIVITY_PROJECT_NUMBER = 496232013492
+ const val MESSAGES_PROJECT_NUMBER = 496232013492
+ const val READ_ONLY_PROJECT_NUMBER = 745476177629
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/RpcClient.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/RpcClient.kt
new file mode 100644
index 0000000000..f7a49733aa
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/RpcClient.kt
@@ -0,0 +1,47 @@
+package org.microg.gms.constellation
+
+import com.squareup.wire.GrpcClient
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import okhttp3.Response
+import org.microg.gms.common.Constants
+import org.microg.gms.constellation.proto.PhoneDeviceVerificationClient
+import org.microg.gms.constellation.proto.PhoneNumberClient
+
+private class AuthInterceptor : Interceptor {
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val originalRequest = chain.request()
+
+ val builder = originalRequest.newBuilder()
+ .header("X-Goog-Api-Key", "AIzaSyAP-gfH3qvi6vgHZbSYwQ_XHqV_mXHhzIk")
+ .header("X-Android-Package", Constants.GMS_PACKAGE_NAME)
+ .header("X-Android-Cert", Constants.GMS_PACKAGE_SIGNATURE_SHA1.uppercase())
+
+ return chain.proceed(builder.build())
+ }
+}
+
+object RpcClient {
+ private val client: OkHttpClient by lazy {
+ OkHttpClient.Builder()
+ .addInterceptor(AuthInterceptor())
+ .build()
+ }
+
+ private val grpcClient: GrpcClient by lazy {
+ GrpcClient.Builder()
+ .client(client)
+ // Google's constellationserver does NOT like compressed requests
+ .minMessageToCompress(Long.MAX_VALUE)
+ .baseUrl("https://phonedeviceverification-pa.googleapis.com/")
+ .build()
+ }
+
+ val phoneDeviceVerificationClient: PhoneDeviceVerificationClient by lazy {
+ grpcClient.create(PhoneDeviceVerificationClient::class)
+ }
+
+ val phoneNumberClient: PhoneNumberClient by lazy {
+ grpcClient.create(PhoneNumberClient::class)
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationMappings.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationMappings.kt
new file mode 100644
index 0000000000..55bbdda27e
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationMappings.kt
@@ -0,0 +1,134 @@
+package org.microg.gms.constellation
+
+import android.os.Bundle
+import com.google.android.gms.constellation.PhoneNumberVerification
+import org.microg.gms.constellation.proto.Param
+import org.microg.gms.constellation.proto.UnverifiedInfo
+import org.microg.gms.constellation.proto.Verification
+import org.microg.gms.constellation.proto.VerificationMethod
+
+fun UnverifiedInfo.Reason.toVerificationStatus(): Verification.Status {
+ return when (this) {
+ UnverifiedInfo.Reason.UNKNOWN_REASON -> Verification.Status.STATUS_PENDING
+ UnverifiedInfo.Reason.THROTTLED -> Verification.Status.STATUS_THROTTLED
+ UnverifiedInfo.Reason.FAILED -> Verification.Status.STATUS_FAILED
+ UnverifiedInfo.Reason.SKIPPED -> Verification.Status.STATUS_SKIPPED
+ UnverifiedInfo.Reason.NOT_REQUIRED -> Verification.Status.STATUS_NOT_REQUIRED
+ UnverifiedInfo.Reason.PHONE_NUMBER_ENTRY_REQUIRED ->
+ Verification.Status.STATUS_PHONE_NUMBER_ENTRY_REQUIRED
+
+ UnverifiedInfo.Reason.INELIGIBLE -> Verification.Status.STATUS_INELIGIBLE
+ UnverifiedInfo.Reason.DENIED -> Verification.Status.STATUS_DENIED
+ UnverifiedInfo.Reason.NOT_IN_SERVICE -> Verification.Status.STATUS_NOT_IN_SERVICE
+ }
+}
+
+fun Verification.getState(): Verification.State {
+ return when {
+ verification_info != null -> Verification.State.VERIFIED
+ pending_verification_info != null -> Verification.State.PENDING
+ unverified_info != null -> Verification.State.NONE
+ else -> Verification.State.UNKNOWN
+ }
+}
+
+fun Verification.getVerificationStatus(): Verification.Status {
+ return when (getState()) {
+ Verification.State.VERIFIED -> Verification.Status.STATUS_VERIFIED
+
+ Verification.State.PENDING -> {
+ if (status != Verification.Status.STATUS_UNKNOWN) {
+ status
+ } else {
+ Verification.Status.STATUS_PENDING
+ }
+ }
+
+ Verification.State.NONE -> {
+ unverified_info?.reason?.toVerificationStatus() ?: Verification.Status.STATUS_PENDING
+ }
+
+ Verification.State.UNKNOWN -> Verification.Status.STATUS_UNKNOWN
+ }
+}
+
+fun Verification.toClientVerification(imsiToSlotMap: Map): PhoneNumberVerification {
+ val verificationStatus = this.getVerificationStatus()
+ var phoneNumber: String? = null
+ var timestampMillis = System.currentTimeMillis()
+ var verificationMethod = VerificationMethod.UNKNOWN
+ var retryAfterSeconds = 0L
+ val extras = buildClientExtras()
+
+ when (this.getState()) {
+ Verification.State.VERIFIED -> {
+ val info = this.verification_info
+ phoneNumber = info?.phone_number
+ timestampMillis = info?.verification_time?.toEpochMilli() ?: System.currentTimeMillis()
+ verificationMethod = info?.challenge_method ?: VerificationMethod.UNKNOWN
+ }
+
+ Verification.State.PENDING -> {
+ verificationMethod =
+ this.pending_verification_info?.challenge?.type ?: VerificationMethod.UNKNOWN
+ }
+
+ Verification.State.NONE -> {
+ val info = this.unverified_info
+ verificationMethod = info?.challenge_method ?: VerificationMethod.UNKNOWN
+ retryAfterSeconds = info?.retry_after_time?.let { ts ->
+ val now = System.currentTimeMillis() / 1000L
+ (ts.epochSecond - now).coerceAtLeast(0L)
+ } ?: 0L
+ }
+
+ else -> {}
+ }
+
+ val simImsi = this.association?.sim?.sim_info?.imsi?.firstOrNull()
+ val simSlot = if (simImsi != null) imsiToSlotMap[simImsi] ?: 0 else 0
+ val verificationToken = extras.getString("id_token")
+
+ return PhoneNumberVerification(
+ phoneNumber,
+ timestampMillis,
+ verificationMethod.toClientMethod(),
+ simSlot,
+ verificationToken,
+ extras,
+ verificationStatus.value,
+ retryAfterSeconds
+ )
+}
+
+private fun Verification.buildClientExtras(): Bundle {
+ val bundle = Bundle()
+ for (param in api_params) {
+ bundle.putParam(param)
+ }
+
+ val slotIndex = association?.sim?.sim_slot?.slot_index
+ if (slotIndex != null && slotIndex >= 0) {
+ // GMS exposes this as a string inside the extras bundle.
+ bundle.putString("sim_slot_index", slotIndex.toString())
+ }
+ return bundle
+}
+
+private fun Bundle.putParam(param: Param) {
+ if (param.key == "verification_method") {
+ param.value_.toIntOrNull()?.let {
+ putInt(param.key, it)
+ return
+ }
+ }
+ putString(param.key, param.value_)
+}
+
+fun VerificationMethod.toClientMethod(): Int {
+ return when (this) {
+ VerificationMethod.TS43 -> 9
+ VerificationMethod.UNKNOWN -> 0
+ else -> value
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationSettingsPhenotypes.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationSettingsPhenotypes.kt
new file mode 100644
index 0000000000..c51f0a279e
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationSettingsPhenotypes.kt
@@ -0,0 +1,6 @@
+package org.microg.gms.constellation
+
+object VerificationSettingsPhenotypes {
+ const val A2P_SMS_SIGNAL_GRANULARITY_HRS = 1L
+ const val A2P_HISTORY_WINDOW_HOURS = 168L
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumber.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumber.kt
new file mode 100644
index 0000000000..d0dc3b314c
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumber.kt
@@ -0,0 +1,346 @@
+package org.microg.gms.constellation
+
+import android.content.Context
+import android.os.Build
+import android.os.Bundle
+import android.telephony.SubscriptionInfo
+import android.util.Log
+import androidx.annotation.RequiresApi
+import com.google.android.gms.common.api.ApiMetadata
+import com.google.android.gms.common.api.Status
+import com.google.android.gms.constellation.IdTokenRequest
+import com.google.android.gms.constellation.PhoneNumberInfo
+import com.google.android.gms.constellation.PhoneNumberVerification
+import com.google.android.gms.constellation.VerifyPhoneNumberRequest
+import com.google.android.gms.constellation.VerifyPhoneNumberResponse
+import com.google.android.gms.constellation.internal.IConstellationCallbacks
+import com.squareup.wire.GrpcException
+import com.squareup.wire.GrpcStatus
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.microg.gms.constellation.proto.SyncRequest
+import org.microg.gms.constellation.proto.Verification
+import org.microg.gms.constellation.proto.builders.RequestBuildContext
+import org.microg.gms.constellation.proto.builders.buildImsiToSubscriptionInfoMap
+import org.microg.gms.constellation.proto.builders.buildRequestContext
+import org.microg.gms.constellation.proto.builders.invoke
+import org.microg.gms.constellation.verification.ChallengeProcessor
+import java.util.UUID
+
+private const val TAG = "VerifyPhoneNumber"
+
+private enum class ReadCallbackMode {
+ NONE,
+ LEGACY,
+ TYPED
+}
+
+suspend fun handleVerifyPhoneNumberV1(
+ context: Context,
+ callbacks: IConstellationCallbacks,
+ bundle: Bundle,
+ packageName: String?
+) {
+ val callingPackage = packageName ?: bundle.getString("calling_package") ?: context.packageName
+ val extras = Bundle(bundle).apply {
+ putString("calling_package", callingPackage)
+ putString("calling_api", "verifyPhoneNumber")
+ }
+ val timeout = when (val timeoutValue = extras.get("timeout")) {
+ is Long -> timeoutValue
+ is Int -> timeoutValue.toLong()
+ else -> 300L
+ }
+ val request = VerifyPhoneNumberRequest(
+ policyId = extras.getString("policy_id", ""),
+ timeout = timeout,
+ idTokenRequest = IdTokenRequest(
+ extras.getString("certificate_hash", ""),
+ extras.getString("token_nonce", "")
+ ),
+ extras = extras,
+ targetedSims = emptyList(),
+ silent = false,
+ apiVersion = 2,
+ verificationTypes = emptyList()
+ )
+ val policyId = bundle.getString("policy_id", "")
+ val mode = bundle.getInt("verification_mode", 0)
+ val useReadPath = when (mode) {
+ 0 -> policyId in VerifyPhoneNumberApiPhenotypes.READ_ONLY_POLICY_IDS
+ 2 -> VerifyPhoneNumberApiPhenotypes.ENABLE_READ_FLOW
+
+ else -> false
+ }
+
+ handleVerifyPhoneNumberRequest(
+ context,
+ callbacks,
+ request,
+ callingPackage,
+ if (useReadPath) ReadCallbackMode.LEGACY else ReadCallbackMode.NONE,
+ legacyCallbackOnFullFlow = true
+ )
+}
+
+suspend fun handleVerifyPhoneNumberSingleUse(
+ context: Context,
+ callbacks: IConstellationCallbacks,
+ bundle: Bundle,
+ packageName: String?
+) {
+ val callingPackage = packageName ?: bundle.getString("calling_package") ?: context.packageName
+ val extras = Bundle(bundle).apply {
+ putString("calling_package", callingPackage)
+ putString("calling_api", "verifyPhoneNumberSingleUse")
+ putString("one_time_verification", "True")
+ }
+ val timeout = when (val timeoutValue = extras.get("timeout")) {
+ is Long -> timeoutValue
+ is Int -> timeoutValue.toLong()
+ else -> 300L
+ }
+ val request = VerifyPhoneNumberRequest(
+ policyId = extras.getString("policy_id", ""),
+ timeout = timeout,
+ idTokenRequest = IdTokenRequest(
+ extras.getString("certificate_hash", ""),
+ extras.getString("token_nonce", "")
+ ),
+ extras = extras,
+ targetedSims = emptyList(),
+ silent = false,
+ apiVersion = 2,
+ verificationTypes = emptyList()
+ )
+
+ handleVerifyPhoneNumberRequest(
+ context,
+ callbacks,
+ request,
+ callingPackage,
+ ReadCallbackMode.NONE,
+ legacyCallbackOnFullFlow = true
+ )
+}
+
+suspend fun handleVerifyPhoneNumberRequest(
+ context: Context,
+ callbacks: IConstellationCallbacks,
+ request: VerifyPhoneNumberRequest,
+ packageName: String?
+) {
+ val callingPackage = packageName ?: context.packageName
+ val requestWithExtras = request.copy(
+ extras = Bundle(request.extras).apply {
+ putString("calling_api", "verifyPhoneNumber")
+ }
+ )
+ val useReadPath = when (requestWithExtras.apiVersion) {
+ 0 -> requestWithExtras.policyId in VerifyPhoneNumberApiPhenotypes.READ_ONLY_POLICY_IDS
+ 2 -> VerifyPhoneNumberApiPhenotypes.ENABLE_READ_FLOW
+ 3 -> requestWithExtras.policyId in VerifyPhoneNumberApiPhenotypes.POLICY_IDS_ALLOWED_FOR_LOCAL_READ
+ else -> false
+ }
+
+ handleVerifyPhoneNumberRequest(
+ context,
+ callbacks,
+ requestWithExtras,
+ callingPackage,
+ if (useReadPath) ReadCallbackMode.TYPED else ReadCallbackMode.NONE,
+ localReadFallback = requestWithExtras.apiVersion == 3 && useReadPath,
+ legacyCallbackOnFullFlow = false
+ )
+}
+
+private suspend fun handleVerifyPhoneNumberRequest(
+ context: Context,
+ callbacks: IConstellationCallbacks,
+ request: VerifyPhoneNumberRequest,
+ callingPackage: String,
+ readCallbackMode: ReadCallbackMode,
+ localReadFallback: Boolean = false,
+ legacyCallbackOnFullFlow: Boolean = false
+) {
+ try {
+ when (readCallbackMode) {
+ ReadCallbackMode.LEGACY -> {
+ Log.d(TAG, "Using read-only mode")
+ handleGetVerifiedPhoneNumbers(context, callbacks, request.extras)
+ }
+
+ ReadCallbackMode.TYPED -> {
+ if (localReadFallback) {
+ Log.w(TAG, "Local-read mode not implemented, falling back to read-only RPC")
+ } else {
+ Log.d(TAG, "Using typed read-only mode")
+ }
+ val response = fetchVerifiedPhoneNumbers(context, request.extras, callingPackage)
+ .toVerifyPhoneNumberResponse()
+ callbacks.onPhoneNumberVerificationsCompleted(
+ Status.SUCCESS,
+ response,
+ ApiMetadata.DEFAULT
+ )
+ }
+
+ ReadCallbackMode.NONE -> {
+ Log.d(TAG, "Using full verification mode")
+ runVerificationFlow(
+ context,
+ request,
+ callingPackage,
+ callbacks,
+ legacyCallback = legacyCallbackOnFullFlow
+ )
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "verifyPhoneNumber failed", e)
+ when (readCallbackMode) {
+ ReadCallbackMode.LEGACY -> {
+ callbacks.onPhoneNumberVerified(Status.INTERNAL_ERROR, emptyList(), ApiMetadata.DEFAULT)
+ }
+
+ ReadCallbackMode.NONE -> {
+ if (legacyCallbackOnFullFlow) {
+ callbacks.onPhoneNumberVerified(Status.INTERNAL_ERROR, emptyList(), ApiMetadata.DEFAULT)
+ } else {
+ callbacks.onPhoneNumberVerificationsCompleted(
+ Status.INTERNAL_ERROR,
+ VerifyPhoneNumberResponse(emptyArray(), Bundle()),
+ ApiMetadata.DEFAULT
+ )
+ }
+ }
+
+ ReadCallbackMode.TYPED -> {
+ callbacks.onPhoneNumberVerificationsCompleted(
+ Status.INTERNAL_ERROR,
+ VerifyPhoneNumberResponse(emptyArray(), Bundle()),
+ ApiMetadata.DEFAULT
+ )
+ }
+ }
+ }
+}
+
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
+private suspend fun runVerificationFlow(
+ context: Context,
+ request: VerifyPhoneNumberRequest,
+ callingPackage: String,
+ callbacks: IConstellationCallbacks,
+ legacyCallback: Boolean
+) {
+ val sessionId = UUID.randomUUID().toString()
+
+ val imsiToInfoMap = buildImsiToSubscriptionInfoMap(context)
+ val buildContext = buildRequestContext(context)
+ val syncRequest = SyncRequest(
+ context,
+ sessionId,
+ request,
+ buildContext,
+ imsiToInfoMap = imsiToInfoMap,
+ includeClientAuth = ConstellationStateStore.isPublicKeyAcked(context),
+ callingPackage = callingPackage
+ )
+ val (verifications, isPublicKeyAcked) = executeSyncFlow(
+ context,
+ sessionId,
+ request,
+ syncRequest,
+ buildContext,
+ imsiToInfoMap
+ )
+ if (isPublicKeyAcked) {
+ ConstellationStateStore.setPublicKeyAcked(context, true)
+ }
+
+ if (legacyCallback) {
+ callbacks.onPhoneNumberVerified(
+ Status.SUCCESS,
+ verifications.mapNotNull { it.toLegacyPhoneNumberInfoOrNull() },
+ ApiMetadata.DEFAULT
+ )
+ } else {
+ callbacks.onPhoneNumberVerificationsCompleted(
+ Status.SUCCESS,
+ VerifyPhoneNumberResponse(verifications, Bundle()),
+ ApiMetadata.DEFAULT
+ )
+ }
+}
+
+private fun PhoneNumberVerification.toLegacyPhoneNumberInfoOrNull(): PhoneNumberInfo? {
+ if (verificationStatus != Verification.Status.STATUS_VERIFIED.value || phoneNumber.isNullOrEmpty()) {
+ return null
+ }
+ val extras = Bundle(this.extras ?: Bundle.EMPTY).apply {
+ verificationToken?.let { putString("id_token", it) }
+ }
+ return PhoneNumberInfo(1, phoneNumber, timestampMillis, extras)
+}
+
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
+private suspend fun executeSyncFlow(
+ context: Context,
+ sessionId: String,
+ request: VerifyPhoneNumberRequest,
+ syncRequest: SyncRequest,
+ buildContext: RequestBuildContext,
+ imsiToInfoMap: Map
+): Pair, Boolean> = withContext(Dispatchers.IO) {
+ Log.d(TAG, "Sending Sync request")
+ val syncResponse = try {
+ RpcClient.phoneDeviceVerificationClient.Sync().execute(syncRequest)
+ } catch (e: GrpcException) {
+ if (e.grpcStatus == GrpcStatus.PERMISSION_DENIED ||
+ e.grpcStatus == GrpcStatus.UNAUTHENTICATED
+ ) {
+ Log.w(TAG, "Suspicious client status ${e.grpcStatus.name}. Clearing DroidGuard cache...")
+ ConstellationStateStore.clearDroidGuardToken(context)
+ }
+ throw e
+ }
+ Log.d(TAG, "Sync response: ${syncResponse.responses.size} verifications")
+ ConstellationStateStore.storeSyncResponse(context, syncResponse)
+
+ val isPublicKeyAcked = syncResponse.header_?.status?.code == 1
+ val imsiToSlotMap = imsiToInfoMap.mapValues { it.value.simSlotIndex }
+ val requestedImsis = request.targetedSims.map { it.imsi }.toSet()
+
+ val verifications = syncResponse.responses.mapNotNull { result ->
+ val verification = result.verification ?: Verification()
+ val verificationImsis = verification.association?.sim?.sim_info?.imsi.orEmpty()
+ if (requestedImsis.isNotEmpty() && verificationImsis.none { it in requestedImsis }) {
+ Log.w(
+ TAG,
+ "Skipping verification for IMSIs=$verificationImsis because it does not match requested IMSIs=$requestedImsis"
+ )
+ return@mapNotNull null
+ }
+
+ val finalVerification = if (verification.getState() == Verification.State.PENDING) {
+ ChallengeProcessor.process(
+ context,
+ sessionId,
+ imsiToInfoMap,
+ buildContext,
+ verification
+ )
+ } else {
+ verification
+ }
+
+ finalVerification.toClientVerification(imsiToSlotMap)
+ }.toTypedArray()
+
+ if (isPublicKeyAcked) {
+ Log.d(TAG, "Server acknowledged client public key")
+ }
+
+ verifications to isPublicKeyAcked
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumberApiPhenotypes.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumberApiPhenotypes.kt
new file mode 100644
index 0000000000..93faca2dd1
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumberApiPhenotypes.kt
@@ -0,0 +1,37 @@
+package org.microg.gms.constellation
+
+object VerifyPhoneNumberApiPhenotypes {
+ val PACKAGES_ALLOWED_TO_CALL = listOf(
+ "com.google.android.gms",
+ "com.google.android.apps.messaging",
+ "com.google.android.ims",
+ "com.google.android.apps.tachyon",
+ "com.google.android.dialer",
+ "com.google.android.apps.nbu.paisa.user.dev",
+ "com.google.android.apps.nbu.paisa.user.qa",
+ "com.google.android.apps.nbu.paisa.user.teamfood2",
+ "com.google.android.apps.nbu.paisa.user.partner",
+ "com.google.android.apps.nbu.paisa.user",
+ "com.google.android.gms.constellation.getiidtoken",
+ "com.google.android.gms.constellation.ondemandconsent",
+ "com.google.android.gms.constellation.ondemandconsentv2",
+ "com.google.android.gms.constellation.readphonenumber",
+ "com.google.android.gms.constellation.verifyphonenumberlite",
+ "com.google.android.gms.constellation.verifyphonenumber",
+ "com.google.android.gms.test",
+ "com.google.android.apps.stargate",
+ "com.google.android.gms.firebase.fpnv",
+ "com.google.firebase.pnv.testapp",
+ "com.google.firebase.pnv"
+ )
+ val POLICY_IDS_ALLOWED_FOR_LOCAL_READ = listOf("emergency_location")
+ val READ_ONLY_POLICY_IDS = listOf(
+ "business_voice",
+ "verifiedsmsconsent",
+ "hint",
+ "nearbysharing"
+ )
+ const val ENABLE_CLIENT_SIGNATURE = true
+ const val ENABLE_LOCAL_READ_FLOW = true
+ const val ENABLE_READ_FLOW = true
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/ClientInfoBuilder.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/ClientInfoBuilder.kt
new file mode 100644
index 0000000000..99e224cb3d
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/ClientInfoBuilder.kt
@@ -0,0 +1,173 @@
+package org.microg.gms.constellation.proto.builders
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.util.Log
+import androidx.annotation.RequiresPermission
+import androidx.core.content.edit
+import androidx.core.content.getSystemService
+import com.google.android.gms.tasks.await
+import okio.ByteString.Companion.toByteString
+import org.microg.gms.common.Constants
+import org.microg.gms.constellation.AuthManager
+import org.microg.gms.constellation.ConstellationStateStore
+import org.microg.gms.constellation.GServices
+import org.microg.gms.constellation.proto.ClientInfo
+import org.microg.gms.constellation.proto.CountryInfo
+import org.microg.gms.constellation.proto.DeviceID
+import org.microg.gms.constellation.proto.DeviceType
+import org.microg.gms.constellation.proto.DroidGuardSignals
+import org.microg.gms.constellation.proto.GaiaSignals
+import org.microg.gms.constellation.proto.GaiaToken
+import org.microg.gms.constellation.proto.NetworkSignal
+import org.microg.gms.constellation.proto.SimOperatorInfo
+import org.microg.gms.constellation.proto.UserProfileType
+import java.util.Locale
+
+private const val TAG = "ClientInfoBuilder"
+private const val PREFS_NAME = "constellation_prefs"
+
+@RequiresPermission(Manifest.permission.GET_ACCOUNTS)
+@SuppressLint("HardwareIds")
+suspend operator fun ClientInfo.Companion.invoke(context: Context, iidToken: String): ClientInfo {
+ return ClientInfo(
+ context,
+ RequestBuildContext(
+ iidToken = iidToken,
+ gaiaTokens = GaiaToken.getList(context)
+ )
+ )
+}
+
+@RequiresPermission(Manifest.permission.GET_ACCOUNTS)
+@SuppressLint("HardwareIds")
+suspend operator fun ClientInfo.Companion.invoke(
+ context: Context,
+ buildContext: RequestBuildContext
+): ClientInfo {
+ val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
+ val locale = Locale.getDefault().let { "${it.language}_${it.country}" }
+ val authManager = AuthManager.get(context)
+
+ return ClientInfo(
+ device_id = DeviceID(context, buildContext.iidToken),
+ client_public_key = authManager.getOrCreateKeyPair().public.encoded.toByteString(),
+ locale = locale,
+ gmscore_version_number = Constants.GMS_VERSION_CODE / 1000,
+ gmscore_version = packageInfo.versionName ?: "",
+ android_sdk_version = Build.VERSION.SDK_INT,
+ user_profile_type = UserProfileType.REGULAR_USER,
+ gaia_tokens = buildContext.gaiaTokens,
+ country_info = CountryInfo(context),
+ connectivity_infos = NetworkSignal.getList(context),
+ model = Build.MODEL,
+ manufacturer = Build.MANUFACTURER,
+ partial_sim_infos = SimOperatorInfo.getList(context),
+ device_type = DeviceType.DEVICE_TYPE_PHONE,
+ is_wearable_standalone = false,
+ gaia_signals = GaiaSignals(context),
+ device_fingerprint = Build.FINGERPRINT,
+ droidguard_signals = DroidGuardSignals(context),
+ )
+}
+
+operator fun DeviceID.Companion.invoke(context: Context, iidToken: String): DeviceID {
+ val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
+
+ var gmsAndroidId = prefs.getLong("gms_android_id", 0L)
+ if (gmsAndroidId == 0L) {
+ gmsAndroidId = GServices.getLong(context.contentResolver, "android_id", 0L)
+ if (gmsAndroidId == 0L) {
+ val androidIdStr =
+ android.provider.Settings.Secure.getString(context.contentResolver, "android_id")
+ gmsAndroidId =
+ androidIdStr?.toLongOrNull(16) ?: (Build.ID.hashCode()
+ .toLong() and 0x7FFFFFFFFFFFFFFFL)
+ }
+ prefs.edit { putLong("gms_android_id", gmsAndroidId) }
+ }
+
+ val userSerial = try {
+ val userManager = context.getSystemService()
+ userManager?.getSerialNumberForUser(android.os.Process.myUserHandle()) ?: 0L
+ } catch (_: Exception) {
+ 0L
+ }
+
+ var primaryDeviceId = prefs.getLong("primary_device_id", 0L)
+ val isSystemUser = try {
+ val userManager = context.getSystemService()
+ userManager?.isSystemUser ?: true
+ } catch (_: Exception) {
+ true
+ }
+ if (primaryDeviceId == 0L && isSystemUser) {
+ primaryDeviceId = gmsAndroidId
+ prefs.edit { putLong("primary_device_id", primaryDeviceId) }
+ }
+
+ return DeviceID(
+ iid_token = iidToken,
+ primary_device_id = primaryDeviceId,
+ user_serial = userSerial,
+ gms_android_id = gmsAndroidId
+ )
+}
+
+operator fun CountryInfo.Companion.invoke(context: Context): CountryInfo {
+ val simCountries = mutableListOf()
+ val networkCountries = mutableListOf()
+
+ try {
+ val sm = context.getSystemService()
+ val tm = context.getSystemService()
+
+ sm?.activeSubscriptionInfoList?.forEach { info ->
+ val targetTM = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ tm?.createForSubscriptionId(info.subscriptionId)
+ } else tm
+
+ targetTM?.networkCountryIso?.let { networkCountries.add(it.lowercase()) }
+ info.countryIso?.let { simCountries.add(it.lowercase()) }
+ }
+ } catch (e: SecurityException) {
+ Log.w(TAG, "No permission to access country info", e)
+ }
+
+ if (simCountries.isEmpty()) {
+ val tm = context.getSystemService()
+ val simCountry = tm?.simCountryIso?.lowercase()?.takeIf { it.isNotBlank() }
+ if (simCountry != null) simCountries.add(simCountry)
+ }
+ if (networkCountries.isEmpty()) {
+ val tm = context.getSystemService()
+ val networkCountry = tm?.networkCountryIso?.lowercase()?.takeIf { it.isNotBlank() }
+ if (networkCountry != null) networkCountries.add(networkCountry)
+ }
+
+ return CountryInfo(sim_countries = simCountries, network_countries = networkCountries)
+}
+
+suspend operator fun DroidGuardSignals.Companion.invoke(context: Context): DroidGuardSignals? {
+ val cachedToken = ConstellationStateStore.loadDroidGuardToken(context)
+ if (!cachedToken.isNullOrBlank()) {
+ return DroidGuardSignals(droidguard_token = cachedToken, droidguard_result = "")
+ }
+
+ return try {
+ val client = com.google.android.gms.droidguard.DroidGuard.getClient(context)
+ val data = mapOf(
+ "package_name" to context.packageName,
+ "timestamp" to System.currentTimeMillis().toString()
+ )
+ val result = client.getResults("constellation_verify", data, null).await()
+ DroidGuardSignals(droidguard_result = result, droidguard_token = "")
+ } catch (e: Exception) {
+ Log.w(TAG, "DroidGuard generation failed", e)
+ null
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/CommonBuilders.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/CommonBuilders.kt
new file mode 100644
index 0000000000..b05e0dc40c
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/CommonBuilders.kt
@@ -0,0 +1,70 @@
+package org.microg.gms.constellation.proto.builders
+
+import android.Manifest
+import android.content.Context
+import android.os.Bundle
+import androidx.annotation.RequiresPermission
+import okio.ByteString.Companion.toByteString
+import org.microg.gms.constellation.AuthManager
+import org.microg.gms.constellation.proto.AuditToken
+import org.microg.gms.constellation.proto.AuditTokenMetadata
+import org.microg.gms.constellation.proto.AuditUuid
+import org.microg.gms.constellation.proto.ClientInfo
+import org.microg.gms.constellation.proto.DeviceID
+import org.microg.gms.constellation.proto.Param
+import org.microg.gms.constellation.proto.RequestHeader
+import org.microg.gms.constellation.proto.RequestTrigger
+
+fun Param.Companion.getList(extras: Bundle?): List {
+ if (extras == null) return emptyList()
+ val params = mutableListOf()
+ val ignoreKeys = setOf("consent_variant_key", "consent_trigger_key", "gaia_access_token")
+ for (key in extras.keySet()) {
+ if (key !in ignoreKeys) {
+ extras.get(key)?.toString()?.let { value ->
+ params.add(Param(key = key, value_ = value))
+ }
+ }
+ }
+ return params
+}
+
+fun AuditToken.Companion.generate(): AuditToken {
+ val uuid = java.util.UUID.randomUUID()
+ return AuditToken(
+ metadata = AuditTokenMetadata(
+ uuid = AuditUuid(
+ uuid_msb = uuid.mostSignificantBits,
+ uuid_lsb = uuid.leastSignificantBits
+ )
+ )
+ )
+}
+
+@RequiresPermission(Manifest.permission.GET_ACCOUNTS)
+suspend operator fun RequestHeader.Companion.invoke(
+ context: Context,
+ sessionId: String,
+ buildContext: RequestBuildContext,
+ triggerType: RequestTrigger.Type = RequestTrigger.Type.CONSENT_API_TRIGGER,
+ includeClientAuth: Boolean = false
+): RequestHeader {
+ val authManager = if (includeClientAuth) AuthManager.get(context) else null
+ val clientAuth = if (includeClientAuth) {
+ val (signature, timestamp) = authManager!!.signIidToken(buildContext.iidToken)
+ org.microg.gms.constellation.proto.ClientAuth(
+ device_id = DeviceID(context, buildContext.iidToken),
+ signature = signature.toByteString(),
+ sign_timestamp = timestamp
+ )
+ } else {
+ null
+ }
+
+ return RequestHeader(
+ client_info = ClientInfo(context, buildContext),
+ client_auth = clientAuth,
+ session_id = sessionId,
+ trigger = RequestTrigger(type = triggerType)
+ )
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/GaiaInfoBuilder.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/GaiaInfoBuilder.kt
new file mode 100644
index 0000000000..dd2b34b42d
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/GaiaInfoBuilder.kt
@@ -0,0 +1,74 @@
+package org.microg.gms.constellation.proto.builders
+
+import android.Manifest
+import android.accounts.AccountManager
+import android.content.Context
+import android.util.Log
+import androidx.annotation.RequiresPermission
+import com.squareup.wire.Instant
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.microg.gms.constellation.proto.GaiaAccountSignalType
+import org.microg.gms.constellation.proto.GaiaSignalEntry
+import org.microg.gms.constellation.proto.GaiaSignals
+import org.microg.gms.constellation.proto.GaiaToken
+import java.security.MessageDigest
+import kotlin.math.abs
+import kotlin.math.absoluteValue
+
+private const val TAG = "GaiaInfoBuilder"
+
+@RequiresPermission(Manifest.permission.GET_ACCOUNTS)
+operator fun GaiaSignals.Companion.invoke(context: Context): GaiaSignals? {
+ val entries = mutableListOf()
+ try {
+ val accounts = AccountManager.get(context).getAccountsByType("com.google")
+ val md = MessageDigest.getInstance("SHA-256")
+
+ for (account in accounts) {
+ val hash = md.digest(account.name.toByteArray(Charsets.UTF_8))
+ val number =
+ hash.take(8).fold(0L) { acc, byte -> (acc shl 8) or (byte.toLong() and 0xFF) }
+ val obfuscatedId = try {
+ number.absoluteValue.toString()
+ } catch (_: Exception) {
+ abs(account.name.hashCode().toLong()).toString()
+ }
+
+ entries.add(
+ GaiaSignalEntry(
+ gaia_id = obfuscatedId,
+ signal_type = GaiaAccountSignalType.GAIA_ACCOUNT_SIGNAL_AUTHENTICATED,
+ timestamp = Instant.ofEpochMilli(System.currentTimeMillis())
+ )
+ )
+ }
+ } catch (e: Exception) {
+ Log.w(TAG, "Could not build Gaia signals", e)
+ }
+ return if (entries.isNotEmpty()) GaiaSignals(gaia_signals = entries) else null
+}
+
+@Suppress("DEPRECATION")
+suspend fun GaiaToken.Companion.getList(context: Context): List =
+ withContext(Dispatchers.IO) {
+ val gaiaTokens = mutableListOf()
+ try {
+ val accounts = AccountManager.get(context).getAccountsByType("com.google")
+ if (accounts.isNotEmpty()) {
+ val future = AccountManager.get(context).getAuthToken(
+ accounts.first(),
+ "oauth2:https://www.googleapis.com/auth/numberer",
+ null,
+ false,
+ null,
+ null
+ )
+ val token = future.result?.getString(AccountManager.KEY_AUTHTOKEN)
+ if (!token.isNullOrBlank()) gaiaTokens.add(GaiaToken(token = token))
+ }
+ } catch (e: Exception) {
+ Log.w(TAG, "Could not retrieve Gaia tokens", e)
+ }
+ gaiaTokens
+ }
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/RequestBuildContext.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/RequestBuildContext.kt
new file mode 100644
index 0000000000..5ac41bba97
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/RequestBuildContext.kt
@@ -0,0 +1,20 @@
+package org.microg.gms.constellation.proto.builders
+
+import android.content.Context
+import org.microg.gms.constellation.AuthManager
+import org.microg.gms.constellation.proto.GaiaToken
+
+data class RequestBuildContext(
+ val iidToken: String,
+ val gaiaTokens: List
+)
+
+suspend fun buildRequestContext(
+ context: Context,
+ authManager: AuthManager = AuthManager.get(context)
+): RequestBuildContext {
+ return RequestBuildContext(
+ iidToken = authManager.getIidToken(),
+ gaiaTokens = GaiaToken.getList(context)
+ )
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/SyncRequestBuilder.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/SyncRequestBuilder.kt
new file mode 100644
index 0000000000..7d86e3964f
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/SyncRequestBuilder.kt
@@ -0,0 +1,190 @@
+package org.microg.gms.constellation.proto.builders
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.core.content.getSystemService
+import com.google.android.gms.constellation.VerifyPhoneNumberRequest
+import org.microg.gms.constellation.ConstellationStateStore
+import org.microg.gms.constellation.proto.ChallengePreference
+import org.microg.gms.constellation.proto.ChallengePreferenceMetadata
+import org.microg.gms.constellation.proto.IdTokenRequest
+import org.microg.gms.constellation.proto.Param
+import org.microg.gms.constellation.proto.RequestHeader
+import org.microg.gms.constellation.proto.RequestTrigger
+import org.microg.gms.constellation.proto.SIMAssociation
+import org.microg.gms.constellation.proto.SIMSlotInfo
+import org.microg.gms.constellation.proto.SyncRequest
+import org.microg.gms.constellation.proto.TelephonyInfo
+import org.microg.gms.constellation.proto.TelephonyPhoneNumberType
+import org.microg.gms.constellation.proto.Verification
+import org.microg.gms.constellation.proto.VerificationAssociation
+import org.microg.gms.constellation.proto.VerificationParam
+import org.microg.gms.constellation.proto.VerificationPolicy
+
+private const val TAG = "SyncRequestBuilder"
+
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
+@SuppressLint("HardwareIds", "MissingPermission")
+fun buildImsiToSubscriptionInfoMap(context: Context): Map {
+ val subscriptionManager =
+ context.getSystemService() ?: return emptyMap()
+ val telephonyManager =
+ context.getSystemService() ?: return emptyMap()
+ val map = mutableMapOf()
+
+ try {
+ subscriptionManager.activeSubscriptionInfoList?.forEach { info ->
+ val subsTelephonyManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ telephonyManager.createForSubscriptionId(info.subscriptionId)
+ } else {
+ telephonyManager
+ }
+ subsTelephonyManager.subscriberId?.let { imsi ->
+ map[imsi] = info
+ }
+ }
+ } catch (e: SecurityException) {
+ Log.w(TAG, "No permission to read SIM info for SubscriptionInfo mapping", e)
+ }
+ return map
+}
+
+@SuppressLint("MissingPermission")
+fun getTelephonyPhoneNumbers(
+ context: Context,
+ subscriptionId: Int
+): List {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return emptyList()
+
+ val subscriptionManager =
+ context.getSystemService() ?: return emptyList()
+
+ val sources = intArrayOf(
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_UICC,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_IMS
+ )
+
+ return try {
+ sources.map { source ->
+ val number = subscriptionManager.getPhoneNumber(subscriptionId, source)
+ if (number.isNotEmpty()) {
+ SIMAssociation.TelephonyPhoneNumber(
+ phone_number = number,
+ phone_number_type = when (source) {
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER ->
+ TelephonyPhoneNumberType.PHONE_NUMBER_SOURCE_CARRIER
+
+ SubscriptionManager.PHONE_NUMBER_SOURCE_UICC ->
+ TelephonyPhoneNumberType.PHONE_NUMBER_SOURCE_UICC
+
+ SubscriptionManager.PHONE_NUMBER_SOURCE_IMS ->
+ TelephonyPhoneNumberType.PHONE_NUMBER_SOURCE_IMS
+
+ else -> TelephonyPhoneNumberType.PHONE_NUMBER_SOURCE_UNSPECIFIED
+ }
+ )
+ } else null
+ }.filterNotNull()
+ } catch (e: Exception) {
+ Log.w(TAG, "Error getting telephony phone numbers", e)
+ emptyList()
+ }
+}
+
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
+@SuppressLint("HardwareIds", "MissingPermission")
+suspend operator fun SyncRequest.Companion.invoke(
+ context: Context,
+ sessionId: String,
+ request: VerifyPhoneNumberRequest,
+ includeClientAuth: Boolean = false,
+ callingPackage: String = context.packageName,
+ triggerType: RequestTrigger.Type = RequestTrigger.Type.TRIGGER_API_CALL
+): SyncRequest {
+ val buildContext = buildRequestContext(context)
+ return SyncRequest(
+ context = context,
+ sessionId = sessionId,
+ request = request,
+ buildContext = buildContext,
+ includeClientAuth = includeClientAuth,
+ callingPackage = callingPackage,
+ triggerType = triggerType
+ )
+}
+
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
+@SuppressLint("HardwareIds", "MissingPermission")
+suspend operator fun SyncRequest.Companion.invoke(
+ context: Context,
+ sessionId: String,
+ request: VerifyPhoneNumberRequest,
+ buildContext: RequestBuildContext,
+ imsiToInfoMap: Map = buildImsiToSubscriptionInfoMap(context),
+ includeClientAuth: Boolean = false,
+ callingPackage: String = context.packageName,
+ triggerType: RequestTrigger.Type = RequestTrigger.Type.TRIGGER_API_CALL
+): SyncRequest {
+ val apiParamsList = Param.getList(request.extras)
+
+ val verificationParams = request.targetedSims.map {
+ VerificationParam(key = it.imsi, value_ = it.phoneNumberHint)
+ }
+
+ val structuredParams = VerificationPolicy(
+ policy_id = request.policyId,
+ max_verification_age_hours = request.timeout,
+ id_token_request = IdTokenRequest(
+ certificate_hash = request.idTokenRequest.idToken,
+ token_nonce = request.idTokenRequest.subscriberHash
+ ),
+ calling_package = callingPackage,
+ params = verificationParams
+ )
+
+ val verifications = imsiToInfoMap.map { (imsi, subscriptionInfo) ->
+ val subscriptionId = subscriptionInfo.subscriptionId
+ val slotIndex = subscriptionInfo.simSlotIndex
+ val phoneNumber = subscriptionInfo.number ?: ""
+ val iccid = subscriptionInfo.iccId ?: ""
+
+ Verification(
+ status = Verification.Status.STATUS_NONE,
+ association = VerificationAssociation(
+ sim = SIMAssociation(
+ sim_info = SIMAssociation.SIMInfo(
+ imsi = listOf(imsi),
+ sim_readable_number = phoneNumber,
+ telephony_phone_number = getTelephonyPhoneNumbers(context, subscriptionId),
+ iccid = iccid
+ ),
+ gaia_tokens = buildContext.gaiaTokens,
+ sim_slot = SIMSlotInfo(
+ slot_index = slotIndex,
+ subscription_id = subscriptionId
+ )
+ )
+ ),
+ telephony_info = TelephonyInfo(context, subscriptionId),
+ structured_api_params = structuredParams,
+ api_params = apiParamsList,
+ challenge_preference = ChallengePreference(
+ capabilities = request.verificationMethods,
+ metadata = ChallengePreferenceMetadata()
+ )
+ )
+ }
+
+ return SyncRequest(
+ verifications = verifications,
+ header_ = RequestHeader(context, sessionId, buildContext, triggerType, includeClientAuth),
+ verification_tokens = ConstellationStateStore.loadVerificationTokens(context)
+ )
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/TelephonyInfoBuilder.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/TelephonyInfoBuilder.kt
new file mode 100644
index 0000000000..62e4ada9b7
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/TelephonyInfoBuilder.kt
@@ -0,0 +1,143 @@
+package org.microg.gms.constellation.proto.builders
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.net.ConnectivityManager
+import android.os.Build
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.util.Base64
+import android.util.Log
+import androidx.core.content.getSystemService
+import org.microg.gms.constellation.proto.NetworkSignal
+import org.microg.gms.constellation.proto.SimNetworkInfo
+import org.microg.gms.constellation.proto.SimOperatorInfo
+import org.microg.gms.constellation.proto.TelephonyInfo
+import java.security.MessageDigest
+
+private const val TAG = "TelephonyInfoBuilder"
+
+@SuppressLint("HardwareIds")
+operator fun TelephonyInfo.Companion.invoke(context: Context, subscriptionId: Int): TelephonyInfo {
+ val tm = context.getSystemService()
+ val targetTm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && subscriptionId >= 0) {
+ tm?.createForSubscriptionId(subscriptionId)
+ } else tm
+
+ val simState = when (targetTm?.simState) {
+ TelephonyManager.SIM_STATE_READY -> TelephonyInfo.SimState.SIM_STATE_READY
+ else -> TelephonyInfo.SimState.SIM_STATE_NOT_READY
+ }
+
+ val phoneType = when (targetTm?.phoneType) {
+ TelephonyManager.PHONE_TYPE_GSM -> TelephonyInfo.PhoneType.PHONE_TYPE_GSM
+ TelephonyManager.PHONE_TYPE_CDMA -> TelephonyInfo.PhoneType.PHONE_TYPE_CDMA
+ TelephonyManager.PHONE_TYPE_SIP -> TelephonyInfo.PhoneType.PHONE_TYPE_SIP
+ else -> TelephonyInfo.PhoneType.PHONE_TYPE_UNKNOWN
+ }
+
+ val networkRoaming = if (targetTm?.isNetworkRoaming == true) {
+ TelephonyInfo.RoamingState.ROAMING_ROAMING
+ } else {
+ TelephonyInfo.RoamingState.ROAMING_HOME
+ }
+
+ val cm = context.getSystemService()
+ val activeNetwork = cm?.activeNetworkInfo
+ val connectivityState = when {
+ activeNetwork == null -> TelephonyInfo.ConnectivityState.CONNECTIVITY_UNKNOWN
+ activeNetwork.isRoaming -> TelephonyInfo.ConnectivityState.CONNECTIVITY_ROAMING
+ else -> TelephonyInfo.ConnectivityState.CONNECTIVITY_HOME
+ }
+
+ val simInfo = SimNetworkInfo(
+ country_iso = targetTm?.simCountryIso?.lowercase() ?: "",
+ operator_ = targetTm?.simOperator ?: "",
+ operator_name = targetTm?.simOperatorName ?: ""
+ )
+
+ val networkInfo = SimNetworkInfo(
+ country_iso = targetTm?.networkCountryIso?.lowercase() ?: "",
+ operator_ = targetTm?.networkOperator ?: "",
+ operator_name = targetTm?.networkOperatorName ?: ""
+ )
+
+ return TelephonyInfo(
+ phone_type = phoneType,
+ group_id_level1 = targetTm?.groupIdLevel1 ?: "",
+ sim_info = simInfo,
+ network_info = networkInfo,
+ network_roaming = networkRoaming,
+ connectivity_state = connectivityState,
+ sms_capability = if (targetTm?.isSmsCapable == true) TelephonyInfo.SmsCapability.SMS_CAPABLE else TelephonyInfo.SmsCapability.SMS_NOT_CAPABLE,
+ sim_state = simState,
+ is_embedded = false
+ )
+}
+
+fun NetworkSignal.Companion.getList(context: Context): List {
+ val connectivityInfos = mutableListOf()
+ try {
+ val cm = context.getSystemService()
+ cm?.activeNetworkInfo?.let { networkInfo ->
+ val type = when (networkInfo.type) {
+ ConnectivityManager.TYPE_WIFI -> NetworkSignal.Type.TYPE_WIFI
+ ConnectivityManager.TYPE_MOBILE -> NetworkSignal.Type.TYPE_MOBILE
+ else -> NetworkSignal.Type.TYPE_UNKNOWN
+ }
+ val state = when {
+ networkInfo.isConnected -> NetworkSignal.State.STATE_CONNECTED
+ networkInfo.isConnectedOrConnecting && !networkInfo.isConnected -> NetworkSignal.State.STATE_CONNECTING
+ else -> NetworkSignal.State.STATE_DISCONNECTED
+ }
+ val availability =
+ if (networkInfo.isAvailable) NetworkSignal.Availability.AVAILABLE else NetworkSignal.Availability.NOT_AVAILABLE
+
+ connectivityInfos.add(
+ NetworkSignal(
+ type = type,
+ availability = availability,
+ state = state
+ )
+ )
+ }
+ } catch (e: Exception) {
+ Log.w(TAG, "Could not retrieve network info", e)
+ }
+ return connectivityInfos
+}
+
+fun SimOperatorInfo.Companion.getList(context: Context): List {
+ val infos = mutableListOf()
+ try {
+ val sm = context.getSystemService()
+ val tm = context.getSystemService()
+
+ sm?.activeSubscriptionInfoList?.forEach { info ->
+ val targetTM = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ tm?.createForSubscriptionId(info.subscriptionId)
+ } else tm
+
+ targetTM?.subscriberId?.let { imsi ->
+ val md = MessageDigest.getInstance("SHA-256")
+ val hash = Base64.encodeToString(
+ md.digest(imsi.toByteArray(Charsets.UTF_8)),
+ Base64.NO_WRAP
+ )
+
+ val simOperator = targetTM.simOperator ?: ""
+ infos.add(
+ SimOperatorInfo(
+ sim_operator = simOperator,
+ imsi_hash = hash
+ )
+ )
+ }
+ }
+ } catch (e: SecurityException) {
+ Log.e(TAG, "No permission to access SIM info for operator list", e)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error hashing IMSI", e)
+ }
+ return infos
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/CarrierIdVerifier.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/CarrierIdVerifier.kt
new file mode 100644
index 0000000000..b7c138638f
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/CarrierIdVerifier.kt
@@ -0,0 +1,84 @@
+package org.microg.gms.constellation.verification
+
+import android.content.Context
+import android.os.Build
+import android.telephony.TelephonyManager
+import android.util.Log
+import androidx.core.content.getSystemService
+import org.microg.gms.constellation.proto.CarrierIdChallengeResponse
+import org.microg.gms.constellation.proto.CarrierIdError
+import org.microg.gms.constellation.proto.Challenge
+import org.microg.gms.constellation.proto.ChallengeResponse
+
+private const val TAG = "CarrierIdVerifier"
+
+class CarrierIdVerifier(private val context: Context, private val subId: Int) {
+ fun verify(challenge: Challenge?): ChallengeResponse? {
+ val carrierChallenge = challenge?.carrier_id_challenge ?: return null
+ val challengeData = carrierChallenge.isim_request.takeIf { it.isNotEmpty() }
+ ?: return failure(
+ CarrierIdError.CARRIER_ID_ERROR_UNKNOWN_ERROR,
+ "Carrier challenge data missing"
+ )
+ if (subId == -1) return failure(
+ CarrierIdError.CARRIER_ID_ERROR_NO_SIM,
+ "No active subscription for carrier auth"
+ )
+
+ val appType = carrierChallenge.app_type
+ val authType = carrierChallenge.auth_type
+
+ return try {
+ val telephonyManager = context.getSystemService()
+ ?: return failure(
+ CarrierIdError.CARRIER_ID_ERROR_NOT_SUPPORTED,
+ "TelephonyManager unavailable"
+ )
+ val targetManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ telephonyManager.createForSubscriptionId(subId)
+ } else {
+ telephonyManager
+ }
+
+ val response = targetManager.getIccAuthentication(appType, authType, challengeData)
+ if (response.isNullOrEmpty()) {
+ failure(CarrierIdError.CARRIER_ID_ERROR_NULL_RESPONSE, "Null ISIM response")
+ } else {
+ ChallengeResponse(
+ carrier_id_response = CarrierIdChallengeResponse(
+ isim_response = response,
+ carrier_id_error = CarrierIdError.CARRIER_ID_ERROR_NO_ERROR
+ )
+ )
+ }
+ } catch (e: SecurityException) {
+ Log.w(TAG, "Unable to read subscription for carrier auth", e)
+ failure(
+ CarrierIdError.CARRIER_ID_ERROR_UNABLE_TO_READ_SUBSCRIPTION,
+ e.message ?: "SecurityException"
+ )
+ } catch (e: UnsupportedOperationException) {
+ Log.w(TAG, "Carrier auth API unavailable", e)
+ failure(
+ CarrierIdError.CARRIER_ID_ERROR_NOT_SUPPORTED,
+ e.message ?: "UnsupportedOperationException"
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "Carrier auth failed", e)
+ failure(
+ CarrierIdError.CARRIER_ID_ERROR_REFLECTION_ERROR,
+ e.message ?: "Reflection or platform error"
+ )
+ }
+ }
+
+ private fun failure(status: CarrierIdError, message: String): ChallengeResponse {
+ Log.w(TAG, message)
+ return ChallengeResponse(
+ carrier_id_response = CarrierIdChallengeResponse(
+ isim_response = "",
+ carrier_id_error = status
+ )
+ )
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ChallengeProcessor.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ChallengeProcessor.kt
new file mode 100644
index 0000000000..e731f5112d
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ChallengeProcessor.kt
@@ -0,0 +1,152 @@
+package org.microg.gms.constellation.verification
+
+import android.content.Context
+import android.telephony.SubscriptionInfo
+import android.util.Log
+import com.squareup.wire.GrpcException
+import com.squareup.wire.GrpcStatus
+import org.microg.gms.constellation.ConstellationStateStore
+import org.microg.gms.constellation.RpcClient
+import org.microg.gms.constellation.getState
+import org.microg.gms.constellation.proto.ChallengeResponse
+import org.microg.gms.constellation.proto.ProceedRequest
+import org.microg.gms.constellation.proto.RequestHeader
+import org.microg.gms.constellation.proto.RequestTrigger
+import org.microg.gms.constellation.proto.Verification
+import org.microg.gms.constellation.proto.VerificationMethod
+import org.microg.gms.constellation.proto.builders.RequestBuildContext
+import org.microg.gms.constellation.proto.builders.invoke
+
+object ChallengeProcessor {
+ private const val TAG = "ChallengeProcessor"
+ private const val MAX_PROCEED_ROUNDS = 16
+
+ private fun challengeTimeRemainingMillis(verification: Verification): Long? {
+ val expiry = verification.pending_verification_info?.challenge?.expiry_time ?: return null
+ val targetMillis = expiry.timestamp?.toEpochMilli() ?: return null
+ val referenceMillis = expiry.now?.toEpochMilli() ?: return null
+ return targetMillis - referenceMillis
+ }
+
+ suspend fun process(
+ context: Context,
+ sessionId: String,
+ imsiToInfoMap: Map,
+ buildContext: RequestBuildContext,
+ verification: Verification,
+ ): Verification {
+ var currentVerification = verification
+
+ for (attempt in 1..MAX_PROCEED_ROUNDS) {
+ if (currentVerification.getState() != Verification.State.PENDING) {
+ Log.d(
+ TAG,
+ "Verification state: ${currentVerification.getState()}. Stopping sequential verification."
+ )
+ return currentVerification
+ }
+
+ val challenge = currentVerification.pending_verification_info?.challenge
+ if (challenge == null) {
+ Log.w(
+ TAG,
+ "Attempt $attempt: Pending verification but no challenge found. Stopping."
+ )
+ return currentVerification
+ }
+
+ val challengeId = challenge.challenge_id?.id ?: ""
+ val remainingMillis = challengeTimeRemainingMillis(currentVerification)
+ if (remainingMillis != null && remainingMillis <= 0L) {
+ Log.w(TAG, "Attempt $attempt: Challenge $challengeId expired before proceed")
+ return currentVerification
+ }
+ Log.d(
+ TAG,
+ "Attempt $attempt: Solving challenge ID: $challengeId, Type: ${challenge.type}"
+ )
+
+ val challengeImsi = currentVerification.association?.sim?.sim_info?.imsi?.firstOrNull()
+ val info = imsiToInfoMap[challengeImsi]
+ val subId = info?.subscriptionId ?: -1
+
+ val challengeResponse: ChallengeResponse? = when {
+ challenge.type == VerificationMethod.TS43 -> Ts43Verifier(
+ context,
+ subId
+ ).verify(challenge.ts43_challenge)
+
+ challenge.type == VerificationMethod.CARRIER_ID && challenge.ts43_challenge != null ->
+ Ts43Verifier(context, subId).verify(challenge.ts43_challenge)
+
+ challenge.type == VerificationMethod.CARRIER_ID ->
+ CarrierIdVerifier(context, subId).verify(challenge)
+
+ challenge.type == VerificationMethod.MT_SMS -> MtSmsVerifier(
+ context,
+ subId
+ ).verify(challenge.mt_challenge)
+
+ challenge.type == VerificationMethod.MO_SMS -> MoSmsVerifier(
+ context,
+ subId
+ ).verify(challenge.mo_challenge)
+
+ challenge.type == VerificationMethod.REGISTERED_SMS ->
+ RegisteredSmsVerifier(context, subId).verify(challenge.registered_sms_challenge)
+
+ challenge.type == VerificationMethod.FLASH_CALL -> {
+ Log.w(TAG, "Flash call verification is unavailable on this build")
+ null
+ }
+
+ else -> {
+ Log.w(TAG, "Unsupported verification method: ${challenge.type}")
+ null
+ }
+ }
+
+ if (challengeResponse != null) {
+ Log.d(TAG, "Attempt $attempt: Challenge successfully solved. Proceeding...")
+ val proceedHeader = RequestHeader(
+ context,
+ sessionId,
+ buildContext,
+ RequestTrigger.Type.TRIGGER_API_CALL,
+ includeClientAuth = true
+ )
+ val proceedRequest = ProceedRequest(
+ verification = currentVerification,
+ challenge_response = challengeResponse,
+ header_ = proceedHeader
+ )
+ val proceedResponse = try {
+ RpcClient.phoneDeviceVerificationClient.Proceed().execute(proceedRequest)
+ } catch (e: GrpcException) {
+ if (e.grpcStatus == GrpcStatus.PERMISSION_DENIED ||
+ e.grpcStatus == GrpcStatus.UNAUTHENTICATED
+ ) {
+ Log.w(
+ TAG,
+ "Suspicious client status ${e.grpcStatus.name}. Clearing DroidGuard cache..."
+ )
+ ConstellationStateStore.clearDroidGuardToken(context)
+ }
+ throw e
+ }
+ ConstellationStateStore.storeProceedResponse(context, proceedResponse)
+ currentVerification = proceedResponse.verification ?: currentVerification
+ } else {
+ Log.w(
+ TAG,
+ "Attempt $attempt: Challenge verification failed or returned no response."
+ )
+ // GMS continues looping if IMSI doesn't match or other issues, but here we return to avoid infinite retries if verifier is broken
+ return currentVerification
+ }
+ }
+
+ Log.w(TAG, "Exhausted all $MAX_PROCEED_ROUNDS proceed rounds, record is still pending.")
+ return currentVerification
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MoSmsVerifier.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MoSmsVerifier.kt
new file mode 100644
index 0000000000..f9a6f6bc4a
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MoSmsVerifier.kt
@@ -0,0 +1,210 @@
+package org.microg.gms.constellation.verification
+
+import android.Manifest
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.os.Build
+import android.telephony.SmsManager
+import android.telephony.SubscriptionManager
+import android.util.Log
+import androidx.annotation.RequiresPermission
+import androidx.core.content.getSystemService
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withTimeoutOrNull
+import org.microg.gms.constellation.proto.ChallengeResponse
+import org.microg.gms.constellation.proto.MoChallenge
+import org.microg.gms.constellation.proto.MOChallengeResponseData
+import java.util.UUID
+import kotlin.coroutines.resume
+
+private const val TAG = "MoSmsVerifier"
+private const val ACTION_MO_SMS_SENT = "org.microg.gms.constellation.MO_SMS_SENT"
+
+class MoSmsVerifier(private val context: Context, private val subId: Int) {
+ suspend fun verify(challenge: MoChallenge?): ChallengeResponse? {
+ if (challenge == null) return null
+ if (challenge.proxy_number.isEmpty() || challenge.sms.isEmpty()) {
+ return failureResponse(MOChallengeResponseData.Status.FAILED_TO_SEND_MO)
+ }
+ if (context.checkCallingOrSelfPermission(Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "SEND_SMS permission missing")
+ return failureResponse(MOChallengeResponseData.Status.FAILED_TO_SEND_MO)
+ }
+
+ val smsManager = resolveSmsManager() ?: return failureResponse(
+ if (subId != -1 && !isActiveSubscription(subId)) {
+ MOChallengeResponseData.Status.NO_ACTIVE_SUBSCRIPTION
+ } else {
+ MOChallengeResponseData.Status.NO_SMS_MANAGER
+ }
+ )
+
+ val port = challenge.data_sms_info?.destination_port ?: 0
+ val isBinarySms = port > 0
+ val action = if (isBinarySms) ACTION_MO_SMS_SENT else ACTION_MO_SMS_SENT
+ val messageId = UUID.randomUUID().toString()
+ val sentIntent = Intent(action).apply {
+ `package` = context.packageName
+ putExtra("message_id", messageId)
+ }
+
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ messageId.hashCode(),
+ sentIntent,
+ PendingIntent.FLAG_ONE_SHOT or (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0)
+ )
+
+ Log.d(TAG, "Sending MO SMS to ${challenge.proxy_number} with messageId: $messageId")
+
+ return withTimeoutOrNull(30000) {
+ suspendCancellableCoroutine { continuation ->
+ val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == action) {
+ val receivedId = intent.getStringExtra("message_id")
+ if (receivedId != messageId) return
+
+ val resultCode = resultCode
+ val errorCode = intent.getIntExtra("errorCode", -1)
+
+ Log.d(TAG, "MO SMS sent result: $resultCode, error: $errorCode")
+
+ val status = when (resultCode) {
+ -1 -> MOChallengeResponseData.Status.COMPLETED
+ else -> MOChallengeResponseData.Status.FAILED_TO_SEND_MO
+ }
+
+ try {
+ context.unregisterReceiver(this)
+ } catch (_: Exception) {
+ }
+
+ if (continuation.isActive) {
+ continuation.resume(
+ ChallengeResponse(
+ mo_response = MOChallengeResponseData(
+ status = status,
+ sms_result_code = resultCode.toLong(),
+ sms_error_code = errorCode.toLong()
+ )
+ )
+ )
+ }
+ }
+ }
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ context.registerReceiver(
+ receiver,
+ IntentFilter(action),
+ Context.RECEIVER_NOT_EXPORTED
+ )
+ } else {
+ context.registerReceiver(receiver, IntentFilter(action))
+ }
+
+ continuation.invokeOnCancellation {
+ try {
+ context.unregisterReceiver(receiver)
+ } catch (_: Exception) {
+ }
+ }
+
+ try {
+ if (isBinarySms) {
+ smsManager.sendDataMessage(
+ challenge.proxy_number,
+ null,
+ port.toShort(),
+ challenge.sms.encodeToByteArray(),
+ pendingIntent,
+ null
+ )
+ } else {
+ smsManager.sendTextMessage(
+ challenge.proxy_number,
+ null,
+ challenge.sms,
+ pendingIntent,
+ null
+ )
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to initiate MO SMS send", e)
+ try {
+ context.unregisterReceiver(receiver)
+ } catch (_: Exception) {
+ }
+ if (continuation.isActive) {
+ continuation.resume(
+ ChallengeResponse(
+ mo_response = MOChallengeResponseData(
+ status = MOChallengeResponseData.Status.FAILED_TO_SEND_MO
+ )
+ )
+ )
+ }
+ }
+ }
+ } ?: ChallengeResponse(
+ mo_response = MOChallengeResponseData(
+ status = MOChallengeResponseData.Status.FAILED_TO_SEND_MO
+ )
+ )
+ }
+
+ private fun failureResponse(status: MOChallengeResponseData.Status): ChallengeResponse {
+ return ChallengeResponse(
+ mo_response = MOChallengeResponseData(
+ status = status
+ )
+ )
+ }
+
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ private fun isActiveSubscription(subscriptionId: Int): Boolean {
+ if (subscriptionId == -1) return false
+ return try {
+ context.getSystemService()?.isActiveSubscriptionId(subscriptionId)
+ ?: false
+ } catch (e: Exception) {
+ Log.w(TAG, "Failed to query active subscription for $subscriptionId", e)
+ false
+ }
+ }
+
+ private fun resolveSmsManager(): SmsManager? {
+ if (subId != -1 && !isActiveSubscription(subId)) {
+ return null
+ }
+ return try {
+ val manager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ context.getSystemService(SmsManager::class.java)
+ } else {
+ null
+ }
+ when {
+ subId != -1 && manager != null -> manager.createForSubscriptionId(subId)
+ manager != null -> manager
+ subId != -1 -> {
+ @Suppress("DEPRECATION")
+ SmsManager.getSmsManagerForSubscriptionId(subId)
+ }
+
+ else -> {
+ @Suppress("DEPRECATION")
+ SmsManager.getDefault()
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to resolve SmsManager for subId: $subId", e)
+ null
+ }
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MtSmsVerifier.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MtSmsVerifier.kt
new file mode 100644
index 0000000000..e757d60a07
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MtSmsVerifier.kt
@@ -0,0 +1,95 @@
+package org.microg.gms.constellation.verification
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Build
+import android.provider.Telephony
+import android.util.Log
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withTimeoutOrNull
+import org.microg.gms.constellation.proto.ChallengeResponse
+import org.microg.gms.constellation.proto.MTChallenge
+import org.microg.gms.constellation.proto.MTChallengeResponseData
+import kotlin.coroutines.resume
+
+private const val TAG = "MtSmsVerifier"
+
+class MtSmsVerifier(private val context: Context, private val subId: Int) {
+ suspend fun verify(challenge: MTChallenge?): ChallengeResponse? {
+ val expectedBody = challenge?.sms?.takeIf { it.isNotEmpty() } ?: return null
+
+ Log.d(TAG, "Waiting for MT SMS containing challenge string")
+
+ val result = withTimeoutOrNull(300_000) {
+ suspendCancellableCoroutine?> { continuation ->
+ val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ try {
+ if (intent.action == Telephony.Sms.Intents.SMS_RECEIVED_ACTION) {
+ val receivedSubId = intent.getIntExtra(
+ "android.telephony.extra.SUBSCRIPTION_INDEX",
+ intent.getIntExtra("subscription", -1)
+ )
+ if (subId != -1 && receivedSubId != -1 && receivedSubId != subId) {
+ return
+ }
+ val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
+ for (msg in messages) {
+ val body = msg.messageBody
+ if (body != null && body.contains(expectedBody)) {
+ Log.d(
+ TAG,
+ "Matching MT SMS received from ${msg.originatingAddress}"
+ )
+ context.unregisterReceiver(this)
+ if (continuation.isActive) {
+ continuation.resume(
+ Pair(
+ body,
+ msg.originatingAddress ?: ""
+ )
+ )
+ }
+ return
+ }
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error in MT SMS receiver", e)
+ }
+ }
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ context.registerReceiver(
+ receiver,
+ IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION),
+ Context.RECEIVER_NOT_EXPORTED
+ )
+ } else {
+ context.registerReceiver(
+ receiver,
+ IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)
+ )
+ }
+ continuation.invokeOnCancellation {
+ try {
+ context.unregisterReceiver(receiver)
+ } catch (_: Exception) {
+ }
+ }
+ }
+ }
+
+ return result?.let { (body, sender) ->
+ ChallengeResponse(
+ mt_response = MTChallengeResponseData(
+ sms = body,
+ sender = sender
+ )
+ )
+ }
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/RegisteredSmsVerifier.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/RegisteredSmsVerifier.kt
new file mode 100644
index 0000000000..301527f0d7
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/RegisteredSmsVerifier.kt
@@ -0,0 +1,159 @@
+package org.microg.gms.constellation.verification
+
+import android.content.Context
+import android.provider.Telephony
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.util.Log
+import androidx.core.content.getSystemService
+import okio.ByteString.Companion.toByteString
+import org.microg.gms.constellation.VerificationSettingsPhenotypes
+import org.microg.gms.constellation.proto.ChallengeResponse
+import org.microg.gms.constellation.proto.RegisteredSmsChallenge
+import org.microg.gms.constellation.proto.RegisteredSmsChallengeResponse
+import org.microg.gms.constellation.proto.RegisteredSmsChallengeResponseItem
+import org.microg.gms.constellation.proto.RegisteredSmsChallengeResponsePayload
+import java.nio.charset.StandardCharsets
+import java.security.MessageDigest
+import java.util.concurrent.TimeUnit
+
+private const val TAG = "RegisteredSmsVerifier"
+
+class RegisteredSmsVerifier(private val context: Context, private val subId: Int) {
+ fun verify(challenge: RegisteredSmsChallenge?): ChallengeResponse? {
+ val expectedPayloads = challenge?.verified_senders
+ ?.map { it.phone_number_id.toByteArray() }
+ ?.filter { it.isNotEmpty() }
+ .orEmpty()
+ if (expectedPayloads.isEmpty()) return null
+
+ val localNumbers = getLocalNumbers()
+ if (localNumbers.isEmpty()) {
+ Log.w(TAG, "No local phone numbers available for registered SMS verification")
+ return emptyResponse()
+ }
+
+ val historyStart = System.currentTimeMillis() -
+ TimeUnit.HOURS.toMillis(VerificationSettingsPhenotypes.A2P_HISTORY_WINDOW_HOURS)
+ val bucketSizeMillis = (TimeUnit.HOURS.toMillis(
+ VerificationSettingsPhenotypes.A2P_SMS_SIGNAL_GRANULARITY_HRS
+ ) / 2).coerceAtLeast(1L)
+
+ val responseItems = mutableListOf()
+
+ try {
+ val cursor = context.contentResolver.query(
+ Telephony.Sms.Inbox.CONTENT_URI,
+ arrayOf("date", "address", "body", "sub_id"),
+ "date > ?",
+ arrayOf(historyStart.toString()),
+ "date DESC"
+ ) ?: return emptyResponse()
+
+ cursor.use {
+ while (it.moveToNext()) {
+ val date = it.getLong(it.getColumnIndexOrThrow("date"))
+ val sender = it.getString(it.getColumnIndexOrThrow("address")) ?: continue
+ val body = it.getString(it.getColumnIndexOrThrow("body")) ?: continue
+ val messageSubId = runCatching {
+ it.getInt(it.getColumnIndexOrThrow("sub_id"))
+ }.getOrDefault(-1)
+
+ val candidateNumbers = if (subId != -1 && messageSubId == subId) {
+ localNumbers
+ } else if (messageSubId == -1) {
+ localNumbers
+ } else {
+ getLocalNumbers(messageSubId).ifEmpty { localNumbers }
+ }
+
+ val bucketStart = date - (date % bucketSizeMillis)
+ for (localNumber in candidateNumbers) {
+ val payload = computePayload(bucketStart, localNumber, sender, body)
+ if (expectedPayloads.any { expected -> expected.contentEquals(payload) }) {
+ responseItems += RegisteredSmsChallengeResponseItem(
+ payload = RegisteredSmsChallengeResponsePayload(
+ payload = payload.toByteString()
+ )
+ )
+ }
+ }
+ }
+ }
+ } catch (e: SecurityException) {
+ Log.w(TAG, "SMS inbox access denied", e)
+ return emptyResponse()
+ } catch (e: Exception) {
+ Log.e(TAG, "Registered SMS verification failed", e)
+ return emptyResponse()
+ }
+
+ return ChallengeResponse(
+ registered_sms_response = RegisteredSmsChallengeResponse(items = responseItems.distinct())
+ )
+ }
+
+ private fun emptyResponse(): ChallengeResponse {
+ return ChallengeResponse(
+ registered_sms_response = RegisteredSmsChallengeResponse(items = emptyList())
+ )
+ }
+
+ private fun getLocalNumbers(targetSubId: Int = subId): List {
+ val numbers = linkedSetOf()
+ val subscriptionManager =
+ context.getSystemService()
+ val telephonyManager = context.getSystemService()
+
+ try {
+ subscriptionManager?.activeSubscriptionInfoList.orEmpty().forEach { info ->
+ if (targetSubId != -1 && info.subscriptionId != targetSubId) return@forEach
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU && subscriptionManager != null) {
+ numbers += subscriptionManager.getPhoneNumber(
+ info.subscriptionId,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER
+ )
+ numbers += subscriptionManager.getPhoneNumber(
+ info.subscriptionId,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_UICC
+ )
+ numbers += subscriptionManager.getPhoneNumber(
+ info.subscriptionId,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_IMS
+ )
+ }
+ val targetManager =
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ telephonyManager?.createForSubscriptionId(info.subscriptionId)
+ } else {
+ telephonyManager
+ }
+ numbers += targetManager?.line1Number.orEmpty()
+ numbers += info.number.orEmpty()
+ }
+ } catch (e: Exception) {
+ Log.w(TAG, "Unable to collect local phone numbers", e)
+ }
+
+ return numbers.filter { it.isNotBlank() }.distinct()
+ }
+
+ private fun computePayload(
+ bucketStart: Long,
+ localNumber: String,
+ sender: String,
+ body: String
+ ): ByteArray {
+ val digest = MessageDigest.getInstance("SHA-512")
+ digest.update(bucketStart.toString().toByteArray(StandardCharsets.UTF_8))
+ digest.update(hashUtf8(localNumber))
+ digest.update(hashUtf8(sender))
+ digest.update(body.toByteArray(StandardCharsets.UTF_8))
+ return digest.digest()
+ }
+
+ private fun hashUtf8(value: String): ByteArray {
+ return MessageDigest.getInstance("SHA-512")
+ .digest(value.toByteArray(StandardCharsets.UTF_8))
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/Ts43Verifier.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/Ts43Verifier.kt
new file mode 100644
index 0000000000..4030817bcc
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/Ts43Verifier.kt
@@ -0,0 +1,431 @@
+package org.microg.gms.constellation.verification
+
+import android.content.Context
+import android.os.Build
+import android.telephony.TelephonyManager
+import android.util.Log
+import androidx.core.content.getSystemService
+import okhttp3.Cookie
+import okhttp3.CookieJar
+import okhttp3.HttpUrl
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.RequestBody.Companion.toRequestBody
+import org.json.JSONObject
+import org.microg.gms.constellation.proto.ChallengeResponse
+import org.microg.gms.constellation.proto.ClientChallengeResponse
+import org.microg.gms.constellation.proto.OdsaOperation
+import org.microg.gms.constellation.proto.ServerChallengeResponse
+import org.microg.gms.constellation.proto.ServiceEntitlementRequest
+import org.microg.gms.constellation.proto.Ts43Challenge
+import org.microg.gms.constellation.proto.Ts43ChallengeResponse
+import org.microg.gms.constellation.proto.Ts43ChallengeResponseError
+import org.microg.gms.constellation.proto.Ts43ChallengeResponseStatus
+import org.microg.gms.constellation.verification.ts43.EapAkaService
+import org.microg.gms.constellation.verification.ts43.builder
+import org.microg.gms.constellation.verification.ts43.userAgent
+import org.xml.sax.InputSource
+import java.io.IOException
+import java.io.StringReader
+import java.util.Locale
+import java.util.concurrent.TimeUnit
+import javax.xml.parsers.DocumentBuilderFactory
+import kotlin.text.ifEmpty
+
+private const val TAG = "Ts43Verifier"
+
+class Ts43Verifier(private val context: Context, private val subId: Int) {
+
+ private class Ts43ApiException(
+ val errorCode: Ts43ChallengeResponseError.Code,
+ val httpStatus: Int,
+ val requestType: Ts43ChallengeResponseError.RequestType,
+ cause: Throwable? = null
+ ) : Exception(cause)
+
+ private val httpHistory = mutableListOf()
+
+ private val telephonyManager: TelephonyManager by lazy {
+ val tm = requireNotNull(context.getSystemService()) {
+ "TelephonyManager unavailable"
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && subId >= 0) {
+ tm.createForSubscriptionId(subId)
+ } else tm
+ }
+
+ private val eapAkaService by lazy { EapAkaService(telephonyManager) }
+
+ private fun errorCode(rawCode: Int): Ts43ChallengeResponseError.Code =
+ Ts43ChallengeResponseError.Code.fromValue(rawCode)
+ ?: Ts43ChallengeResponseError.Code.TS43_ERROR_CODE_UNSPECIFIED
+
+ private val okHttpClient by lazy {
+ OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .followRedirects(false)
+ .followSslRedirects(false)
+ .cookieJar(object : CookieJar {
+ private val cookieStore = mutableMapOf>()
+ override fun saveFromResponse(url: HttpUrl, cookies: List) {
+ val existing = cookieStore.getOrPut(url.host) { mutableListOf() }
+ for (cookie in cookies) {
+ existing.removeAll {
+ it.name == cookie.name &&
+ it.domain == cookie.domain &&
+ it.path == cookie.path
+ }
+ existing += cookie
+ }
+ }
+
+ override fun loadForRequest(url: HttpUrl): List {
+ return cookieStore[url.host]
+ ?.filter { cookie -> cookie.matches(url) }
+ .orEmpty()
+ }
+ })
+ .build()
+ }
+
+ fun verify(challenge: Ts43Challenge?): ChallengeResponse? {
+ if (challenge == null) return null
+ httpHistory.clear()
+
+ return try {
+ when {
+ challenge.client_challenge != null -> {
+ val op = challenge.client_challenge.operation
+ Log.d(TAG, "TS43: Client ODSA challenge route: ${op?.operation}")
+ if (op == null) {
+ buildFailureResponse(
+ challenge,
+ Ts43ChallengeResponseStatus.Code.TS43_STATUS_CHALLENGE_NOT_SET
+ )
+ } else {
+ val requestPayload = buildOdsaRequestPayload(
+ challenge.entitlement_url,
+ challenge.eap_aka_realm,
+ challenge.service_entitlement_request,
+ challenge.app_id
+ )
+ if (requestPayload == null) {
+ buildFailureResponse(
+ challenge,
+ Ts43ChallengeResponseStatus.Code.TS43_STATUS_INTERNAL_ERROR
+ )
+ } else {
+ val responseBody = performOdsaRequest(
+ challenge.entitlement_url,
+ op,
+ requestPayload,
+ challenge.app_id,
+ Ts43ChallengeResponseError.RequestType.TS43_REQUEST_TYPE_GET_PHONE_NUMBER_API
+ )
+ buildClientResponse(challenge, responseBody)
+ }
+ }
+ }
+
+ challenge.server_challenge != null -> {
+ val op = challenge.server_challenge.operation
+ Log.d(TAG, "TS43: Server ODSA challenge route: ${op?.operation}")
+ if (op == null) {
+ buildFailureResponse(
+ challenge,
+ Ts43ChallengeResponseStatus.Code.TS43_STATUS_CHALLENGE_NOT_SET
+ )
+ } else {
+ val requestPayload = buildOdsaRequestPayload(
+ challenge.entitlement_url,
+ challenge.eap_aka_realm,
+ challenge.service_entitlement_request,
+ challenge.app_id
+ )
+ if (requestPayload == null) {
+ buildFailureResponse(
+ challenge,
+ Ts43ChallengeResponseStatus.Code.TS43_STATUS_INTERNAL_ERROR
+ )
+ } else {
+ val responseBody = performOdsaRequest(
+ challenge.entitlement_url,
+ op,
+ requestPayload,
+ challenge.app_id,
+ Ts43ChallengeResponseError.RequestType.TS43_REQUEST_TYPE_ACQUIRE_TEMPORARY_TOKEN_API
+ )
+ buildServerResponse(challenge, responseBody)
+ }
+ }
+ }
+
+ else -> buildFailureResponse(
+ challenge,
+ Ts43ChallengeResponseStatus.Code.TS43_STATUS_CHALLENGE_NOT_SET
+ )
+ }
+ } catch (e: Ts43ApiException) {
+ Log.w(TAG, "TS43 API failure requestType=${e.requestType} http=${e.httpStatus}", e)
+ buildApiErrorResponse(challenge, e)
+ } catch (e: NullPointerException) {
+ Log.e(TAG, "TS43 null failure", e)
+ buildFailureResponse(
+ challenge,
+ Ts43ChallengeResponseStatus.Code.TS43_STATUS_INTERNAL_ERROR
+ )
+ } catch (e: RuntimeException) {
+ Log.e(TAG, "TS43 runtime failure", e)
+ buildFailureResponse(
+ challenge,
+ Ts43ChallengeResponseStatus.Code.TS43_STATUS_RUNTIME_ERROR
+ )
+ }
+ }
+
+ private fun performOdsaRequest(
+ entitlementUrl: String,
+ op: OdsaOperation,
+ req: ServiceEntitlementRequest?,
+ challengeAppId: String?,
+ requestType: Ts43ChallengeResponseError.RequestType
+ ): String {
+ val builder = op.builder(telephonyManager, req, currentAppIds(challengeAppId))
+ val url = builder.buildBaseUrl(entitlementUrl)
+ val userAgent = req?.userAgent(context) ?: "PRD-TS43 OS-Android/${Build.VERSION.RELEASE}"
+ val accept = req?.accept_content_type?.takeIf { it.isNotEmpty() } ?: "application/json"
+ val acceptLanguage = Locale.getDefault().toLanguageTag().ifEmpty { "en-US" }
+ httpHistory += "GET $url"
+
+ val request = Request.Builder()
+ .url(url)
+ .header("Accept", accept)
+ .header("User-Agent", userAgent)
+ .header("Accept-Language", acceptLanguage)
+ .build()
+
+ return try {
+ val response = okHttpClient.newCall(request).execute()
+ httpHistory += "RESP ${response.code} ${request.url}"
+ if (response.isSuccessful) {
+ response.body?.string()
+ ?: throw Ts43ApiException(
+ errorCode = errorCode(32),
+ httpStatus = response.code,
+ requestType = requestType
+ )
+ } else {
+ Log.w(TAG, "ODSA request failed: ${response.code} for ${op.operation}")
+ throw Ts43ApiException(
+ errorCode = errorCode(31),
+ httpStatus = response.code,
+ requestType = requestType
+ )
+ }
+ } catch (e: IOException) {
+ Log.e(TAG, " Network error in ODSA request", e)
+ throw Ts43ApiException(
+ errorCode = errorCode(30),
+ httpStatus = -1,
+ requestType = requestType,
+ cause = e
+ )
+ }
+ }
+
+ private fun buildOdsaRequestPayload(
+ entitlementUrl: String,
+ eapAkaRealm: String?,
+ req: ServiceEntitlementRequest?,
+ challengeAppId: String?
+ ): ServiceEntitlementRequest? {
+ if (req == null) return null
+ if (req.authentication_token.isNotEmpty() || req.temporary_token.isNotEmpty()) return req
+
+ val mccMnc = telephonyManager.simOperator ?: ""
+ val imsi = telephonyManager.subscriberId ?: ""
+ if (mccMnc.length < 5 || imsi.isEmpty()) return null
+
+ val eapId = eapAkaService.buildEapId(mccMnc, imsi, eapAkaRealm)
+ val builder = req.builder(telephonyManager, eapId, currentAppIds(challengeAppId))
+
+ val initialUrl = builder.buildBaseUrl(entitlementUrl)
+ val postUrl = entitlementUrl
+ val userAgent = req.userAgent(context)
+ val acceptLanguage = Locale.getDefault().toLanguageTag().ifEmpty { "en-US" }
+
+ // 1. Initial Identity Probe (GET)
+ var currentRequest = Request.Builder()
+ .url(initialUrl)
+ .header("Accept", "application/vnd.gsma.eap-relay.v1.0+json")
+ .header("User-Agent", userAgent)
+ .header("Accept-Language", acceptLanguage)
+ .build()
+ httpHistory += "GET $initialUrl"
+
+ // 2. EAP Rounds Loop
+ for (round in 1..3) {
+ val response = try {
+ okHttpClient.newCall(currentRequest).execute()
+ } catch (e: IOException) {
+ Log.e(TAG, "Network error in EAP round $round", e)
+ throw Ts43ApiException(
+ errorCode = errorCode(30),
+ httpStatus = -1,
+ requestType = Ts43ChallengeResponseError.RequestType.TS43_REQUEST_TYPE_AUTH_API,
+ cause = e
+ )
+ }
+
+ val body = response.body?.string() ?: return null
+ httpHistory += "RESP ${response.code} ${currentRequest.url}"
+ if (!response.isSuccessful) {
+ Log.w(TAG, "EAP round $round failed with code ${response.code}")
+ throw Ts43ApiException(
+ errorCode = errorCode(31),
+ httpStatus = response.code,
+ requestType = Ts43ChallengeResponseError.RequestType.TS43_REQUEST_TYPE_AUTH_API
+ )
+ }
+
+ val token = extractAuthToken(body)
+ if (token != null) {
+ return req.copy(authentication_token = token)
+ }
+
+ val eapRelayPacket = extractEapRelayPacket(body)
+ ?: throw Ts43ApiException(
+ errorCode = errorCode(32),
+ httpStatus = response.code,
+ requestType = Ts43ChallengeResponseError.RequestType.TS43_REQUEST_TYPE_AUTH_API
+ )
+
+ val akaResponse =
+ eapAkaService.performSimAkaAuth(eapRelayPacket, imsi, mccMnc) ?: return null
+
+ val postBody = JSONObject().put("eap-relay-packet", akaResponse).toString()
+ currentRequest = Request.Builder()
+ .url(postUrl)
+ .header(
+ "Accept",
+ "application/vnd.gsma.eap-relay.v1.0+json, text/vnd.wap.connectivity-xml"
+ )
+ .header("User-Agent", userAgent)
+ .header("Content-Type", "application/vnd.gsma.eap-relay.v1.0+json")
+ .header("Accept-Language", acceptLanguage)
+ .post(
+ postBody.toByteArray().toRequestBody(
+ "application/vnd.gsma.eap-relay.v1.0+json".toMediaType()
+ )
+ )
+ .build()
+ httpHistory += "POST $postUrl"
+ }
+
+ return null
+ }
+
+ private fun currentAppIds(challengeAppId: String? = null): List {
+ return listOfNotNull(
+ challengeAppId?.takeIf { it.isNotBlank() }
+ ).ifEmpty {
+ listOf("ap2014")
+ }
+ }
+
+ private fun extractEapRelayPacket(body: String): String? {
+ return runCatching { JSONObject(body).optString("eap-relay-packet") }
+ .getOrNull()
+ ?.takeIf { it.isNotEmpty() }
+ }
+
+ private fun extractAuthToken(body: String): String? {
+ runCatching {
+ JSONObject(body).optJSONObject("Token")?.optString("token")
+ }.getOrNull()?.takeIf { it.isNotEmpty() }?.let { return it }
+
+ return runCatching {
+ val normalized = body
+ .replace("&", "&")
+ .replace("&", "&")
+ .replace("\r\n", "\n")
+ val doc = DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder()
+ .parse(InputSource(StringReader(normalized)))
+ val parms = doc.getElementsByTagName("parm")
+ for (i in 0 until parms.length) {
+ val node = parms.item(i)
+ val attrs = node.attributes ?: continue
+ val name = attrs.getNamedItem("name")?.nodeValue ?: continue
+ if (name == "token") {
+ return@runCatching attrs.getNamedItem("value")?.nodeValue
+ }
+ }
+ null
+ }.getOrNull()?.takeIf { it.isNotEmpty() }
+ }
+
+ private fun buildServerResponse(
+ challenge: Ts43Challenge,
+ responseBody: String
+ ): ChallengeResponse {
+ return ChallengeResponse(
+ ts43_challenge_response = Ts43ChallengeResponse(
+ ts43_type = challenge.ts43_type,
+ server_challenge_response = ServerChallengeResponse(
+ acquire_temporary_token_response = responseBody
+ ),
+ http_history = httpHistory.toList()
+ )
+ )
+ }
+
+ private fun buildClientResponse(
+ challenge: Ts43Challenge,
+ responseBody: String
+ ): ChallengeResponse {
+ return ChallengeResponse(
+ ts43_challenge_response = Ts43ChallengeResponse(
+ ts43_type = challenge.ts43_type,
+ client_challenge_response = ClientChallengeResponse(
+ get_phone_number_response = responseBody
+ ),
+ http_history = httpHistory.toList()
+ )
+ )
+ }
+
+ private fun buildFailureResponse(
+ challenge: Ts43Challenge,
+ statusCode: Ts43ChallengeResponseStatus.Code
+ ): ChallengeResponse {
+ return ChallengeResponse(
+ ts43_challenge_response = Ts43ChallengeResponse(
+ ts43_type = challenge.ts43_type,
+ status = Ts43ChallengeResponseStatus(status_code = statusCode),
+ http_history = httpHistory.toList()
+ )
+ )
+ }
+
+ private fun buildApiErrorResponse(
+ challenge: Ts43Challenge,
+ error: Ts43ApiException
+ ): ChallengeResponse {
+ return ChallengeResponse(
+ ts43_challenge_response = Ts43ChallengeResponse(
+ ts43_type = challenge.ts43_type,
+ status = Ts43ChallengeResponseStatus(
+ error = Ts43ChallengeResponseError(
+ error_code = error.errorCode,
+ http_status = error.httpStatus,
+ request_type = error.requestType
+ )
+ ),
+ http_history = httpHistory.toList()
+ )
+ )
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/EapAkaService.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/EapAkaService.kt
new file mode 100644
index 0000000000..9e271e5a4f
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/EapAkaService.kt
@@ -0,0 +1,211 @@
+package org.microg.gms.constellation.verification.ts43
+
+import android.os.Build
+import android.telephony.TelephonyManager
+import android.util.Base64
+import android.util.Log
+import androidx.annotation.RequiresApi
+import java.nio.ByteBuffer
+import java.nio.charset.StandardCharsets
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
+
+private const val TAG = "EapAkaService"
+
+class EapAkaService(private val telephonyManager: TelephonyManager) {
+
+ companion object {
+ private const val EAP_CODE_REQUEST = 1
+ private const val EAP_CODE_RESPONSE = 2
+
+ private const val EAP_TYPE_AKA = 23
+
+ private const val EAP_AKA_SUBTYPE_CHALLENGE = 1
+ private const val EAP_AKA_SUBTYPE_SYNC_FAILURE = 4
+
+ private const val AT_RAND = 1
+ private const val AT_AUTN = 2
+ private const val AT_RES = 3
+ private const val AT_AUTS = 4
+ private const val AT_MAC = 11
+
+ private const val SIM_RES_SUCCESS = 0xDB.toByte()
+ private const val SIM_RES_SYNC_FAIL = 0xDC.toByte()
+ }
+
+ @RequiresApi(Build.VERSION_CODES.N)
+ fun performSimAkaAuth(eapRelayBase64: String, imsi: String, mccMnc: String): String? {
+ val eapPacket = Base64.decode(eapRelayBase64, Base64.DEFAULT)
+ if (eapPacket.size < 12) return null
+
+ val code = eapPacket[0].toInt()
+ val eapId = eapPacket[1]
+ val type = eapPacket[4].toInt()
+ val subtype = eapPacket[5].toInt()
+
+ if (code != EAP_CODE_REQUEST || type != EAP_TYPE_AKA || subtype != EAP_AKA_SUBTYPE_CHALLENGE) {
+ Log.w(TAG, "Unexpected EAP packet: code=$code, type=$type, subtype=$subtype")
+ return null
+ }
+
+ // Parse attributes (starting at offset 8)
+ var rand: ByteArray? = null
+ var autn: ByteArray? = null
+
+ var offset = 8
+ while (offset + 2 <= eapPacket.size) {
+ val attrType = eapPacket[offset].toInt() and 0xFF
+ val attrLen = (eapPacket[offset + 1].toInt() and 0xFF) * 4
+ if (offset + attrLen > eapPacket.size || attrLen < 4) break
+
+ when (attrType) {
+ AT_RAND -> {
+ if (attrLen >= 20) {
+ rand = ByteArray(16)
+ System.arraycopy(eapPacket, offset + 4, rand, 0, 16)
+ }
+ }
+
+ AT_AUTN -> {
+ if (attrLen >= 20) {
+ autn = ByteArray(16)
+ System.arraycopy(eapPacket, offset + 4, autn, 0, 16)
+ }
+ }
+ }
+ offset += attrLen
+ if (rand != null && autn != null) break
+ }
+
+ if (rand == null || autn == null) {
+ Log.e(TAG, "Missing RAND or AUTN in EAP-AKA challenge")
+ return null
+ }
+
+ val challengeBytes = byteArrayOf(16) + rand + byteArrayOf(16) + autn
+ val challengeB64 = Base64.encodeToString(challengeBytes, Base64.NO_WRAP)
+
+ val iccAuthResult = telephonyManager.getIccAuthentication(
+ TelephonyManager.APPTYPE_USIM, TelephonyManager.AUTHTYPE_EAP_AKA, challengeB64
+ ) ?: run {
+ Log.e(TAG, "SIM returned null for AKA auth")
+ return null
+ }
+
+ val iccBytes = Base64.decode(iccAuthResult, Base64.DEFAULT)
+ if (iccBytes.isEmpty()) return null
+
+ return when (iccBytes[0]) {
+ SIM_RES_SUCCESS -> {
+ val res = extractTlv(1, iccBytes) ?: return null
+ val ck = extractTlv(1 + res.size + 1, iccBytes) ?: return null
+ val ik = extractTlv(1 + res.size + 1 + ck.size + 1, iccBytes) ?: return null
+
+ val identity = buildEapId(mccMnc, imsi)
+ val identityBytes = identity.toByteArray(StandardCharsets.UTF_8)
+ val keys = Fips186Prf.deriveKeys(identityBytes, ik, ck)
+
+ val kAut = keys["K_aut"] ?: run {
+ Log.e(TAG, "Failed to derive K_aut")
+ return null
+ }
+
+ val responsePacket = buildEapAkaResponse(eapId, res, kAut) ?: return null
+ Base64.encodeToString(responsePacket, Base64.NO_WRAP)
+ }
+
+ SIM_RES_SYNC_FAIL -> {
+ val auts = extractTlv(1, iccBytes) ?: return null
+ val responsePacket = buildEapAkaSyncFailure(eapId, auts)
+ Base64.encodeToString(responsePacket, Base64.NO_WRAP)
+ }
+
+ else -> {
+ Log.e(TAG, "Unknown SIM response tag: ${iccBytes[0]}")
+ null
+ }
+ }
+ }
+
+ fun buildEapId(mccMnc: String, imsi: String, realm: String? = null): String {
+ val mcc = mccMnc.substring(0, 3)
+ var mnc = mccMnc.substring(3)
+ if (mnc.length == 2) mnc = "0$mnc" // Zero-pad 2-digit MNCs
+ val defaultRealm = "nai.epc.mnc$mnc.mcc$mcc.3gppnetwork.org"
+ val resolvedRealm = when {
+ realm.isNullOrBlank() -> defaultRealm
+ realm == "nai.epc" -> defaultRealm
+ realm.contains(".mnc") && realm.contains(".mcc") && realm.contains("3gppnetwork.org") -> realm
+ else -> realm
+ }
+ return "0$imsi@$resolvedRealm"
+ }
+
+ private fun extractTlv(index: Int, data: ByteArray): ByteArray? {
+ if (index >= data.size) return null
+ val len = data[index].toInt() and 0xFF
+ if (index + 1 + len > data.size) return null
+ return data.copyOfRange(index + 1, index + 1 + len)
+ }
+
+ private fun buildEapAkaResponse(id: Byte, res: ByteArray, kAut: ByteArray): ByteArray? {
+ val resAttrLen = ((res.size + 4 + 3) / 4) * 4
+ val totalLen = 8 + resAttrLen + 20
+ val buffer = ByteBuffer.allocate(totalLen)
+
+ buffer.put(EAP_CODE_RESPONSE.toByte())
+ buffer.put(id)
+ buffer.putShort(totalLen.toShort())
+ buffer.put(EAP_TYPE_AKA.toByte())
+ buffer.put(EAP_AKA_SUBTYPE_CHALLENGE.toByte())
+ buffer.putShort(0)
+
+ buffer.put(AT_RES.toByte())
+ buffer.put((resAttrLen / 4).toByte())
+ buffer.putShort((res.size * 8).toShort())
+ buffer.put(res)
+
+ val padding = resAttrLen - 4 - res.size
+ if (padding > 0) buffer.put(ByteArray(padding))
+
+ buffer.position()
+ buffer.put(AT_MAC.toByte())
+ buffer.put(5)
+ buffer.putShort(0)
+ val macValueOffset = buffer.position()
+ buffer.put(ByteArray(16))
+
+ val packet = buffer.array()
+ val mac = hmacSha1(kAut, packet) ?: return null
+
+ System.arraycopy(mac, 0, packet, macValueOffset, 16)
+ return packet
+ }
+
+ private fun buildEapAkaSyncFailure(id: Byte, auts: ByteArray): ByteArray {
+ val attrLen = ((auts.size + 2 + 3) / 4) * 4
+ val totalLen = 8 + attrLen
+
+ return ByteBuffer.allocate(totalLen).apply {
+ put(EAP_CODE_RESPONSE.toByte())
+ put(id)
+ putShort(totalLen.toShort())
+ put(EAP_TYPE_AKA.toByte())
+ put(EAP_AKA_SUBTYPE_SYNC_FAILURE.toByte())
+ putShort(0)
+
+ put(AT_AUTS.toByte())
+ put((attrLen / 4).toByte())
+ put(auts)
+
+ val padding = attrLen - 2 - auts.size
+ if (padding > 0) put(ByteArray(padding))
+ }.array()
+ }
+
+ private fun hmacSha1(key: ByteArray, data: ByteArray): ByteArray? = try {
+ Mac.getInstance("HmacSHA1").apply { init(SecretKeySpec(key, "HmacSHA1")) }.doFinal(data)
+ } catch (_: Exception) {
+ null
+ }
+}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/Fips186Prf.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/Fips186Prf.kt
new file mode 100644
index 0000000000..0f2b138ebe
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/Fips186Prf.kt
@@ -0,0 +1,108 @@
+package org.microg.gms.constellation.verification.ts43
+
+import java.security.MessageDigest
+import java.security.NoSuchAlgorithmException
+
+object Fips186Prf {
+ fun deriveKeys(
+ identityBytes: ByteArray,
+ ik: ByteArray,
+ ck: ByteArray
+ ): Map {
+ val xKey = try {
+ val md = MessageDigest.getInstance("SHA-1")
+ md.update(identityBytes)
+ md.update(ik)
+ md.update(ck)
+ md.digest()
+ } catch (_: NoSuchAlgorithmException) {
+ ByteArray(20)
+ }
+
+ if (xKey.size != 20) return emptyMap()
+
+ val result = ByteArray(160)
+ val xKeyWorking = xKey.copyOf()
+ var resultOffset = 0
+
+ repeat(8) {
+ val h = intArrayOf(
+ 0x67452301,
+ 0xEFCDAB89.toInt(),
+ 0x98BADCFE.toInt(),
+ 0x10325476,
+ 0xC3D2E1F0.toInt()
+ )
+ val w = IntArray(80)
+ for (k in 0 until 16) {
+ val wordIdx = k * 4
+ w[k] = if (wordIdx < 20) {
+ ((xKeyWorking.getOrElse(wordIdx) { 0 }.toInt() and 0xFF) shl 24) or
+ ((xKeyWorking.getOrElse(wordIdx + 1) { 0 }
+ .toInt() and 0xFF) shl 16) or
+ ((xKeyWorking.getOrElse(wordIdx + 2) { 0 }
+ .toInt() and 0xFF) shl 8) or
+ (xKeyWorking.getOrElse(wordIdx + 3) { 0 }.toInt() and 0xFF)
+ } else 0
+ }
+ for (k in 16 until 80) {
+ val temp = w[k - 3] xor w[k - 8] xor w[k - 14] xor w[k - 16]
+ w[k] = (temp shl 1) or (temp ushr 31)
+ }
+ var a = h[0]
+ var b = h[1]
+ var c = h[2]
+ var d = h[3]
+ var e = h[4]
+ for (t in 0 until 80) {
+ val f: Int
+ val k: Int
+ when {
+ t <= 19 -> {
+ f = (b and c) or (b.inv() and d); k = 0x5A827999
+ }
+
+ t <= 39 -> {
+ f = b xor c xor d; k = 0x6ED9EBA1
+ }
+
+ t <= 59 -> {
+ f = (b and c) or (b and d) or (c and d); k = 0x8F1BBCDC.toInt()
+ }
+
+ else -> {
+ f = b xor c xor d; k = 0xCA62C1D6.toInt()
+ }
+ }
+ val temp = ((a shl 5) or (a ushr 27)) + f + e + k + w[t]
+ e = d; d = c; c = (b shl 30) or (b ushr 2); b = a; a = temp
+ }
+ val block = IntArray(5)
+ block[0] = h[0] + a; block[1] = h[1] + b; block[2] = h[2] + c; block[3] =
+ h[3] + d; block[4] = h[4] + e
+ for (k in 0 until 5) {
+ val word = block[k]
+ result[resultOffset++] = (word shr 24).toByte()
+ result[resultOffset++] = (word shr 16).toByte()
+ result[resultOffset++] = (word shr 8).toByte()
+ result[resultOffset++] = word.toByte()
+ }
+ var carry = 1
+ for (k in 19 downTo 0) {
+ val resByte = result[resultOffset - 20 + k].toInt() and 0xFF
+ val keyByte = xKeyWorking[k].toInt() and 0xFF
+ val sum = carry + keyByte + resByte
+ xKeyWorking[k] = sum.toByte()
+ carry = sum shr 8
+ }
+ }
+
+ // RFC 4187 Section 7: PRF output slicing
+ return mapOf(
+ "K_encr" to result.copyOfRange(0, 16),
+ "K_aut" to result.copyOfRange(16, 32),
+ "MSK" to result.copyOfRange(32, 96),
+ "EMSK" to result.copyOfRange(96, 160)
+ )
+ }
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/ServiceEntitlementExtension.kt b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/ServiceEntitlementExtension.kt
new file mode 100644
index 0000000000..71f9e4c87b
--- /dev/null
+++ b/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/ServiceEntitlementExtension.kt
@@ -0,0 +1,200 @@
+package org.microg.gms.constellation.verification.ts43
+
+import android.content.Context
+import android.telephony.TelephonyManager
+import okhttp3.HttpUrl
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import org.microg.gms.constellation.proto.OdsaOperation
+import org.microg.gms.constellation.proto.ServiceEntitlementRequest
+
+fun ServiceEntitlementRequest.builder(
+ telephonyManager: TelephonyManager,
+ eapId: String,
+ appIds: List = emptyList()
+) = ServiceEntitlementBuilder(
+ imsi = telephonyManager.subscriberId ?: "",
+ iccid = try {
+ telephonyManager.simSerialNumber
+ } catch (_: Exception) {
+ null
+ },
+ terminalId = telephonyManager.imei,
+ groupIdLevel1 = runCatching { telephonyManager.groupIdLevel1 }.getOrNull(),
+ eapId = eapId,
+ appIds = appIds,
+ req = this
+)
+
+fun ServiceEntitlementRequest.userAgent(context: Context): String {
+ val packageVersion = runCatching {
+ context.packageManager.getPackageInfo(context.packageName, 0).versionName.orEmpty()
+ }.getOrDefault("")
+ val vendor = terminal_vendor.take(4)
+ val model = terminal_model.take(10)
+ val swVersion = terminal_software_version.take(20)
+ return "PRD-TS43 term-$vendor/$model /$packageVersion OS-Android/$swVersion"
+}
+
+fun OdsaOperation.builder(
+ telephonyManager: TelephonyManager,
+ serviceEntitlementRequest: ServiceEntitlementRequest? = null,
+ appIds: List = emptyList()
+) = ServiceEntitlementBuilder(
+ imsi = telephonyManager.subscriberId ?: "",
+ iccid = try {
+ telephonyManager.simSerialNumber
+ } catch (_: Exception) {
+ null
+ },
+ terminalId = telephonyManager.imei,
+ groupIdLevel1 = runCatching { telephonyManager.groupIdLevel1 }.getOrNull(),
+ eapId = "", // Not needed for ODSA
+ appIds = appIds,
+ req = serviceEntitlementRequest ?: ServiceEntitlementRequest(),
+ odsa = this
+)
+
+class ServiceEntitlementBuilder(
+ private val imsi: String,
+ private val iccid: String?,
+ private val terminalId: String?,
+ private val groupIdLevel1: String?,
+ private val eapId: String,
+ private val appIds: List,
+ private val req: ServiceEntitlementRequest,
+ private val odsa: OdsaOperation? = null
+) {
+ fun buildBaseUrl(entitlementUrl: String): HttpUrl {
+ val baseUrl = entitlementUrl.toHttpUrl()
+
+ // GMS truncates these fields: vendor (4), model (10), sw_version (20)
+ val vendor = req.terminal_vendor.take(4)
+ val model = req.terminal_model.take(10)
+ val swVersion = req.terminal_software_version.take(20)
+
+ return baseUrl.newBuilder().apply {
+ when {
+ req.authentication_token.isNotEmpty() -> {
+ addQueryParameter("token", req.authentication_token)
+ if (imsi.isNotEmpty()) addQueryParameter("IMSI", imsi)
+ }
+
+ req.temporary_token.isNotEmpty() -> {
+ addQueryParameter("temporary_token", req.temporary_token)
+ }
+
+ eapId.isNotEmpty() -> {
+ addQueryParameter("EAP_ID", eapId)
+ }
+ }
+ addQueryParameter("terminal_id", terminalId ?: req.terminal_id)
+ if (req.gid1.isNotEmpty()) {
+ addQueryParameter("GID1", req.gid1)
+ } else if ((req.entitlement_version.toBigDecimalOrNull()?.toInt() ?: 0) >= 12) {
+ groupIdLevel1?.takeIf { it.isNotEmpty() }?.let { addQueryParameter("GID1", it) }
+ }
+ if (req.app_version.isNotEmpty()) addQueryParameter("app_version", req.app_version)
+ addQueryParameter("terminal_vendor", vendor)
+ addQueryParameter("terminal_model", model)
+ addQueryParameter("terminal_sw_version", swVersion)
+ addQueryParameter("app_name", req.app_name)
+ if (req.boost_type.isNotEmpty()) addQueryParameter("boost_type", req.boost_type)
+ if (appIds.isNotEmpty()) {
+ appIds.forEach { addQueryParameter("app", it) }
+ } else {
+ addQueryParameter("app", "ap2014")
+ }
+ addQueryParameter("vers", req.configuration_version.toString())
+ addQueryParameter("entitlement_version", req.entitlement_version)
+ if (req.notification_token.isNotEmpty()) {
+ addQueryParameter("notif_action", req.notification_action.toString())
+ addQueryParameter("notif_token", req.notification_token)
+ }
+
+ // Handle ODSA specific fields if present
+ odsa?.let {
+ addQueryParameter("operation", it.operation)
+ if (it.operation_type != -1) {
+ addQueryParameter("operation_type", it.operation_type.toString())
+ }
+ if (it.operation_targets.isNotEmpty()) {
+ addQueryParameter("operation_targets", it.operation_targets.joinToString(","))
+ }
+ if (it.terminal_iccid.isNotEmpty()) addQueryParameter(
+ "terminal_iccid",
+ it.terminal_iccid
+ )
+ else iccid?.let { i -> addQueryParameter("terminal_iccid", i) }
+
+ if (it.terminal_eid.isNotEmpty()) addQueryParameter("terminal_eid", it.terminal_eid)
+ if (it.target_terminal_id.isNotEmpty()) addQueryParameter(
+ "target_terminal_id",
+ it.target_terminal_id
+ )
+ if (it.target_terminal_iccid.isNotEmpty()) addQueryParameter(
+ "target_terminal_iccid",
+ it.target_terminal_iccid
+ )
+ if (it.target_terminal_eid.isNotEmpty()) addQueryParameter(
+ "target_terminal_eid",
+ it.target_terminal_eid
+ )
+ if (it.target_terminal_model.isNotEmpty()) addQueryParameter(
+ "target_terminal_model",
+ it.target_terminal_model
+ )
+ if (it.target_terminal_serial_number.isNotEmpty()) addQueryParameter(
+ "target_terminal_sn",
+ it.target_terminal_serial_number
+ )
+
+ it.target_terminal_ids.forEach { id ->
+ addQueryParameter("target_terminal_imeis", id)
+ }
+
+ if (it.old_terminal_id.isNotEmpty()) addQueryParameter(
+ "old_terminal_id",
+ it.old_terminal_id
+ )
+ if (it.old_terminal_iccid.isNotEmpty()) addQueryParameter(
+ "old_terminal_iccid",
+ it.old_terminal_iccid
+ )
+
+ // Companion fields
+ if (it.companion_terminal_id.isNotEmpty()) addQueryParameter(
+ "companion_terminal_id",
+ it.companion_terminal_id
+ )
+ if (it.companion_terminal_vendor.isNotEmpty()) addQueryParameter(
+ "companion_terminal_vendor",
+ it.companion_terminal_vendor
+ )
+ if (it.companion_terminal_model.isNotEmpty()) addQueryParameter(
+ "companion_terminal_model",
+ it.companion_terminal_model
+ )
+ if (it.companion_terminal_software_version.isNotEmpty()) addQueryParameter(
+ "companion_terminal_sw_version",
+ it.companion_terminal_software_version
+ )
+ if (it.companion_terminal_friendly_name.isNotEmpty()) addQueryParameter(
+ "companion_terminal_friendly_name",
+ it.companion_terminal_friendly_name
+ )
+ if (it.companion_terminal_service.isNotEmpty()) addQueryParameter(
+ "companion_terminal_service",
+ it.companion_terminal_service
+ )
+ if (it.companion_terminal_iccid.isNotEmpty()) addQueryParameter(
+ "companion_terminal_iccid",
+ it.companion_terminal_iccid
+ )
+ if (it.companion_terminal_eid.isNotEmpty()) addQueryParameter(
+ "companion_terminal_eid",
+ it.companion_terminal_eid
+ )
+ }
+ }.build()
+ }
+}
diff --git a/play-services-constellation/src/main/proto/constellation.proto b/play-services-constellation/src/main/proto/constellation.proto
new file mode 100644
index 0000000000..29a8bd027a
--- /dev/null
+++ b/play-services-constellation/src/main/proto/constellation.proto
@@ -0,0 +1,1025 @@
+syntax = "proto3";
+option java_package = "org.microg.gms.constellation.proto";
+
+package google.internal.communications.phonedeviceverification.v1;
+
+import "google/protobuf/timestamp.proto";
+
+// Verification and consent RPCs.
+service PhoneDeviceVerification {
+ rpc GetConsent(GetConsentRequest) returns (GetConsentResponse);
+ rpc SetConsent(SetConsentRequest) returns (SetConsentResponse);
+ rpc Sync(SyncRequest) returns (SyncResponse);
+ rpc Proceed(ProceedRequest) returns (ProceedResponse);
+}
+
+// Read-only phone number retrieval RPC.
+service PhoneNumber {
+ rpc GetVerifiedPhoneNumbers(GetVerifiedPhoneNumbersRequest) returns (GetVerifiedPhoneNumbersResponse);
+}
+
+// Shared request and response messages.
+// Header for each client request.
+message RequestHeader {
+ ClientInfo client_info = 1;
+ ClientAuth client_auth = 2;
+ string session_id = 3;
+ RequestTrigger trigger = 4;
+}
+message ResponseHeader {
+ StatusProtoSimple status = 1;
+}
+message StatusProtoSimple {
+ int32 code = 1;
+}
+enum StatusCode {
+ STATUS_CODE_UNSPECIFIED = 0;
+ STATUS_CODE_OK = 1;
+ STATUS_CODE_ERROR = 2;
+}
+message ClientInfo {
+ DeviceID device_id = 1;
+ bytes client_public_key = 2;
+ string locale = 3;
+ int32 gmscore_version_number = 4;
+ string gmscore_version = 5;
+ int32 android_sdk_version = 6;
+ DroidGuardSignals droidguard_signals = 8;
+ repeated Experiment experiments = 9;
+ UserProfileType user_profile_type = 11;
+ repeated GaiaToken gaia_tokens = 12;
+ CountryInfo country_info = 13;
+ repeated NetworkSignal connectivity_infos = 14;
+ string model = 15;
+ string manufacturer = 16;
+ repeated SimOperatorInfo partial_sim_infos = 17;
+ DeviceType device_type = 18;
+ bool is_wearable_standalone = 19;
+ GaiaSignals gaia_signals = 20;
+ string device_fingerprint = 21;
+}
+
+enum UserProfileType {
+ UNKNOWN_PROFILE_TYPE = 0;
+ REGULAR_USER = 1;
+ MANAGED_PROFILE = 2;
+}
+
+enum DeviceType {
+ DEVICE_TYPE_UNKNOWN = 0;
+ DEVICE_TYPE_PHONE = 1;
+ DEVICE_TYPE_PHONE_GO = 2;
+ DEVICE_TYPE_TV = 3;
+ DEVICE_TYPE_WEARABLE = 4;
+ DEVICE_TYPE_AUTOMOTIVE = 5;
+ DEVICE_TYPE_BATTLESTAR = 6;
+ DEVICE_TYPE_CHROME_OS = 7;
+ DEVICE_TYPE_XR = 8;
+ DEVICE_TYPE_DESKTOP = 9;
+ DEVICE_TYPE_XR_PERIPHERAL = 10;
+}
+message ClientAuth {
+ DeviceID device_id = 1;
+ bytes signature = 2;
+ google.protobuf.Timestamp sign_timestamp = 3;
+}
+message CountryInfo {
+ repeated string sim_countries = 1;
+ repeated string network_countries = 2;
+}
+message DroidGuardSignals {
+ string droidguard_result = 1;
+ string droidguard_token = 2;
+}
+message DeviceID {
+ string iid_token = 1;
+ int64 primary_device_id = 2;
+ int64 user_serial = 3;
+ int64 gms_android_id = 4;
+}
+message RequestTrigger {
+ enum Type {
+ UNKNOWN = 0;
+ PERIODIC_CONSENT_CHECK = 1;
+ PERIODIC_REFRESH = 2;
+ SIM_STATE_CHANGED = 3;
+ GAIA_CHANGE_EVENT = 4;
+ USER_SETTINGS = 5;
+ DEBUG_SETTINGS = 6;
+ TRIGGER_API_CALL = 7;
+ REBOOT_CHECKER = 8;
+ SERVER_TRIGGER = 9;
+ FAILURE_RETRY = 10;
+ CONSENT_API_TRIGGER = 11;
+ PNVR_DEVICE_SETTINGS = 12;
+ }
+ Type type = 1;
+}
+message DroidGuardAttestation {
+ string droidguard_result = 1;
+ string droidguard_token = 2;
+}
+message Experiment {
+ string key = 1;
+ string value = 2;
+}
+message GaiaID {
+ string id = 1;
+}
+message GaiaToken {
+ string token = 1;
+}
+message GaiaSignalEntry {
+ string gaia_id = 1;
+ GaiaAccountSignalType signal_type = 2;
+ google.protobuf.Timestamp timestamp = 3;
+}
+
+enum GaiaAccountSignalType {
+ GAIA_ACCOUNT_SIGNAL_UNSPECIFIED = 0;
+ GAIA_ACCOUNT_SIGNAL_AUTHENTICATED = 1;
+ GAIA_ACCOUNT_SIGNAL_UNAUTHENTICATED = 2;
+ GAIA_ACCOUNT_SIGNAL_REMOVED_WITH_GMS_AUTH_BROADCAST = 3;
+}
+message GaiaSignals {
+ repeated GaiaSignalEntry gaia_signals = 1;
+}
+message Status {
+ int32 code = 1;
+}
+
+// Named API params used by verification RPCs.
+message VerificationPolicy {
+ string policy_id = 1;
+ int64 max_verification_age_hours = 2;
+ IdTokenRequest id_token_request = 3;
+ string calling_package = 4;
+ repeated VerificationParam params = 5;
+}
+
+// Parameters for ID token generation.
+message IdTokenRequest {
+ string certificate_hash = 1;
+ string token_nonce = 2;
+}
+message VerificationParam {
+ string key = 1;
+ string value = 2;
+}
+message SimOperatorInfo {
+ string imsi_hash = 1;
+
+ string sim_operator = 3;
+}
+message NetworkSignal {
+ enum Type {
+ TYPE_UNKNOWN = 0;
+ TYPE_WIFI = 1;
+ TYPE_MOBILE = 2;
+ }
+ enum Availability {
+ AVAILABILITY_UNKNOWN = 0;
+ AVAILABLE = 1;
+ NOT_AVAILABLE = 2;
+ }
+ enum State {
+ STATE_UNKNOWN = 0;
+ STATE_CONNECTING = 1;
+ STATE_CONNECTED = 2;
+ STATE_DISCONNECTING = 3;
+ STATE_DISCONNECTED = 4;
+ STATE_SUSPENDED = 5;
+ }
+ Type type = 1;
+ State state = 2;
+ Availability availability = 3;
+}
+message MobileOperatorInfo {
+ string country_code = 1;
+ uint64 nil_since_usec = 2;
+ string operator = 3;
+ uint32 nil_since_micros = 4;
+ string operator_name = 5;
+}
+
+// Sync and proceed flow.
+message SyncRequest {
+ repeated Verification verifications = 3;
+ RequestHeader header = 4;
+ repeated VerificationToken verification_tokens = 5;
+}
+message SyncResponse {
+ repeated VerificationResponse responses = 1;
+ ServerTimestamp next_sync_time = 2;
+ ResponseHeader header = 3;
+ DroidguardToken droidguard_token = 4;
+ repeated VerificationToken verification_tokens = 5;
+}
+message ProceedRequest {
+ Verification verification = 2;
+ ChallengeResponse challenge_response = 3;
+ RequestHeader header = 4;
+}
+message ProceedResponse {
+ Verification verification = 1;
+ ResponseHeader header = 2;
+ ServerTimestamp next_sync_time = 3;
+ DroidguardToken droidguard_token = 4;
+}
+
+// Server time plus the time when the server wrote this message.
+message ServerTimestamp {
+ google.protobuf.Timestamp timestamp = 1;
+ google.protobuf.Timestamp now = 2;
+}
+message VerificationToken {
+ bytes token = 1;
+ google.protobuf.Timestamp expiration_time = 2;
+}
+
+// Verification state and challenge model.
+message Verification {
+ enum State {
+ UNKNOWN = 0;
+ NONE = 1;
+ PENDING = 2;
+ VERIFIED = 3;
+ }
+
+ enum Status {
+ STATUS_UNKNOWN = 0;
+ STATUS_NONE = 1;
+ STATUS_PENDING = 2;
+ STATUS_VERIFIED = 3;
+ STATUS_THROTTLED = 4;
+ STATUS_FAILED = 5;
+ STATUS_SKIPPED = 6;
+ STATUS_NOT_REQUIRED = 7;
+ STATUS_PHONE_NUMBER_ENTRY_REQUIRED = 8;
+ STATUS_INELIGIBLE = 9;
+ STATUS_DENIED = 10;
+ STATUS_NOT_IN_SERVICE = 11;
+ }
+
+ VerificationAssociation association = 1;
+ Status status = 2;
+ oneof info {
+ VerificationInfo verification_info = 3;
+ PendingVerificationInfo pending_verification_info = 4;
+ UnverifiedInfo unverified_info = 9;
+ }
+ TelephonyInfo telephony_info = 5;
+ repeated Param api_params = 6;
+ ChallengePreference challenge_preference = 7;
+ VerificationPolicy structured_api_params = 8;
+}
+message VerificationResponse {
+ Verification verification = 1;
+ StatusProto error = 2;
+}
+message StatusProto {
+ int32 code = 1;
+ string message = 3;
+}
+message SIMSlotInfo {
+ int32 slot_index = 1;
+ int32 subscription_id = 2;
+}
+message SIMAssociation {
+
+ message SIMInfo {
+ repeated string imsi = 1;
+ string sim_readable_number = 2;
+ repeated TelephonyPhoneNumber telephony_phone_number = 3;
+ string iccid = 4;
+ }
+
+ message TelephonyPhoneNumber {
+ string phone_number = 1;
+ TelephonyPhoneNumberType phone_number_type = 2;
+ }
+
+ SIMInfo sim_info = 1;
+ repeated GaiaToken gaia_tokens = 2;
+ SIMSlotInfo sim_slot = 4;
+}
+message GaiaAssociation {
+}
+message VerificationAssociation {
+ oneof association {
+ SIMAssociation sim = 1;
+ GaiaAssociation gaia = 2;
+ }
+}
+
+// Information for a verified record.
+message VerificationInfo {
+ string phone_number = 1;
+ google.protobuf.Timestamp verification_time = 2;
+ VerificationMethod challenge_method = 6;
+}
+message PendingVerificationInfo {
+ Challenge challenge = 2;
+}
+
+// Challenge payloads and responses.
+// Challenge issued by the server.
+message Challenge {
+ ChallengeID challenge_id = 1;
+ VerificationMethod type = 2;
+ MoChallenge mo_challenge = 3;
+ CarrierIdChallenge carrier_id_challenge = 4;
+ ServerTimestamp expiry_time = 5;
+ MTChallenge mt_challenge = 6;
+ RegisteredSmsChallenge registered_sms_challenge = 7;
+ FlashCallChallenge flash_call_challenge = 8;
+ Ts43Challenge ts43_challenge = 12;
+}
+
+// Carrier ID challenge used for SS7 traffic.
+message CarrierIdChallenge {
+ string isim_request = 3;
+ int32 auth_type = 5;
+ int32 app_type = 6;
+}
+message MTChallenge {
+ string sms = 1;
+}
+message ChallengeID {
+ string id = 1;
+}
+message Capabilities {
+ string droidguard_result = 1;
+ string droidguard_token = 2;
+}
+
+// MO SMS challenge.
+message MoChallenge {
+ string proxy_number = 1;
+ DataSmsInfo data_sms_info = 3;
+ string sms = 4;
+ string polling_intervals = 5;
+ string sms_without_persisting = 6;
+}
+message DataSmsInfo {
+ int32 destination_port = 1;
+}
+message RegisteredSmsChallenge {
+ repeated PhoneNumberID verified_senders = 1;
+}
+message PhoneNumberID {
+ bytes phone_number_id = 1;
+}
+message FlashCallChallenge {
+ repeated PhoneRange phone_ranges = 1;
+ FlashCallState state = 2;
+ repeated ChallengeID previous_challenge_ids = 3;
+ repeated ChallengeResponse previous_challenge_responses = 4;
+ int64 millis_between_interceptions = 5;
+}
+message PhoneRange {
+ string phone_number_prefix = 1;
+ string phone_number_suffix = 2;
+ string country_code = 3;
+ string carrier = 4;
+}
+
+enum FlashCallState {
+ FLASH_CALL_STATE_UNKNOWN = 0;
+ FLASH_CALL_STATE_PREPARING = 1;
+ FLASH_CALL_STATE_VERIFYING = 2;
+ FLASH_CALL_STATE_VERIFIED = 3;
+ FLASH_CALL_STATE_FAILED = 4;
+}
+message UnverifiedInfo {
+ enum Reason {
+ UNKNOWN_REASON = 0;
+ THROTTLED = 1;
+ FAILED = 2;
+ SKIPPED = 3;
+ NOT_REQUIRED = 4;
+ PHONE_NUMBER_ENTRY_REQUIRED = 5;
+ INELIGIBLE = 6;
+ DENIED = 7;
+ NOT_IN_SERVICE = 8;
+ }
+ Reason reason = 1;
+ google.protobuf.Timestamp retry_after_time = 2;
+ VerificationMethod challenge_method = 3;
+}
+
+// Challenge response.
+message ChallengeResponse {
+ MTChallengeResponseData mt_response = 1;
+ CarrierIdChallengeResponse carrier_id_response = 2;
+ MOChallengeResponseData mo_response = 3;
+ RegisteredSmsChallengeResponse registered_sms_response = 6;
+ FlashCallChallengeResponse flash_call_response = 7;
+ Ts43ChallengeResponse ts43_challenge_response = 9;
+}
+message MTChallengeResponseData {
+ string sms = 1;
+ string sender = 2;
+}
+message CarrierIdChallengeResponse {
+ string isim_response = 3;
+ CarrierIdError carrier_id_error = 4;
+}
+
+enum CarrierIdError {
+ CARRIER_ID_ERROR_NO_ERROR = 0;
+ CARRIER_ID_ERROR_NOT_SUPPORTED = 1;
+ CARRIER_ID_ERROR_RETRY_ATTEMPT_EXCEEDED = 2;
+ CARRIER_ID_ERROR_NULL_RESPONSE = 3;
+ CARRIER_ID_ERROR_REFLECTION_ERROR = 4;
+ CARRIER_ID_ERROR_NO_SIM = 5;
+ CARRIER_ID_ERROR_UNABLE_TO_READ_SUBSCRIPTION = 6;
+ CARRIER_ID_ERROR_UNKNOWN_ERROR = 7;
+ CARRIER_ID_ERROR_ENTITLEMENT_SERVER_ERROR = 8;
+ CARRIER_ID_ERROR_JSON_PARSE_ERROR = 9;
+ CARRIER_ID_ERROR_INTERNAL_ERROR = 10;
+ CARRIER_ID_ERROR_INVALID_ARGUMENT = 11;
+}
+message MOChallengeResponseData {
+ enum Status {
+ UNKNOWN_STATUS = 0;
+ COMPLETED = 1;
+ FAILED_TO_SEND_MO = 2;
+ NO_ACTIVE_SUBSCRIPTION = 3;
+ NO_SMS_MANAGER = 4;
+ }
+ Status status = 1;
+ int64 sms_result_code = 2;
+ int64 sms_error_code = 3;
+}
+message RegisteredSmsChallengeResponse {
+ repeated RegisteredSmsChallengeResponseItem items = 1;
+}
+message RegisteredSmsChallengeResponseItem {
+ RegisteredSmsChallengeResponsePayload payload = 2;
+}
+message RegisteredSmsChallengeResponsePayload {
+ bytes payload = 1;
+}
+message Ts43ChallengeResponse {
+ Ts43Type ts43_type = 1;
+ ClientChallengeResponse client_challenge_response = 2;
+ ServerChallengeResponse server_challenge_response = 3;
+ Ts43ChallengeResponseStatus status = 4;
+ repeated string http_history = 5;
+}
+message Ts43ChallengeResponseStatus {
+ enum Code {
+ TS43_STATUS_UNSPECIFIED = 0;
+ TS43_STATUS_NOT_SUPPORTED = 1;
+ TS43_STATUS_CHALLENGE_NOT_SET = 2;
+ TS43_STATUS_INTERNAL_ERROR = 3;
+ TS43_STATUS_RUNTIME_ERROR = 4;
+ TS43_STATUS_JSON_PARSE_ERROR = 5;
+ }
+
+ oneof result {
+ Code status_code = 1;
+ Ts43ChallengeResponseError error = 2;
+ }
+}
+
+// The numeric 1..9 range is intentionally left generic because those values are
+// overloaded across multiple TS.43 request stages.
+message Ts43ChallengeResponseError {
+ enum Code {
+ TS43_ERROR_CODE_UNSPECIFIED = 0;
+ TS43_ERROR_CODE_1 = 1;
+ TS43_ERROR_CODE_2 = 2;
+ TS43_ERROR_CODE_3 = 3;
+ TS43_ERROR_CODE_4 = 4;
+ TS43_ERROR_CODE_5 = 5;
+ TS43_ERROR_CODE_6 = 6;
+ TS43_ERROR_CODE_7 = 7;
+ TS43_ERROR_CODE_8 = 8;
+ TS43_ERROR_CODE_9 = 9;
+ TS43_ERROR_CODE_PHONE_UNAVAILABLE = 10;
+ TS43_ERROR_CODE_INVALID_IMSI_OR_MCCMNC = 11;
+ TS43_ERROR_CODE_EAP_AKA_CHALLENGE_FAILED = 20;
+ TS43_ERROR_CODE_EAP_AKA_SYNCHRONIZATION_FAILURE = 21;
+ TS43_ERROR_CODE_EAP_AKA_AUTHENTICATION_FAILED = 22;
+ TS43_ERROR_CODE_CONNECTIVITY_FAILURE = 30;
+ TS43_ERROR_CODE_HTTP_ERROR_STATUS = 31;
+ TS43_ERROR_CODE_HTTP_MALFORMED_RESPONSE = 32;
+ TS43_ERROR_CODE_TEMPORARY_TOKEN_UNAVAILABLE = 60;
+ }
+
+ enum RequestType {
+ TS43_REQUEST_TYPE_UNSPECIFIED = 0;
+ TS43_REQUEST_TYPE_AUTH_API = 1;
+ TS43_REQUEST_TYPE_GET_PHONE_NUMBER_API = 2;
+ TS43_REQUEST_TYPE_ACQUIRE_TEMPORARY_TOKEN_API = 3;
+ TS43_REQUEST_TYPE_SERVICE_ENTITLEMENT_API = 4;
+ }
+
+ Code error_code = 1;
+ int32 http_status = 2;
+ RequestType request_type = 3;
+}
+message ServerChallengeResponse {
+ string acquire_temporary_token_response = 2;
+}
+message ClientChallengeResponse {
+ string get_phone_number_response = 2;
+}
+message FlashCallChallengeResponse {
+ enum Error {
+ NO_ERROR = 0;
+ UNSPECIFIED = 1;
+ TIMED_OUT = 2;
+ NETWORK_NOT_AVAILABLE = 3;
+ TOO_MANY_CALLS = 4;
+ CONCURRENT_REQUESTS = 5;
+ IN_ECBM = 6;
+ IN_EMERGENCY_CALL = 7;
+ PRECONDITIONS_FAILED = 8;
+ API_NOT_AVAILABLE = 9;
+ ERROR_PREVIOUS_INCOMING_CALL = 10;
+ STATE_NOT_PREPARING = 11;
+ STATE_NOT_VERIFYING = 12;
+ ERROR_PENDING_VERIFICATION = 13;
+ PROCEED_FAILED = 14;
+ INTERCEPTION_FAILED = 15;
+ }
+ string caller = 1;
+ Error error = 2;
+}
+message Ts43Type {
+ enum Integrator {
+ TS43_INTEGRATOR_UNSPECIFIED = 0;
+ JIO = 1;
+ TELUS = 2;
+ ERICSSON = 3;
+ HPE = 4;
+ TMO = 5;
+ TMO_SERVER = 6;
+ TELENOR = 7;
+ RCS_CIS_PROXY = 8;
+ MOBI_US = 9;
+ SFR = 10;
+ SASKTEL_CANADA = 11;
+ DT = 13;
+ DT_SERVER = 14;
+ NETLYNC = 17;
+ ORANGE_FRANCE = 18;
+ AMDOCS = 19;
+ }
+ Integrator integrator = 1;
+}
+message CellularNetworkEvent {
+ google.protobuf.Timestamp timestamp = 1;
+ bool mobile_data_enabled = 2;
+ bool airplane_mode_enabled = 3;
+ bool data_roaming_enabled = 4;
+ bool mobile_data_always_on = 5;
+ repeated NetworkState networks = 6;
+}
+message NetworkState {
+ repeated int32 types = 1;
+ bool available = 2;
+}
+message ServiceStateEvent {
+ google.protobuf.Timestamp timestamp = 1;
+ bool airplane_mode_enabled = 2;
+ bool mobile_data_enabled = 3;
+ int32 voice_registration_state = 4;
+ int32 data_registration_state = 5;
+ int32 voice_network_type = 6;
+ int32 data_network_type = 7;
+ int32 signal_strength = 8;
+}
+message SMSEvent {
+ google.protobuf.Timestamp timestamp = 1;
+ EventDirection direction = 2;
+ EventPhoneNumberType number_type = 3;
+}
+message CallEvent {
+ google.protobuf.Timestamp timestamp = 1;
+ EventDirection direction = 2;
+}
+
+enum EventDirection {
+ UNKNOWN_DIRECTION = 0;
+ INCOMING = 1;
+ OUTGOING = 2;
+ MISSED = 3;
+}
+
+enum EventPhoneNumberType {
+ UNKNOWN_TYPE = 0;
+ LONG_NUMBER = 1;
+ SHORT_CODE = 2;
+}
+message TelephonyInfo {
+ enum PhoneType {
+ PHONE_TYPE_UNKNOWN = 0;
+ PHONE_TYPE_GSM = 1;
+ PHONE_TYPE_CDMA = 2;
+ PHONE_TYPE_SIP = 3;
+ }
+ enum SimState {
+ SIM_STATE_UNKNOWN = 0;
+ SIM_STATE_NOT_READY = 1;
+ SIM_STATE_READY = 2;
+ }
+ enum RoamingState {
+ ROAMING_UNKNOWN = 0;
+ ROAMING_HOME = 1;
+ ROAMING_ROAMING = 2;
+ }
+
+ enum ConnectivityState {
+ CONNECTIVITY_UNKNOWN = 0;
+ CONNECTIVITY_HOME = 1;
+ CONNECTIVITY_ROAMING = 2;
+ }
+ enum SmsCapability {
+ SMS_UNKNOWN = 0;
+ SMS_NOT_CAPABLE = 1;
+ SMS_CAPABLE = 2;
+ SMS_DEFAULT_CAPABILITY = 3;
+ SMS_RESTRICTED = 4;
+ }
+ enum ToggleState {
+ TOGGLE_UNKNOWN = 0;
+ TOGGLE_DISABLED = 1;
+ TOGGLE_ENABLED = 2;
+ }
+ enum ServiceState {
+ SERVICE_STATE_UNKNOWN = 0;
+ SERVICE_STATE_IN_SERVICE = 1;
+ SERVICE_STATE_OUT_OF_SERVICE = 2;
+ SERVICE_STATE_EMERGENCY_ONLY = 3;
+ SERVICE_STATE_POWER_OFF = 4;
+ }
+
+ PhoneType phone_type = 1;
+ string group_id_level1 = 2;
+ SimNetworkInfo sim_info = 3;
+ SimNetworkInfo network_info = 4;
+ RoamingState network_roaming = 5;
+ ConnectivityState connectivity_state = 6;
+ SmsCapability sms_capability = 7;
+ int32 active_sub_count = 8;
+ int32 active_sub_count_max = 9;
+ ToggleState vowifi_state = 11;
+ ToggleState sms_no_confirmation_state = 12;
+ SimState sim_state = 13;
+ string device_id = 15;
+ ServiceState service_state = 16;
+ repeated CellularNetworkEvent cellular_events = 17;
+ repeated CallEvent call_events = 18;
+ repeated SMSEvent sms_events = 19;
+ repeated ServiceStateEvent service_events = 20;
+ bool is_embedded = 21;
+ ToggleState carrier_id_capability = 23;
+ int64 sim_carrier_id = 25;
+}
+message SimNetworkInfo {
+ string country_iso = 1;
+ string operator = 2;
+ string operator_name = 3;
+ int32 inactive_time_diff_ms = 4;
+}
+message DroidguardToken {
+ string token = 1;
+ google.protobuf.Timestamp ttl = 2;
+}
+
+// Consent and Asterism messages.
+message GetConsentRequest {
+ DeviceID device_id = 1;
+ repeated GaiaToken gaia_tokens = 2;
+ RequestHeader header = 4;
+ repeated VerificationToken verification_tokens = 5;
+ AsterismClient asterism_client = 6;
+ VerificationPolicy structured_api_params = 7;
+ bool force_refresh = 8;
+ string session_id = 9;
+ bool unknown_flag = 10;
+}
+message GetConsentResponse {
+ RcsConsent rcs_consent = 1;
+ ServerTimestamp next_check_time = 5;
+ ConsentState device_consent = 6;
+ repeated GaiaConsent gaia_consents = 8;
+ DroidguardToken droidguard_token = 9;
+ PermissionState permission_state = 10;
+}
+
+// These values intentionally alias because the same field is reused across
+// multiple consent surfaces.
+enum FlowContext {
+ option allow_alias = true;
+ FLOW_CONTEXT_UNSPECIFIED = 0;
+ FLOW_CONTEXT_RCS_DEFAULT_ON_LEGAL_FYI = 0;
+ FLOW_CONTEXT_RCS_CONSENT = 1;
+ FLOW_CONTEXT_RCS_DEFAULT_ON_OUT_OF_BOX = 2;
+ FLOW_CONTEXT_RCS_SAMSUNG_UNFREEZE = 3;
+ FLOW_CONTEXT_RCS_DEFAULT_ON_LEGAL_FYI_IN_SETTINGS = 4;
+ FLOW_CONTEXT_RCS_UNKNOWN_5 = 5;
+}
+message SetConsentRequest {
+ RequestHeader header = 1;
+ RcsConsent rcs_consent = 3;
+ AsterismClient asterism_client = 4;
+ bytes audit_record = 6;
+ repeated Param api_params = 7;
+ OnDemandConsent on_demand_consent = 8;
+ FlowContext flow_context = 9;
+ oneof device_consent_oneof {
+ AsterismConsent device_consent = 10;
+ }
+}
+message SetConsentResponse {
+}
+
+enum Consent {
+ CONSENT_UNKNOWN = 0;
+ CONSENTED = 1;
+ NO_CONSENT = 2;
+ EXPIRED = 3;
+}
+
+// These values intentionally alias because RCS and device consent versions
+// share the same field.
+enum ConsentVersion {
+ option allow_alias = true;
+ CONSENT_VERSION_UNSPECIFIED = 0;
+ RCS_LEGAL_FYI = 0;
+ RCS_CONSENT = 1;
+ RCS_OUT_OF_BOX = 2;
+ PHONE_VERIFICATION_DEFAULT = 1;
+ PHONE_VERIFICATION_MESSAGES_CALLS_V1 = 2;
+ PHONE_VERIFICATION_INTL_SMS_CALLS = 3;
+ PHONE_VERIFICATION_REACHABILITY_INTL_SMS_CALLS = 4;
+}
+message AsterismConsent {
+ Consent consent = 1;
+ ConsentSource consent_source = 2;
+ ConsentVersion consent_version = 3;
+}
+message ConsentState {
+ Consent state = 2;
+}
+
+// `flags` is still an opaque server-defined bitfield.
+message PermissionState {
+ int32 flags = 1;
+ PermissionType type = 2;
+}
+enum PermissionType {
+ PERMISSION_TYPE_UNSPECIFIED = 0;
+ LEGACY_DPNV = 1;
+ PNVR = 2;
+ NOT_ALLOWED = 3;
+}
+message RcsConsent {
+ Consent consent = 2;
+ ConsentVersion consent_version = 3;
+}
+message GaiaConsent {
+ AsterismClient asterism_client = 1;
+ Consent consent = 2;
+ ConsentVersion consent_version = 3;
+}
+message OnDemandConsent {
+ Consent consent = 1;
+ GaiaToken gaia_token = 2;
+ string consent_variant = 3;
+ string trigger = 4;
+}
+enum ConsentSource {
+ SOURCE_UNSPECIFIED = 0;
+ ANDROID_DEVICE_SETTINGS = 1;
+ GAIA_USERNAME_RECOVERY = 2;
+ AOB_SETUP_WIZARD = 3;
+ MINUTEMAID_JS_BRIDGE = 4;
+ GAIA_WEB_JS_BRIDGE = 5;
+ AM_PROFILES = 6;
+ MEET_ON_DEMAND_CONSENT = 7;
+ GPAY_ON_DEMAND_CONSENT = 8;
+}
+enum AsterismClient {
+ UNKNOWN_CLIENT = 0;
+ CONSTELLATION = 1;
+ RCS = 2;
+ ONE_TIME_VERIFICATION = 3;
+}
+
+// Read-only phone number RPC.
+// Request message for GetVerifiedPhoneNumbers.
+message GetVerifiedPhoneNumbersRequest {
+ enum PhoneNumberSelection {
+ SELECTION_UNSPECIFIED = 0;
+ CONSTELLATION = 1;
+ RCS = 2;
+ }
+
+ string session_id = 1;
+ IIDTokenAuth iid_token_auth = 2;
+ repeated PhoneNumberSelection phone_number_selections = 3;
+ TokenOption token_option = 4;
+ string droidguard_result = 5;
+ ConsistencyOption consistency_option = 6;
+ RequestInfo request_info = 7;
+}
+
+// Response message for GetVerifiedPhoneNumbers.
+message GetVerifiedPhoneNumbersResponse {
+ repeated VerifiedPhoneNumber phone_numbers = 2;
+}
+message IIDTokenAuth {
+ string iid_token = 1;
+ bytes client_sign = 2;
+ google.protobuf.Timestamp sign_timestamp = 3;
+}
+message TokenOption {
+ string certificate_hash = 1;
+ string token_nonce = 2;
+ string package_name = 3;
+}
+message VerifiedPhoneNumber {
+ string phone_number = 1;
+ google.protobuf.Timestamp verification_time = 2;
+ string id_token = 3;
+ RcsState rcs_state = 4;
+}
+
+enum RcsState {
+ STATE_UNSPECIFIED = 0;
+ ACTIVE = 1;
+}
+
+enum TelephonyPhoneNumberType {
+ PHONE_NUMBER_SOURCE_UNSPECIFIED = 0;
+ PHONE_NUMBER_SOURCE_CARRIER = 1;
+ PHONE_NUMBER_SOURCE_UICC = 2;
+ PHONE_NUMBER_SOURCE_IMS = 3;
+}
+
+// Shared helpers.
+// Data consistency requirement for read-only RPCs.
+message ConsistencyOption {
+ enum Consistency {
+ CONSISTENCY_UNSPECIFIED = 0;
+ STALE = 1;
+ STRONG = 2;
+ }
+ Consistency consistency = 1;
+}
+
+// Request metadata for analytics and legacy policy attribution.
+message RequestInfo {
+ string policy_id = 1;
+}
+message Param {
+ string key = 1;
+ string value = 2;
+}
+
+// Client challenge preference.
+message ChallengePreference {
+ repeated VerificationMethod capabilities = 1;
+ ChallengePreferenceMetadata metadata = 2;
+}
+message ChallengePreferenceMetadata {
+ string sms_signature = 2;
+}
+enum VerificationMethod {
+ UNKNOWN = 0;
+ MO_SMS = 1;
+ MT_SMS = 2;
+ CARRIER_ID = 3;
+ IMSI_LOOKUP = 5;
+ REGISTERED_SMS = 7;
+ FLASH_CALL = 8;
+ TS43 = 11;
+}
+
+// Audit payloads.
+message AuditToken {
+ AuditTokenMetadata metadata = 2;
+}
+message AuditTokenMetadata {
+ AuditUuid uuid = 1;
+}
+message AuditUuid {
+ int64 uuid_msb = 1;
+ int64 uuid_lsb = 2;
+}
+message AuditPayload {
+ AuditDeviceInfo device_info = 1;
+ AuditDeviceId device_id = 2;
+ AuditEventMetadata event_metadata = 10;
+}
+message AuditDeviceInfo {
+ oneof device_identifier {
+ string android_id_hash = 1;
+ string instance_id = 2;
+ }
+}
+message AuditDeviceId {
+ string android_id_hash = 1;
+}
+message AuditEventMetadata {
+ AuditEventType event_type = 1;
+ string session_id = 2;
+ int64 event_timestamp = 3;
+ AuditConsentDetails consent_details = 4;
+ string package_name = 5;
+ string tos_url = 6;
+ string tos_version = 7;
+ string language = 8;
+ string country = 9;
+ string gaia_id = 10;
+ AuditComponentInfo component_info = 11;
+ string trigger = 12;
+}
+enum AuditEventType {
+ EVENT_TYPE_UNSPECIFIED = 0;
+ ASTERISM_CONSENT_CHANGE = 1;
+ RCS_CONSENT_CHANGE = 2;
+ VERIFICATION_COMPLETE = 3;
+}
+message AuditConsentDetails {
+ repeated int32 sim_slot_ids = 1;
+}
+message AuditComponentInfo {
+ AuditComponentType component_type = 1;
+}
+enum AuditComponentType {
+ COMPONENT_UNSPECIFIED = 0;
+ ASTERISM_CONSTELLATION = 119;
+ ASTERISM_RCS = 120;
+}
+message AuditConsentState {
+ Consent state = 1;
+}
+enum AuditEventTypeValue {
+ EVENT_VALUE_UNSPECIFIED = 0;
+ ASTERISM_CLIENT_CONSENT_CHANGE = 187;
+}
+
+// TS.43 challenge messages.
+message Ts43Challenge {
+ Ts43Type ts43_type = 1;
+ string entitlement_url = 2;
+ ServiceEntitlementRequest service_entitlement_request = 3;
+ ClientChallenge client_challenge = 4;
+ ServerChallenge server_challenge = 5;
+ string app_id = 6;
+ string eap_aka_realm = 7;
+}
+message ServerChallenge {
+ OdsaOperation operation = 1;
+}
+message ClientChallenge {
+ OdsaOperation operation = 1;
+}
+message OdsaOperation {
+ string operation = 1;
+ int32 operation_type = 2;
+ repeated string operation_targets = 3;
+ string companion_terminal_id = 4;
+ string companion_terminal_vendor = 5;
+ string companion_terminal_model = 6;
+ string companion_terminal_software_version = 7;
+ string companion_terminal_friendly_name = 8;
+ string companion_terminal_service = 9;
+ string companion_terminal_iccid = 10;
+ string companion_terminal_eid = 11;
+ string terminal_iccid = 12;
+ string terminal_eid = 13;
+ string target_terminal_id = 14;
+ repeated string target_terminal_ids = 15;
+ string target_terminal_iccid = 16;
+ string target_terminal_eid = 17;
+ string target_terminal_serial_number = 18;
+ string target_terminal_model = 19;
+ string old_terminal_id = 20;
+ string old_terminal_iccid = 21;
+}
+message ServiceEntitlementRequest {
+ int32 notification_action = 1;
+ string entitlement_version = 2;
+ string temporary_token = 3;
+ string authentication_token = 4;
+ string terminal_id = 5;
+ string terminal_vendor = 6;
+ string terminal_model = 7;
+ string terminal_software_version = 8;
+ string app_name = 9;
+ string app_version = 10;
+ string notification_token = 11;
+ int32 configuration_version = 12;
+ string accept_content_type = 13;
+ string boost_type = 14;
+ string gid1 = 15;
+}
+
diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle
index 8fc2896bbc..47c2dc6553 100644
--- a/play-services-core/build.gradle
+++ b/play-services-core/build.gradle
@@ -59,6 +59,7 @@ dependencies {
implementation project(':play-services-auth-base')
implementation project(':play-services-auth')
implementation project(':play-services-clearcut')
+ implementation project(':play-services-constellation')
implementation project(':play-services-drive')
implementation project(':play-services-games')
implementation project(':play-services-maps')
diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml
index 66038da7e2..1b7e601d97 100644
--- a/play-services-core/src/main/AndroidManifest.xml
+++ b/play-services-core/src/main/AndroidManifest.xml
@@ -179,6 +179,9 @@
android:name="android.hardware.camera"
android:required="false" />
+
+
+
+
+
+
+
+
@@ -1218,7 +1228,6 @@
-
diff --git a/play-services-iid/src/main/java/com/google/android/gms/iid/InstanceID.java b/play-services-iid/src/main/java/com/google/android/gms/iid/InstanceID.java
index 327d8b1cc0..92df450a4f 100644
--- a/play-services-iid/src/main/java/com/google/android/gms/iid/InstanceID.java
+++ b/play-services-iid/src/main/java/com/google/android/gms/iid/InstanceID.java
@@ -246,8 +246,24 @@ public InstanceIdStore getStore() {
}
@PublicApi(exclude = true)
- public String requestToken(String authorizedEntity, String scope, Bundle extras) {
- throw new UnsupportedOperationException();
+ public String requestToken(String authorizedEntity, String scope, Bundle extras) throws IOException {
+ if (extras == null) extras = new Bundle();
+
+ extras.putString(EXTRA_SENDER, authorizedEntity);
+ if (scope != null) {
+ extras.putString(EXTRA_SCOPE, scope);
+ }
+
+ String actualSubtype = TextUtils.isEmpty(this.subtype) ? authorizedEntity : this.subtype;
+
+ if (!extras.containsKey("legacy.register")) {
+ extras.putString(EXTRA_SUBSCIPTION, authorizedEntity);
+ extras.putString(EXTRA_SUBTYPE, actualSubtype);
+ extras.putString("X-" + EXTRA_SUBSCIPTION, authorizedEntity);
+ extras.putString("X-" + EXTRA_SUBTYPE, actualSubtype);
+ }
+
+ return rpc.handleRegisterMessageResult(rpc.sendRegisterMessageBlocking(extras, getKeyPair()));
}
private synchronized KeyPair getKeyPair() {
diff --git a/settings.gradle b/settings.gradle
index 1ab99b9000..bc68c6a7b1 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -36,6 +36,7 @@ include ':play-services-basement'
include ':play-services-cast'
include ':play-services-cast-framework'
include ':play-services-clearcut'
+include ':play-services-constellation'
include ':play-services-drive'
include ':play-services-droidguard'
include ':play-services-fido'
From 977afd11ee1855cd932e9da43413a04916a3502f Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Thu, 26 Mar 2026 00:17:26 -0400
Subject: [PATCH 02/31] Refactor and small cleanups
---
play-services-constellation/build.gradle | 30 +--------
play-services-constellation/core/build.gradle | 64 +++++++++++++++++++
.../core/src/main/AndroidManifest.xml | 26 ++++++++
.../gms/constellation/GetPnvCapabilities.kt | 30 +++++++++
.../gms/constellation/VerifyPhoneNumber.kt | 6 ++
.../gms/constellation/core}/AuthManager.kt | 2 +-
.../core}/ConstellationApiService.kt | 2 +-
.../core}/ConstellationStateStore.kt | 21 +++---
.../gms/constellation/core}/GServices.kt | 2 +-
.../gms/constellation/core}/GetIidToken.kt | 2 +-
.../constellation/core}/GetPnvCapabilities.kt | 15 ++---
.../core}/GetPnvCapabilitiesApiPhenotype.kt | 2 +-
.../core}/GetVerifiedPhoneNumbers.kt | 17 +++--
.../constellation/core}/IidTokenPhenotypes.kt | 2 +-
.../gms/constellation/core}/RpcClient.kt | 6 +-
.../core}/VerificationMappings.kt | 10 +--
.../core}/VerificationSettingsPhenotypes.kt | 2 +-
.../constellation/core}/VerifyPhoneNumber.kt | 37 +++++++----
.../core}/VerifyPhoneNumberApiPhenotypes.kt | 2 +-
.../core}/proto/builders/ClientInfoBuilder.kt | 32 ++++------
.../core}/proto/builders/CommonBuilders.kt | 27 ++++----
.../core}/proto/builders/GaiaInfoBuilder.kt | 13 ++--
.../proto/builders/RequestBuildContext.kt | 6 +-
.../proto/builders/SyncRequestBuilder.kt | 35 +++++-----
.../proto/builders/TelephonyInfoBuilder.kt | 11 ++--
.../core}/verification/CarrierIdVerifier.kt | 10 +--
.../core}/verification/ChallengeProcessor.kt | 24 +++----
.../core}/verification/MoSmsVerifier.kt | 12 ++--
.../core}/verification/MtSmsVerifier.kt | 8 +--
.../verification/RegisteredSmsVerifier.kt | 14 ++--
.../core}/verification/Ts43Verifier.kt | 27 ++++----
.../core}/verification/ts43/EapAkaService.kt | 2 +-
.../core}/verification/ts43/Fips186Prf.kt | 2 +-
.../ts43/ServiceEntitlementExtension.kt | 6 +-
.../src/main/proto/constellation.proto | 2 +-
.../src/main/AndroidManifest.xml | 15 +----
.../gms/constellation/GetPnvCapabilities.kt | 28 +-------
.../gms/constellation/PhoneNumberInfo.kt | 1 -
.../gms/constellation/VerifyPhoneNumber.kt | 6 +-
play-services-core/build.gradle | 2 +-
.../src/main/AndroidManifest.xml | 10 ---
settings.gradle | 1 +
42 files changed, 311 insertions(+), 261 deletions(-)
create mode 100644 play-services-constellation/core/build.gradle
create mode 100644 play-services-constellation/core/src/main/AndroidManifest.xml
create mode 100644 play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
create mode 100644 play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/AuthManager.kt (98%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/ConstellationApiService.kt (99%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/ConstellationStateStore.kt (91%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/GServices.kt (95%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/GetIidToken.kt (96%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/GetPnvCapabilities.kt (93%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/GetPnvCapabilitiesApiPhenotype.kt (69%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/GetVerifiedPhoneNumbers.kt (89%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/IidTokenPhenotypes.kt (88%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/RpcClient.kt (88%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/VerificationMappings.kt (94%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/VerificationSettingsPhenotypes.kt (76%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/VerifyPhoneNumber.kt (90%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/VerifyPhoneNumberApiPhenotypes.kt (97%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/proto/builders/ClientInfoBuilder.kt (86%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/proto/builders/CommonBuilders.kt (69%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/proto/builders/GaiaInfoBuilder.kt (85%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/proto/builders/RequestBuildContext.kt (70%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/proto/builders/SyncRequestBuilder.kt (85%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/proto/builders/TelephonyInfoBuilder.kt (94%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/verification/CarrierIdVerifier.kt (90%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/verification/ChallengeProcessor.kt (88%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/verification/MoSmsVerifier.kt (95%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/verification/MtSmsVerifier.kt (94%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/verification/RegisteredSmsVerifier.kt (92%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/verification/Ts43Verifier.kt (94%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/verification/ts43/EapAkaService.kt (99%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/verification/ts43/Fips186Prf.kt (98%)
rename play-services-constellation/{src/main/kotlin/org/microg/gms/constellation => core/src/main/kotlin/org/microg/gms/constellation/core}/verification/ts43/ServiceEntitlementExtension.kt (97%)
rename play-services-constellation/{ => core}/src/main/proto/constellation.proto (99%)
diff --git a/play-services-constellation/build.gradle b/play-services-constellation/build.gradle
index 2301c04063..e0cc981be3 100644
--- a/play-services-constellation/build.gradle
+++ b/play-services-constellation/build.gradle
@@ -10,16 +10,6 @@ apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
apply plugin: 'signing'
-buildscript {
- repositories {
- mavenCentral()
- }
- dependencies {
- classpath 'com.squareup.wire:wire-gradle-plugin:4.9.3'
- }
-}
-apply plugin: 'com.squareup.wire'
-
android {
namespace "org.microg.gms.constellation"
@@ -46,28 +36,12 @@ android {
}
}
-wire {
- kotlin {
- rpcRole = 'client'
- rpcCallStyle = 'suspending'
- }
-}
-
apply from: '../gradle/publish-android.gradle'
-description = 'microG service implementation for play-services-constellation'
+description = 'microG implementation of play-services-constellation'
dependencies {
- implementation project(':play-services-base-core')
- implementation project(':play-services-iid')
- implementation project(':play-services-auth-base')
-
- implementation project(':play-services-droidguard')
- implementation project(':play-services-tasks-ktx')
-
- api 'com.squareup.wire:wire-runtime:4.9.3'
- api 'com.squareup.wire:wire-grpc-client:4.9.3'
- api 'com.squareup.okhttp3:okhttp:4.12.0'
+ implementation project(':play-services-basement')
kapt project(":safe-parcel-processor")
}
diff --git a/play-services-constellation/core/build.gradle b/play-services-constellation/core/build.gradle
new file mode 100644
index 0000000000..c51c1f39ea
--- /dev/null
+++ b/play-services-constellation/core/build.gradle
@@ -0,0 +1,64 @@
+/*
+ * SPDX-FileCopyrightText: 2026, microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'maven-publish'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-parcelize'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'signing'
+apply plugin: 'com.squareup.wire'
+
+android {
+ namespace "org.microg.gms.constellation.core"
+
+ compileSdkVersion androidCompileSdk
+ buildToolsVersion "$androidBuildVersionTools"
+
+ defaultConfig {
+ versionName version
+ minSdkVersion androidMinSdk
+ targetSdkVersion androidTargetSdk
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = 1.8
+ }
+}
+
+wire {
+ kotlin {
+ rpcRole = 'client'
+ rpcCallStyle = 'suspending'
+ }
+}
+
+apply from: '../../gradle/publish-android.gradle'
+
+description = 'microG service implementation for play-services-constellation'
+
+dependencies {
+ api project(':play-services-constellation')
+
+ implementation project(':play-services-base-core')
+ implementation project(':play-services-iid')
+ implementation project(':play-services-auth-base')
+
+ implementation project(':play-services-droidguard')
+ implementation project(':play-services-tasks-ktx')
+
+ api "com.squareup.wire:wire-runtime:$wireVersion"
+ api "com.squareup.wire:wire-grpc-client:$wireVersion"
+ api "com.squareup.okhttp3:okhttp:$okHttpVersion"
+}
diff --git a/play-services-constellation/core/src/main/AndroidManifest.xml b/play-services-constellation/core/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..cbe65637d0
--- /dev/null
+++ b/play-services-constellation/core/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt b/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
new file mode 100644
index 0000000000..3bd89b61e0
--- /dev/null
+++ b/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
@@ -0,0 +1,30 @@
+package com.google.android.gms.constellation
+
+enum class VerificationStatus(val value: Int) {
+ SUPPORTED(1),
+ UNSUPPORTED_CARRIER(2),
+ UNSUPPORTED_API_VERSION(3),
+ UNSUPPORTED_SIM_NOT_READY(4);
+
+ companion object {
+ fun fromInt(value: Int): VerificationStatus = when (value) {
+ 2 -> UNSUPPORTED_CARRIER
+ 3 -> UNSUPPORTED_API_VERSION
+ 4 -> UNSUPPORTED_SIM_NOT_READY
+ else -> SUPPORTED
+ }
+ }
+}
+
+operator fun VerificationCapability.Companion.invoke(
+ verificationMethod: Int,
+ status: VerificationStatus
+): VerificationCapability {
+ return VerificationCapability(
+ verificationMethod = verificationMethod,
+ statusValue = status.value
+ )
+}
+
+val VerificationCapability.status: VerificationStatus
+ get() = VerificationStatus.fromInt(statusValue)
\ No newline at end of file
diff --git a/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt b/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
new file mode 100644
index 0000000000..95c87e2688
--- /dev/null
+++ b/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
@@ -0,0 +1,6 @@
+package com.google.android.gms.constellation
+
+import org.microg.gms.constellation.core.proto.VerificationMethod
+
+val VerifyPhoneNumberRequest.verificationMethods: List
+ get() = verificationMethodsValues.mapNotNull { VerificationMethod.fromValue(it) }
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/AuthManager.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
similarity index 98%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/AuthManager.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
index 148d585ed3..94b4bdb8a8 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/AuthManager.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
import android.content.Context
import android.util.Base64
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationApiService.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationApiService.kt
similarity index 99%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationApiService.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationApiService.kt
index a6829f734b..f4ddbb4123 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationApiService.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationApiService.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
import android.content.Context
import android.os.Bundle
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationStateStore.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationStateStore.kt
similarity index 91%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationStateStore.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationStateStore.kt
index 5c6f07bd08..dc802dfacc 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/ConstellationStateStore.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationStateStore.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
import android.annotation.SuppressLint
import android.content.Context
@@ -6,14 +6,14 @@ import android.util.Base64
import androidx.core.content.edit
import com.squareup.wire.Instant
import okio.ByteString.Companion.toByteString
-import org.microg.gms.constellation.proto.DroidguardToken
-import org.microg.gms.constellation.proto.ProceedResponse
-import org.microg.gms.constellation.proto.Consent
-import org.microg.gms.constellation.proto.ConsentSource
-import org.microg.gms.constellation.proto.ConsentVersion
-import org.microg.gms.constellation.proto.ServerTimestamp
-import org.microg.gms.constellation.proto.SyncResponse
-import org.microg.gms.constellation.proto.VerificationToken
+import org.microg.gms.constellation.core.proto.Consent
+import org.microg.gms.constellation.core.proto.ConsentSource
+import org.microg.gms.constellation.core.proto.ConsentVersion
+import org.microg.gms.constellation.core.proto.DroidguardToken
+import org.microg.gms.constellation.core.proto.ProceedResponse
+import org.microg.gms.constellation.core.proto.ServerTimestamp
+import org.microg.gms.constellation.core.proto.SyncResponse
+import org.microg.gms.constellation.core.proto.VerificationToken
private const val STATE_PREFS_NAME = "constellation_prefs"
private const val TOKEN_PREFS_NAME = "com.google.android.gms.constellation"
@@ -119,7 +119,8 @@ object ConstellationStateStore {
}
fun loadPnvrNoticeConsent(context: Context): Consent {
- val value = statePrefs(context).getInt(KEY_PNVR_NOTICE_CONSENT, Consent.CONSENT_UNKNOWN.value)
+ val value =
+ statePrefs(context).getInt(KEY_PNVR_NOTICE_CONSENT, Consent.CONSENT_UNKNOWN.value)
return Consent.fromValue(value) ?: Consent.CONSENT_UNKNOWN
}
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GServices.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GServices.kt
similarity index 95%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GServices.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GServices.kt
index ca5b6acbb9..a8c3c64481 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GServices.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GServices.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
import android.content.ContentResolver
import android.net.Uri
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetIidToken.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt
similarity index 96%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetIidToken.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt
index 08775f8b9f..9ba0e0b8af 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetIidToken.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
import android.content.Context
import android.util.Log
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilities.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilities.kt
similarity index 93%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilities.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilities.kt
index 38fec9f3e8..72d4617f2e 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilities.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilities.kt
@@ -1,6 +1,5 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
-import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
@@ -9,7 +8,6 @@ import android.telephony.TelephonyManager
import android.util.Base64
import android.util.Log
import androidx.annotation.RequiresApi
-import androidx.annotation.RequiresPermission
import androidx.core.content.getSystemService
import com.google.android.gms.common.api.ApiMetadata
import com.google.android.gms.common.api.Status
@@ -19,6 +17,7 @@ import com.google.android.gms.constellation.SimCapability
import com.google.android.gms.constellation.VerificationCapability
import com.google.android.gms.constellation.VerificationStatus
import com.google.android.gms.constellation.internal.IConstellationCallbacks
+import com.google.android.gms.constellation.invoke
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.security.MessageDigest
@@ -26,12 +25,6 @@ import java.security.MessageDigest
private const val TAG = "GetPnvCapabilities"
@SuppressLint("HardwareIds")
-@RequiresPermission(
- allOf =
- [
- Manifest.permission.READ_PHONE_STATE,
- "android.permission.READ_PRIVILEGED_PHONE_STATE"]
-)
@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
suspend fun handleGetPnvCapabilities(
context: Context,
@@ -67,7 +60,9 @@ suspend fun handleGetPnvCapabilities(
VerificationCapability(
9,
when {
- !GetPnvCapabilitiesApiPhenotype.FPNV_ALLOWED_CARRIER_IDS.contains(carrierId) ->
+ !GetPnvCapabilitiesApiPhenotype.FPNV_ALLOWED_CARRIER_IDS.contains(
+ carrierId
+ ) ->
VerificationStatus.UNSUPPORTED_CARRIER
telephonyManager.simState != TelephonyManager.SIM_STATE_READY ->
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilitiesApiPhenotype.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilitiesApiPhenotype.kt
similarity index 69%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilitiesApiPhenotype.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilitiesApiPhenotype.kt
index 814fae3f1c..9a7f8b21bd 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetPnvCapabilitiesApiPhenotype.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilitiesApiPhenotype.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
object GetPnvCapabilitiesApiPhenotype {
val FPNV_ALLOWED_CARRIER_IDS = ArrayList()
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetVerifiedPhoneNumbers.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
similarity index 89%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetVerifiedPhoneNumbers.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
index b382bef5a8..5ab87e178c 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/GetVerifiedPhoneNumbers.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
import android.content.Context
import android.os.Bundle
@@ -12,12 +12,12 @@ import com.google.android.gms.constellation.internal.IConstellationCallbacks
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okio.ByteString.Companion.toByteString
-import org.microg.gms.constellation.proto.GetVerifiedPhoneNumbersRequest
-import org.microg.gms.constellation.proto.GetVerifiedPhoneNumbersRequest.PhoneNumberSelection
-import org.microg.gms.constellation.proto.IIDTokenAuth
-import org.microg.gms.constellation.proto.TokenOption
-import org.microg.gms.constellation.proto.VerifiedPhoneNumber as RpcVerifiedPhoneNumber
+import org.microg.gms.constellation.core.proto.GetVerifiedPhoneNumbersRequest
+import org.microg.gms.constellation.core.proto.GetVerifiedPhoneNumbersRequest.PhoneNumberSelection
+import org.microg.gms.constellation.core.proto.IIDTokenAuth
+import org.microg.gms.constellation.core.proto.TokenOption
import java.util.UUID
+import org.microg.gms.constellation.core.proto.VerifiedPhoneNumber as RpcVerifiedPhoneNumber
private const val TAG = "GetVerifiedPhoneNumbers"
@@ -79,7 +79,10 @@ internal suspend fun fetchVerifiedPhoneNumbers(
}
internal fun List.toVerifyPhoneNumberResponse(): VerifyPhoneNumberResponse {
- return VerifyPhoneNumberResponse(map { it.toPhoneNumberVerification() }.toTypedArray(), Bundle.EMPTY)
+ return VerifyPhoneNumberResponse(
+ map { it.toPhoneNumberVerification() }.toTypedArray(),
+ Bundle.EMPTY
+ )
}
private fun RpcVerifiedPhoneNumber.toPhoneNumberInfo(): PhoneNumberInfo {
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/IidTokenPhenotypes.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/IidTokenPhenotypes.kt
similarity index 88%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/IidTokenPhenotypes.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/IidTokenPhenotypes.kt
index a700f12f2f..984c902321 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/IidTokenPhenotypes.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/IidTokenPhenotypes.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
object IidTokenPhenotypes {
const val ASTERISM_PROJECT_NUMBER = 496232013492
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/RpcClient.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/RpcClient.kt
similarity index 88%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/RpcClient.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/RpcClient.kt
index f7a49733aa..57495656c3 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/RpcClient.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/RpcClient.kt
@@ -1,12 +1,12 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
import com.squareup.wire.GrpcClient
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import org.microg.gms.common.Constants
-import org.microg.gms.constellation.proto.PhoneDeviceVerificationClient
-import org.microg.gms.constellation.proto.PhoneNumberClient
+import org.microg.gms.constellation.core.proto.PhoneDeviceVerificationClient
+import org.microg.gms.constellation.core.proto.PhoneNumberClient
private class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationMappings.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
similarity index 94%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationMappings.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
index 55bbdda27e..62255e1fc0 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationMappings.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
@@ -1,11 +1,11 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
import android.os.Bundle
import com.google.android.gms.constellation.PhoneNumberVerification
-import org.microg.gms.constellation.proto.Param
-import org.microg.gms.constellation.proto.UnverifiedInfo
-import org.microg.gms.constellation.proto.Verification
-import org.microg.gms.constellation.proto.VerificationMethod
+import org.microg.gms.constellation.core.proto.Param
+import org.microg.gms.constellation.core.proto.UnverifiedInfo
+import org.microg.gms.constellation.core.proto.Verification
+import org.microg.gms.constellation.core.proto.VerificationMethod
fun UnverifiedInfo.Reason.toVerificationStatus(): Verification.Status {
return when (this) {
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationSettingsPhenotypes.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationSettingsPhenotypes.kt
similarity index 76%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationSettingsPhenotypes.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationSettingsPhenotypes.kt
index c51f0a279e..f8deaa5a7f 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerificationSettingsPhenotypes.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationSettingsPhenotypes.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
object VerificationSettingsPhenotypes {
const val A2P_SMS_SIGNAL_GRANULARITY_HRS = 1L
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumber.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
similarity index 90%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumber.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
index d0dc3b314c..1d07ea48c3 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumber.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
import android.content.Context
import android.os.Build
@@ -18,13 +18,13 @@ import com.squareup.wire.GrpcException
import com.squareup.wire.GrpcStatus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import org.microg.gms.constellation.proto.SyncRequest
-import org.microg.gms.constellation.proto.Verification
-import org.microg.gms.constellation.proto.builders.RequestBuildContext
-import org.microg.gms.constellation.proto.builders.buildImsiToSubscriptionInfoMap
-import org.microg.gms.constellation.proto.builders.buildRequestContext
-import org.microg.gms.constellation.proto.builders.invoke
-import org.microg.gms.constellation.verification.ChallengeProcessor
+import org.microg.gms.constellation.core.proto.SyncRequest
+import org.microg.gms.constellation.core.proto.Verification
+import org.microg.gms.constellation.core.proto.builders.RequestBuildContext
+import org.microg.gms.constellation.core.proto.builders.buildImsiToSubscriptionInfoMap
+import org.microg.gms.constellation.core.proto.builders.buildRequestContext
+import org.microg.gms.constellation.core.proto.builders.invoke
+import org.microg.gms.constellation.core.verification.ChallengeProcessor
import java.util.UUID
private const val TAG = "VerifyPhoneNumber"
@@ -62,7 +62,7 @@ suspend fun handleVerifyPhoneNumberV1(
targetedSims = emptyList(),
silent = false,
apiVersion = 2,
- verificationTypes = emptyList()
+ verificationMethodsValues = emptyList()
)
val policyId = bundle.getString("policy_id", "")
val mode = bundle.getInt("verification_mode", 0)
@@ -111,7 +111,7 @@ suspend fun handleVerifyPhoneNumberSingleUse(
targetedSims = emptyList(),
silent = false,
apiVersion = 2,
- verificationTypes = emptyList()
+ verificationMethodsValues = emptyList()
)
handleVerifyPhoneNumberRequest(
@@ -200,12 +200,20 @@ private suspend fun handleVerifyPhoneNumberRequest(
Log.e(TAG, "verifyPhoneNumber failed", e)
when (readCallbackMode) {
ReadCallbackMode.LEGACY -> {
- callbacks.onPhoneNumberVerified(Status.INTERNAL_ERROR, emptyList(), ApiMetadata.DEFAULT)
+ callbacks.onPhoneNumberVerified(
+ Status.INTERNAL_ERROR,
+ emptyList(),
+ ApiMetadata.DEFAULT
+ )
}
ReadCallbackMode.NONE -> {
if (legacyCallbackOnFullFlow) {
- callbacks.onPhoneNumberVerified(Status.INTERNAL_ERROR, emptyList(), ApiMetadata.DEFAULT)
+ callbacks.onPhoneNumberVerified(
+ Status.INTERNAL_ERROR,
+ emptyList(),
+ ApiMetadata.DEFAULT
+ )
} else {
callbacks.onPhoneNumberVerificationsCompleted(
Status.INTERNAL_ERROR,
@@ -300,7 +308,10 @@ private suspend fun executeSyncFlow(
if (e.grpcStatus == GrpcStatus.PERMISSION_DENIED ||
e.grpcStatus == GrpcStatus.UNAUTHENTICATED
) {
- Log.w(TAG, "Suspicious client status ${e.grpcStatus.name}. Clearing DroidGuard cache...")
+ Log.w(
+ TAG,
+ "Suspicious client status ${e.grpcStatus.name}. Clearing DroidGuard cache..."
+ )
ConstellationStateStore.clearDroidGuardToken(context)
}
throw e
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumberApiPhenotypes.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumberApiPhenotypes.kt
similarity index 97%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumberApiPhenotypes.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumberApiPhenotypes.kt
index 93faca2dd1..ceebfcb771 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/VerifyPhoneNumberApiPhenotypes.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumberApiPhenotypes.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation
+package org.microg.gms.constellation.core
object VerifyPhoneNumberApiPhenotypes {
val PACKAGES_ALLOWED_TO_CALL = listOf(
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/ClientInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
similarity index 86%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/ClientInfoBuilder.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
index 99e224cb3d..bc97172d63 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/ClientInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
@@ -1,37 +1,34 @@
-package org.microg.gms.constellation.proto.builders
+package org.microg.gms.constellation.core.proto.builders
-import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
-import androidx.annotation.RequiresPermission
import androidx.core.content.edit
import androidx.core.content.getSystemService
import com.google.android.gms.tasks.await
import okio.ByteString.Companion.toByteString
import org.microg.gms.common.Constants
-import org.microg.gms.constellation.AuthManager
-import org.microg.gms.constellation.ConstellationStateStore
-import org.microg.gms.constellation.GServices
-import org.microg.gms.constellation.proto.ClientInfo
-import org.microg.gms.constellation.proto.CountryInfo
-import org.microg.gms.constellation.proto.DeviceID
-import org.microg.gms.constellation.proto.DeviceType
-import org.microg.gms.constellation.proto.DroidGuardSignals
-import org.microg.gms.constellation.proto.GaiaSignals
-import org.microg.gms.constellation.proto.GaiaToken
-import org.microg.gms.constellation.proto.NetworkSignal
-import org.microg.gms.constellation.proto.SimOperatorInfo
-import org.microg.gms.constellation.proto.UserProfileType
+import org.microg.gms.constellation.core.AuthManager
+import org.microg.gms.constellation.core.ConstellationStateStore
+import org.microg.gms.constellation.core.GServices
+import org.microg.gms.constellation.core.proto.ClientInfo
+import org.microg.gms.constellation.core.proto.CountryInfo
+import org.microg.gms.constellation.core.proto.DeviceID
+import org.microg.gms.constellation.core.proto.DeviceType
+import org.microg.gms.constellation.core.proto.DroidGuardSignals
+import org.microg.gms.constellation.core.proto.GaiaSignals
+import org.microg.gms.constellation.core.proto.GaiaToken
+import org.microg.gms.constellation.core.proto.NetworkSignal
+import org.microg.gms.constellation.core.proto.SimOperatorInfo
+import org.microg.gms.constellation.core.proto.UserProfileType
import java.util.Locale
private const val TAG = "ClientInfoBuilder"
private const val PREFS_NAME = "constellation_prefs"
-@RequiresPermission(Manifest.permission.GET_ACCOUNTS)
@SuppressLint("HardwareIds")
suspend operator fun ClientInfo.Companion.invoke(context: Context, iidToken: String): ClientInfo {
return ClientInfo(
@@ -43,7 +40,6 @@ suspend operator fun ClientInfo.Companion.invoke(context: Context, iidToken: Str
)
}
-@RequiresPermission(Manifest.permission.GET_ACCOUNTS)
@SuppressLint("HardwareIds")
suspend operator fun ClientInfo.Companion.invoke(
context: Context,
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/CommonBuilders.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
similarity index 69%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/CommonBuilders.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
index b05e0dc40c..d2f6f1e0da 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/CommonBuilders.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
@@ -1,19 +1,17 @@
-package org.microg.gms.constellation.proto.builders
+package org.microg.gms.constellation.core.proto.builders
-import android.Manifest
import android.content.Context
import android.os.Bundle
-import androidx.annotation.RequiresPermission
import okio.ByteString.Companion.toByteString
-import org.microg.gms.constellation.AuthManager
-import org.microg.gms.constellation.proto.AuditToken
-import org.microg.gms.constellation.proto.AuditTokenMetadata
-import org.microg.gms.constellation.proto.AuditUuid
-import org.microg.gms.constellation.proto.ClientInfo
-import org.microg.gms.constellation.proto.DeviceID
-import org.microg.gms.constellation.proto.Param
-import org.microg.gms.constellation.proto.RequestHeader
-import org.microg.gms.constellation.proto.RequestTrigger
+import org.microg.gms.constellation.core.AuthManager
+import org.microg.gms.constellation.core.proto.AuditToken
+import org.microg.gms.constellation.core.proto.AuditTokenMetadata
+import org.microg.gms.constellation.core.proto.AuditUuid
+import org.microg.gms.constellation.core.proto.ClientInfo
+import org.microg.gms.constellation.core.proto.DeviceID
+import org.microg.gms.constellation.core.proto.Param
+import org.microg.gms.constellation.core.proto.RequestHeader
+import org.microg.gms.constellation.core.proto.RequestTrigger
fun Param.Companion.getList(extras: Bundle?): List {
if (extras == null) return emptyList()
@@ -21,7 +19,7 @@ fun Param.Companion.getList(extras: Bundle?): List {
val ignoreKeys = setOf("consent_variant_key", "consent_trigger_key", "gaia_access_token")
for (key in extras.keySet()) {
if (key !in ignoreKeys) {
- extras.get(key)?.toString()?.let { value ->
+ extras.getString(key)?.let { value ->
params.add(Param(key = key, value_ = value))
}
}
@@ -41,7 +39,6 @@ fun AuditToken.Companion.generate(): AuditToken {
)
}
-@RequiresPermission(Manifest.permission.GET_ACCOUNTS)
suspend operator fun RequestHeader.Companion.invoke(
context: Context,
sessionId: String,
@@ -52,7 +49,7 @@ suspend operator fun RequestHeader.Companion.invoke(
val authManager = if (includeClientAuth) AuthManager.get(context) else null
val clientAuth = if (includeClientAuth) {
val (signature, timestamp) = authManager!!.signIidToken(buildContext.iidToken)
- org.microg.gms.constellation.proto.ClientAuth(
+ org.microg.gms.constellation.core.proto.ClientAuth(
device_id = DeviceID(context, buildContext.iidToken),
signature = signature.toByteString(),
sign_timestamp = timestamp
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/GaiaInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
similarity index 85%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/GaiaInfoBuilder.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
index dd2b34b42d..febce16e53 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/GaiaInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
@@ -1,24 +1,21 @@
-package org.microg.gms.constellation.proto.builders
+package org.microg.gms.constellation.core.proto.builders
-import android.Manifest
import android.accounts.AccountManager
import android.content.Context
import android.util.Log
-import androidx.annotation.RequiresPermission
import com.squareup.wire.Instant
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import org.microg.gms.constellation.proto.GaiaAccountSignalType
-import org.microg.gms.constellation.proto.GaiaSignalEntry
-import org.microg.gms.constellation.proto.GaiaSignals
-import org.microg.gms.constellation.proto.GaiaToken
+import org.microg.gms.constellation.core.proto.GaiaAccountSignalType
+import org.microg.gms.constellation.core.proto.GaiaSignalEntry
+import org.microg.gms.constellation.core.proto.GaiaSignals
+import org.microg.gms.constellation.core.proto.GaiaToken
import java.security.MessageDigest
import kotlin.math.abs
import kotlin.math.absoluteValue
private const val TAG = "GaiaInfoBuilder"
-@RequiresPermission(Manifest.permission.GET_ACCOUNTS)
operator fun GaiaSignals.Companion.invoke(context: Context): GaiaSignals? {
val entries = mutableListOf()
try {
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/RequestBuildContext.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt
similarity index 70%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/RequestBuildContext.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt
index 5ac41bba97..c581237c17 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/RequestBuildContext.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt
@@ -1,8 +1,8 @@
-package org.microg.gms.constellation.proto.builders
+package org.microg.gms.constellation.core.proto.builders
import android.content.Context
-import org.microg.gms.constellation.AuthManager
-import org.microg.gms.constellation.proto.GaiaToken
+import org.microg.gms.constellation.core.AuthManager
+import org.microg.gms.constellation.core.proto.GaiaToken
data class RequestBuildContext(
val iidToken: String,
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/SyncRequestBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
similarity index 85%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/SyncRequestBuilder.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
index 7d86e3964f..61bc46d639 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/SyncRequestBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.proto.builders
+package org.microg.gms.constellation.core.proto.builders
import android.annotation.SuppressLint
import android.content.Context
@@ -10,22 +10,23 @@ import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import com.google.android.gms.constellation.VerifyPhoneNumberRequest
-import org.microg.gms.constellation.ConstellationStateStore
-import org.microg.gms.constellation.proto.ChallengePreference
-import org.microg.gms.constellation.proto.ChallengePreferenceMetadata
-import org.microg.gms.constellation.proto.IdTokenRequest
-import org.microg.gms.constellation.proto.Param
-import org.microg.gms.constellation.proto.RequestHeader
-import org.microg.gms.constellation.proto.RequestTrigger
-import org.microg.gms.constellation.proto.SIMAssociation
-import org.microg.gms.constellation.proto.SIMSlotInfo
-import org.microg.gms.constellation.proto.SyncRequest
-import org.microg.gms.constellation.proto.TelephonyInfo
-import org.microg.gms.constellation.proto.TelephonyPhoneNumberType
-import org.microg.gms.constellation.proto.Verification
-import org.microg.gms.constellation.proto.VerificationAssociation
-import org.microg.gms.constellation.proto.VerificationParam
-import org.microg.gms.constellation.proto.VerificationPolicy
+import com.google.android.gms.constellation.verificationMethods
+import org.microg.gms.constellation.core.ConstellationStateStore
+import org.microg.gms.constellation.core.proto.ChallengePreference
+import org.microg.gms.constellation.core.proto.ChallengePreferenceMetadata
+import org.microg.gms.constellation.core.proto.IdTokenRequest
+import org.microg.gms.constellation.core.proto.Param
+import org.microg.gms.constellation.core.proto.RequestHeader
+import org.microg.gms.constellation.core.proto.RequestTrigger
+import org.microg.gms.constellation.core.proto.SIMAssociation
+import org.microg.gms.constellation.core.proto.SIMSlotInfo
+import org.microg.gms.constellation.core.proto.SyncRequest
+import org.microg.gms.constellation.core.proto.TelephonyInfo
+import org.microg.gms.constellation.core.proto.TelephonyPhoneNumberType
+import org.microg.gms.constellation.core.proto.Verification
+import org.microg.gms.constellation.core.proto.VerificationAssociation
+import org.microg.gms.constellation.core.proto.VerificationParam
+import org.microg.gms.constellation.core.proto.VerificationPolicy
private const val TAG = "SyncRequestBuilder"
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/TelephonyInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt
similarity index 94%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/TelephonyInfoBuilder.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt
index 62e4ada9b7..4bef0d7581 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/proto/builders/TelephonyInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.proto.builders
+package org.microg.gms.constellation.core.proto.builders
import android.annotation.SuppressLint
import android.content.Context
@@ -9,10 +9,10 @@ import android.telephony.TelephonyManager
import android.util.Base64
import android.util.Log
import androidx.core.content.getSystemService
-import org.microg.gms.constellation.proto.NetworkSignal
-import org.microg.gms.constellation.proto.SimNetworkInfo
-import org.microg.gms.constellation.proto.SimOperatorInfo
-import org.microg.gms.constellation.proto.TelephonyInfo
+import org.microg.gms.constellation.core.proto.NetworkSignal
+import org.microg.gms.constellation.core.proto.SimNetworkInfo
+import org.microg.gms.constellation.core.proto.SimOperatorInfo
+import org.microg.gms.constellation.core.proto.TelephonyInfo
import java.security.MessageDigest
private const val TAG = "TelephonyInfoBuilder"
@@ -107,6 +107,7 @@ fun NetworkSignal.Companion.getList(context: Context): List {
return connectivityInfos
}
+@SuppressLint("HardwareIds")
fun SimOperatorInfo.Companion.getList(context: Context): List {
val infos = mutableListOf()
try {
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/CarrierIdVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt
similarity index 90%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/CarrierIdVerifier.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt
index b7c138638f..f7159238c1 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/CarrierIdVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt
@@ -1,14 +1,14 @@
-package org.microg.gms.constellation.verification
+package org.microg.gms.constellation.core.verification
import android.content.Context
import android.os.Build
import android.telephony.TelephonyManager
import android.util.Log
import androidx.core.content.getSystemService
-import org.microg.gms.constellation.proto.CarrierIdChallengeResponse
-import org.microg.gms.constellation.proto.CarrierIdError
-import org.microg.gms.constellation.proto.Challenge
-import org.microg.gms.constellation.proto.ChallengeResponse
+import org.microg.gms.constellation.core.proto.CarrierIdChallengeResponse
+import org.microg.gms.constellation.core.proto.CarrierIdError
+import org.microg.gms.constellation.core.proto.Challenge
+import org.microg.gms.constellation.core.proto.ChallengeResponse
private const val TAG = "CarrierIdVerifier"
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ChallengeProcessor.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
similarity index 88%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ChallengeProcessor.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
index e731f5112d..a5775cdedb 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ChallengeProcessor.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
@@ -1,21 +1,21 @@
-package org.microg.gms.constellation.verification
+package org.microg.gms.constellation.core.verification
import android.content.Context
import android.telephony.SubscriptionInfo
import android.util.Log
import com.squareup.wire.GrpcException
import com.squareup.wire.GrpcStatus
-import org.microg.gms.constellation.ConstellationStateStore
-import org.microg.gms.constellation.RpcClient
-import org.microg.gms.constellation.getState
-import org.microg.gms.constellation.proto.ChallengeResponse
-import org.microg.gms.constellation.proto.ProceedRequest
-import org.microg.gms.constellation.proto.RequestHeader
-import org.microg.gms.constellation.proto.RequestTrigger
-import org.microg.gms.constellation.proto.Verification
-import org.microg.gms.constellation.proto.VerificationMethod
-import org.microg.gms.constellation.proto.builders.RequestBuildContext
-import org.microg.gms.constellation.proto.builders.invoke
+import org.microg.gms.constellation.core.ConstellationStateStore
+import org.microg.gms.constellation.core.RpcClient
+import org.microg.gms.constellation.core.getState
+import org.microg.gms.constellation.core.proto.ChallengeResponse
+import org.microg.gms.constellation.core.proto.ProceedRequest
+import org.microg.gms.constellation.core.proto.RequestHeader
+import org.microg.gms.constellation.core.proto.RequestTrigger
+import org.microg.gms.constellation.core.proto.Verification
+import org.microg.gms.constellation.core.proto.VerificationMethod
+import org.microg.gms.constellation.core.proto.builders.RequestBuildContext
+import org.microg.gms.constellation.core.proto.builders.invoke
object ChallengeProcessor {
private const val TAG = "ChallengeProcessor"
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MoSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
similarity index 95%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MoSmsVerifier.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
index f9a6f6bc4a..a5c914b595 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MoSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.verification
+package org.microg.gms.constellation.core.verification
import android.Manifest
import android.app.PendingIntent
@@ -11,18 +11,17 @@ import android.os.Build
import android.telephony.SmsManager
import android.telephony.SubscriptionManager
import android.util.Log
-import androidx.annotation.RequiresPermission
import androidx.core.content.getSystemService
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
-import org.microg.gms.constellation.proto.ChallengeResponse
-import org.microg.gms.constellation.proto.MoChallenge
-import org.microg.gms.constellation.proto.MOChallengeResponseData
+import org.microg.gms.constellation.core.proto.ChallengeResponse
+import org.microg.gms.constellation.core.proto.MOChallengeResponseData
+import org.microg.gms.constellation.core.proto.MoChallenge
import java.util.UUID
import kotlin.coroutines.resume
private const val TAG = "MoSmsVerifier"
-private const val ACTION_MO_SMS_SENT = "org.microg.gms.constellation.MO_SMS_SENT"
+private const val ACTION_MO_SMS_SENT = "org.microg.gms.constellation.coreMO_SMS_SENT"
class MoSmsVerifier(private val context: Context, private val subId: Int) {
suspend fun verify(challenge: MoChallenge?): ChallengeResponse? {
@@ -167,7 +166,6 @@ class MoSmsVerifier(private val context: Context, private val subId: Int) {
)
}
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
private fun isActiveSubscription(subscriptionId: Int): Boolean {
if (subscriptionId == -1) return false
return try {
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MtSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt
similarity index 94%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MtSmsVerifier.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt
index e757d60a07..5d5cc838f7 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/MtSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.verification
+package org.microg.gms.constellation.core.verification
import android.content.BroadcastReceiver
import android.content.Context
@@ -9,9 +9,9 @@ import android.provider.Telephony
import android.util.Log
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
-import org.microg.gms.constellation.proto.ChallengeResponse
-import org.microg.gms.constellation.proto.MTChallenge
-import org.microg.gms.constellation.proto.MTChallengeResponseData
+import org.microg.gms.constellation.core.proto.ChallengeResponse
+import org.microg.gms.constellation.core.proto.MTChallenge
+import org.microg.gms.constellation.core.proto.MTChallengeResponseData
import kotlin.coroutines.resume
private const val TAG = "MtSmsVerifier"
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/RegisteredSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
similarity index 92%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/RegisteredSmsVerifier.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
index 301527f0d7..3e1ea8d37e 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/RegisteredSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.verification
+package org.microg.gms.constellation.core.verification
import android.content.Context
import android.provider.Telephony
@@ -7,12 +7,12 @@ import android.telephony.TelephonyManager
import android.util.Log
import androidx.core.content.getSystemService
import okio.ByteString.Companion.toByteString
-import org.microg.gms.constellation.VerificationSettingsPhenotypes
-import org.microg.gms.constellation.proto.ChallengeResponse
-import org.microg.gms.constellation.proto.RegisteredSmsChallenge
-import org.microg.gms.constellation.proto.RegisteredSmsChallengeResponse
-import org.microg.gms.constellation.proto.RegisteredSmsChallengeResponseItem
-import org.microg.gms.constellation.proto.RegisteredSmsChallengeResponsePayload
+import org.microg.gms.constellation.core.VerificationSettingsPhenotypes
+import org.microg.gms.constellation.core.proto.ChallengeResponse
+import org.microg.gms.constellation.core.proto.RegisteredSmsChallenge
+import org.microg.gms.constellation.core.proto.RegisteredSmsChallengeResponse
+import org.microg.gms.constellation.core.proto.RegisteredSmsChallengeResponseItem
+import org.microg.gms.constellation.core.proto.RegisteredSmsChallengeResponsePayload
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.concurrent.TimeUnit
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/Ts43Verifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt
similarity index 94%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/Ts43Verifier.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt
index 4030817bcc..4066a5dffb 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/Ts43Verifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.verification
+package org.microg.gms.constellation.core.verification
import android.content.Context
import android.os.Build
@@ -13,25 +13,24 @@ import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
-import org.microg.gms.constellation.proto.ChallengeResponse
-import org.microg.gms.constellation.proto.ClientChallengeResponse
-import org.microg.gms.constellation.proto.OdsaOperation
-import org.microg.gms.constellation.proto.ServerChallengeResponse
-import org.microg.gms.constellation.proto.ServiceEntitlementRequest
-import org.microg.gms.constellation.proto.Ts43Challenge
-import org.microg.gms.constellation.proto.Ts43ChallengeResponse
-import org.microg.gms.constellation.proto.Ts43ChallengeResponseError
-import org.microg.gms.constellation.proto.Ts43ChallengeResponseStatus
-import org.microg.gms.constellation.verification.ts43.EapAkaService
-import org.microg.gms.constellation.verification.ts43.builder
-import org.microg.gms.constellation.verification.ts43.userAgent
+import org.microg.gms.constellation.core.proto.ChallengeResponse
+import org.microg.gms.constellation.core.proto.ClientChallengeResponse
+import org.microg.gms.constellation.core.proto.OdsaOperation
+import org.microg.gms.constellation.core.proto.ServerChallengeResponse
+import org.microg.gms.constellation.core.proto.ServiceEntitlementRequest
+import org.microg.gms.constellation.core.proto.Ts43Challenge
+import org.microg.gms.constellation.core.proto.Ts43ChallengeResponse
+import org.microg.gms.constellation.core.proto.Ts43ChallengeResponseError
+import org.microg.gms.constellation.core.proto.Ts43ChallengeResponseStatus
+import org.microg.gms.constellation.core.verification.ts43.EapAkaService
+import org.microg.gms.constellation.core.verification.ts43.builder
+import org.microg.gms.constellation.core.verification.ts43.userAgent
import org.xml.sax.InputSource
import java.io.IOException
import java.io.StringReader
import java.util.Locale
import java.util.concurrent.TimeUnit
import javax.xml.parsers.DocumentBuilderFactory
-import kotlin.text.ifEmpty
private const val TAG = "Ts43Verifier"
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/EapAkaService.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/EapAkaService.kt
similarity index 99%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/EapAkaService.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/EapAkaService.kt
index 9e271e5a4f..47fae2cb31 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/EapAkaService.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/EapAkaService.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.verification.ts43
+package org.microg.gms.constellation.core.verification.ts43
import android.os.Build
import android.telephony.TelephonyManager
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/Fips186Prf.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/Fips186Prf.kt
similarity index 98%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/Fips186Prf.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/Fips186Prf.kt
index 0f2b138ebe..618cb719cb 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/Fips186Prf.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/Fips186Prf.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.verification.ts43
+package org.microg.gms.constellation.core.verification.ts43
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
diff --git a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/ServiceEntitlementExtension.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
similarity index 97%
rename from play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/ServiceEntitlementExtension.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
index 71f9e4c87b..69e7e846cb 100644
--- a/play-services-constellation/src/main/kotlin/org/microg/gms/constellation/verification/ts43/ServiceEntitlementExtension.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
@@ -1,11 +1,11 @@
-package org.microg.gms.constellation.verification.ts43
+package org.microg.gms.constellation.core.verification.ts43
import android.content.Context
import android.telephony.TelephonyManager
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
-import org.microg.gms.constellation.proto.OdsaOperation
-import org.microg.gms.constellation.proto.ServiceEntitlementRequest
+import org.microg.gms.constellation.core.proto.OdsaOperation
+import org.microg.gms.constellation.core.proto.ServiceEntitlementRequest
fun ServiceEntitlementRequest.builder(
telephonyManager: TelephonyManager,
diff --git a/play-services-constellation/src/main/proto/constellation.proto b/play-services-constellation/core/src/main/proto/constellation.proto
similarity index 99%
rename from play-services-constellation/src/main/proto/constellation.proto
rename to play-services-constellation/core/src/main/proto/constellation.proto
index 29a8bd027a..93a10c652c 100644
--- a/play-services-constellation/src/main/proto/constellation.proto
+++ b/play-services-constellation/core/src/main/proto/constellation.proto
@@ -1,5 +1,5 @@
syntax = "proto3";
-option java_package = "org.microg.gms.constellation.proto";
+option java_package = "org.microg.gms.constellation.core.proto";
package google.internal.communications.phonedeviceverification.v1;
diff --git a/play-services-constellation/src/main/AndroidManifest.xml b/play-services-constellation/src/main/AndroidManifest.xml
index bf92ddcaf3..670e962e03 100644
--- a/play-services-constellation/src/main/AndroidManifest.xml
+++ b/play-services-constellation/src/main/AndroidManifest.xml
@@ -1,17 +1,6 @@
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
index 4598d98bb1..fe80003950 100644
--- a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
+++ b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
@@ -52,36 +52,12 @@ data class SimCapability @Constructor constructor(
@SafeParcelable.Class
data class VerificationCapability @Constructor constructor(
@JvmField @Param(1) @Field(1) val verificationMethod: Int,
- @JvmField @Param(2) @Field(2) val statusValue: Int // Enums are typically passed as Ints in SafeParcelable
+ @JvmField @Param(2) @Field(2) val statusValue: Int
) : AbstractSafeParcelable() {
- constructor(verificationMethod: Int, status: VerificationStatus) : this(
- verificationMethod,
- status.value
- )
-
- val status: VerificationStatus
- get() = VerificationStatus.fromInt(statusValue)
-
override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
companion object {
@JvmField
val CREATOR = findCreator(VerificationCapability::class.java)
}
-}
-
-enum class VerificationStatus(val value: Int) {
- SUPPORTED(1),
- UNSUPPORTED_CARRIER(2),
- UNSUPPORTED_API_VERSION(3),
- UNSUPPORTED_SIM_NOT_READY(4);
-
- companion object {
- fun fromInt(value: Int): VerificationStatus = when (value) {
- 2 -> UNSUPPORTED_CARRIER
- 3 -> UNSUPPORTED_API_VERSION
- 4 -> UNSUPPORTED_SIM_NOT_READY
- else -> SUPPORTED
- }
- }
-}
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt
index 459a05b3cd..f16110f7f9 100644
--- a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt
+++ b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt
@@ -7,7 +7,6 @@ import com.google.android.gms.common.internal.safeparcel.SafeParcelable
import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Constructor
import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
-import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter
@SafeParcelable.Class
data class PhoneNumberInfo @Constructor constructor(
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
index e2f159a44c..c2a0ef8115 100644
--- a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
+++ b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
@@ -8,7 +8,6 @@ import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Construc
import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter
-import org.microg.gms.constellation.proto.VerificationMethod
@SafeParcelable.Class
data class VerifyPhoneNumberRequest @Constructor constructor(
@@ -19,11 +18,8 @@ data class VerifyPhoneNumberRequest @Constructor constructor(
@JvmField @Param(5) @Field(5) val targetedSims: List,
@JvmField @Param(6) @Field(6) val silent: Boolean,
@JvmField @Param(7) @Field(7) val apiVersion: Int,
- @JvmField @Param(8) @Field(8) val verificationTypes: List
+ @JvmField @Param(8) @Field(8) val verificationMethodsValues: List
) : AbstractSafeParcelable() {
- val verificationMethods: List
- get() = verificationTypes.mapNotNull { VerificationMethod.fromValue(it) }
-
override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
companion object {
diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle
index 47c2dc6553..362e39c76a 100644
--- a/play-services-core/build.gradle
+++ b/play-services-core/build.gradle
@@ -32,6 +32,7 @@ dependencies {
implementation project(':play-services-cast-core')
implementation project(':play-services-cast-framework-core')
implementation project(':play-services-conscrypt-provider-core')
+ implementation project(':play-services-constellation-core')
implementation project(':play-services-cronet-core')
implementation project(':play-services-droidguard-core')
implementation project(':play-services-fido-core')
@@ -59,7 +60,6 @@ dependencies {
implementation project(':play-services-auth-base')
implementation project(':play-services-auth')
implementation project(':play-services-clearcut')
- implementation project(':play-services-constellation')
implementation project(':play-services-drive')
implementation project(':play-services-games')
implementation project(':play-services-maps')
diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml
index 2d9e8447d6..a29fff819c 100644
--- a/play-services-core/src/main/AndroidManifest.xml
+++ b/play-services-core/src/main/AndroidManifest.xml
@@ -179,9 +179,6 @@
android:name="android.hardware.camera"
android:required="false" />
-
-
-
-
-
-
-
-
diff --git a/settings.gradle b/settings.gradle
index bc68c6a7b1..9fcbcea289 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -93,6 +93,7 @@ sublude ':play-services-cast:core'
sublude ':play-services-cast-framework:core'
include ':play-services-chimera-core'
include ':play-services-conscrypt-provider-core'
+sublude ':play-services-constellation:core'
sublude ':play-services-cronet:core'
sublude ':play-services-droidguard:core'
sublude ':play-services-fido:core'
From 08b36eba647b0c704aca1975251815efada997a5 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Thu, 26 Mar 2026 01:10:07 -0400
Subject: [PATCH 03/31] Refactor
`org.microg.gms.constellation.core.verification`
---
.../core/verification/CarrierIdVerifier.kt | 119 ++++----
.../core/verification/ChallengeProcessor.kt | 38 +--
.../core/verification/MoSmsVerifier.kt | 287 +++++++++---------
.../core/verification/MtSmsVerifier.kt | 118 ++++---
.../verification/RegisteredSmsVerifier.kt | 243 ++++++++-------
.../core/verification/Ts43Verifier.kt | 170 +++++------
6 files changed, 468 insertions(+), 507 deletions(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt
index f7159238c1..53c1bcc72d 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt
@@ -12,73 +12,74 @@ import org.microg.gms.constellation.core.proto.ChallengeResponse
private const val TAG = "CarrierIdVerifier"
-class CarrierIdVerifier(private val context: Context, private val subId: Int) {
- fun verify(challenge: Challenge?): ChallengeResponse? {
- val carrierChallenge = challenge?.carrier_id_challenge ?: return null
- val challengeData = carrierChallenge.isim_request.takeIf { it.isNotEmpty() }
- ?: return failure(
- CarrierIdError.CARRIER_ID_ERROR_UNKNOWN_ERROR,
- "Carrier challenge data missing"
- )
- if (subId == -1) return failure(
- CarrierIdError.CARRIER_ID_ERROR_NO_SIM,
- "No active subscription for carrier auth"
+fun Challenge.verifyCarrierId(context: Context, subId: Int): ChallengeResponse {
+ val carrierChallenge = carrier_id_challenge ?: return failure(
+ CarrierIdError.CARRIER_ID_ERROR_UNKNOWN_ERROR,
+ "Carrier challenge data missing"
+ )
+ val challengeData = carrierChallenge.isim_request.takeIf { it.isNotEmpty() }
+ ?: return failure(
+ CarrierIdError.CARRIER_ID_ERROR_UNKNOWN_ERROR,
+ "Carrier challenge data missing"
)
+ if (subId == -1) return failure(
+ CarrierIdError.CARRIER_ID_ERROR_NO_SIM,
+ "No active subscription for carrier auth"
+ )
- val appType = carrierChallenge.app_type
- val authType = carrierChallenge.auth_type
-
- return try {
- val telephonyManager = context.getSystemService()
- ?: return failure(
- CarrierIdError.CARRIER_ID_ERROR_NOT_SUPPORTED,
- "TelephonyManager unavailable"
- )
- val targetManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- telephonyManager.createForSubscriptionId(subId)
- } else {
- telephonyManager
- }
+ val appType = carrierChallenge.app_type
+ val authType = carrierChallenge.auth_type
- val response = targetManager.getIccAuthentication(appType, authType, challengeData)
- if (response.isNullOrEmpty()) {
- failure(CarrierIdError.CARRIER_ID_ERROR_NULL_RESPONSE, "Null ISIM response")
- } else {
- ChallengeResponse(
- carrier_id_response = CarrierIdChallengeResponse(
- isim_response = response,
- carrier_id_error = CarrierIdError.CARRIER_ID_ERROR_NO_ERROR
- )
- )
- }
- } catch (e: SecurityException) {
- Log.w(TAG, "Unable to read subscription for carrier auth", e)
- failure(
- CarrierIdError.CARRIER_ID_ERROR_UNABLE_TO_READ_SUBSCRIPTION,
- e.message ?: "SecurityException"
- )
- } catch (e: UnsupportedOperationException) {
- Log.w(TAG, "Carrier auth API unavailable", e)
- failure(
+ return try {
+ val telephonyManager = context.getSystemService()
+ ?: return failure(
CarrierIdError.CARRIER_ID_ERROR_NOT_SUPPORTED,
- e.message ?: "UnsupportedOperationException"
- )
- } catch (e: Exception) {
- Log.e(TAG, "Carrier auth failed", e)
- failure(
- CarrierIdError.CARRIER_ID_ERROR_REFLECTION_ERROR,
- e.message ?: "Reflection or platform error"
+ "TelephonyManager unavailable"
)
+ val targetManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ telephonyManager.createForSubscriptionId(subId)
+ } else {
+ telephonyManager
}
- }
- private fun failure(status: CarrierIdError, message: String): ChallengeResponse {
- Log.w(TAG, message)
- return ChallengeResponse(
- carrier_id_response = CarrierIdChallengeResponse(
- isim_response = "",
- carrier_id_error = status
+ val response = targetManager.getIccAuthentication(appType, authType, challengeData)
+ if (response.isNullOrEmpty()) {
+ failure(CarrierIdError.CARRIER_ID_ERROR_NULL_RESPONSE, "Null ISIM response")
+ } else {
+ ChallengeResponse(
+ carrier_id_response = CarrierIdChallengeResponse(
+ isim_response = response,
+ carrier_id_error = CarrierIdError.CARRIER_ID_ERROR_NO_ERROR
+ )
)
+ }
+ } catch (e: SecurityException) {
+ Log.w(TAG, "Unable to read subscription for carrier auth", e)
+ failure(
+ CarrierIdError.CARRIER_ID_ERROR_UNABLE_TO_READ_SUBSCRIPTION,
+ e.message ?: "SecurityException"
+ )
+ } catch (e: UnsupportedOperationException) {
+ Log.w(TAG, "Carrier auth API unavailable", e)
+ failure(
+ CarrierIdError.CARRIER_ID_ERROR_NOT_SUPPORTED,
+ e.message ?: "UnsupportedOperationException"
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "Carrier auth failed", e)
+ failure(
+ CarrierIdError.CARRIER_ID_ERROR_REFLECTION_ERROR,
+ e.message ?: "Reflection or platform error"
)
}
}
+
+private fun failure(status: CarrierIdError, message: String): ChallengeResponse {
+ Log.w(TAG, message)
+ return ChallengeResponse(
+ carrier_id_response = CarrierIdChallengeResponse(
+ isim_response = "",
+ carrier_id_error = status
+ )
+ )
+}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
index a5775cdedb..ac79655766 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
@@ -70,35 +70,25 @@ object ChallengeProcessor {
val info = imsiToInfoMap[challengeImsi]
val subId = info?.subscriptionId ?: -1
- val challengeResponse: ChallengeResponse? = when {
- challenge.type == VerificationMethod.TS43 -> Ts43Verifier(
- context,
- subId
- ).verify(challenge.ts43_challenge)
-
- challenge.type == VerificationMethod.CARRIER_ID && challenge.ts43_challenge != null ->
- Ts43Verifier(context, subId).verify(challenge.ts43_challenge)
+ val challengeResponse: ChallengeResponse? = when (challenge.type) {
+ VerificationMethod.TS43 -> challenge.ts43_challenge?.verify(context, subId)
+
+ VerificationMethod.CARRIER_ID -> {
+ if (challenge.ts43_challenge != null) {
+ challenge.ts43_challenge.verify(context, subId)
+ } else {
+ challenge.verifyCarrierId(context, subId)
+ }
+ }
- challenge.type == VerificationMethod.CARRIER_ID ->
- CarrierIdVerifier(context, subId).verify(challenge)
+ VerificationMethod.MT_SMS -> challenge.mt_challenge?.verify(context, subId)
- challenge.type == VerificationMethod.MT_SMS -> MtSmsVerifier(
- context,
- subId
- ).verify(challenge.mt_challenge)
+ VerificationMethod.MO_SMS -> challenge.mo_challenge?.verify(context, subId)
- challenge.type == VerificationMethod.MO_SMS -> MoSmsVerifier(
+ VerificationMethod.REGISTERED_SMS -> challenge.registered_sms_challenge?.verify(
context,
subId
- ).verify(challenge.mo_challenge)
-
- challenge.type == VerificationMethod.REGISTERED_SMS ->
- RegisteredSmsVerifier(context, subId).verify(challenge.registered_sms_challenge)
-
- challenge.type == VerificationMethod.FLASH_CALL -> {
- Log.w(TAG, "Flash call verification is unavailable on this build")
- null
- }
+ )
else -> {
Log.w(TAG, "Unsupported verification method: ${challenge.type}")
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
index a5c914b595..19fd2d0305 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
@@ -23,186 +23,177 @@ import kotlin.coroutines.resume
private const val TAG = "MoSmsVerifier"
private const val ACTION_MO_SMS_SENT = "org.microg.gms.constellation.coreMO_SMS_SENT"
-class MoSmsVerifier(private val context: Context, private val subId: Int) {
- suspend fun verify(challenge: MoChallenge?): ChallengeResponse? {
- if (challenge == null) return null
- if (challenge.proxy_number.isEmpty() || challenge.sms.isEmpty()) {
- return failureResponse(MOChallengeResponseData.Status.FAILED_TO_SEND_MO)
- }
- if (context.checkCallingOrSelfPermission(Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
- Log.w(TAG, "SEND_SMS permission missing")
- return failureResponse(MOChallengeResponseData.Status.FAILED_TO_SEND_MO)
- }
+suspend fun MoChallenge.verify(context: Context, subId: Int): ChallengeResponse {
+ if (proxy_number.isEmpty() || sms.isEmpty()) {
+ return ChallengeResponse(mo_response = MOChallengeResponseData(status = MOChallengeResponseData.Status.FAILED_TO_SEND_MO))
+ }
+ if (context.checkCallingOrSelfPermission(Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "SEND_SMS permission missing")
+ return ChallengeResponse(mo_response = MOChallengeResponseData(status = MOChallengeResponseData.Status.FAILED_TO_SEND_MO))
+ }
- val smsManager = resolveSmsManager() ?: return failureResponse(
- if (subId != -1 && !isActiveSubscription(subId)) {
+ val smsManager = resolveSmsManager(context, subId) ?: return ChallengeResponse(
+ mo_response = MOChallengeResponseData(
+ status = if (subId != -1 && !isActiveSubscription(context, subId)) {
MOChallengeResponseData.Status.NO_ACTIVE_SUBSCRIPTION
} else {
MOChallengeResponseData.Status.NO_SMS_MANAGER
}
)
+ )
+
+ val port = data_sms_info?.destination_port ?: 0
+ val isBinarySms = port > 0
+ val action = ACTION_MO_SMS_SENT
+ val messageId = UUID.randomUUID().toString()
+ val sentIntent = Intent(action).apply {
+ `package` = context.packageName
+ putExtra("message_id", messageId)
+ }
- val port = challenge.data_sms_info?.destination_port ?: 0
- val isBinarySms = port > 0
- val action = if (isBinarySms) ACTION_MO_SMS_SENT else ACTION_MO_SMS_SENT
- val messageId = UUID.randomUUID().toString()
- val sentIntent = Intent(action).apply {
- `package` = context.packageName
- putExtra("message_id", messageId)
- }
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ messageId.hashCode(),
+ sentIntent,
+ PendingIntent.FLAG_ONE_SHOT or (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0)
+ )
- val pendingIntent = PendingIntent.getBroadcast(
- context,
- messageId.hashCode(),
- sentIntent,
- PendingIntent.FLAG_ONE_SHOT or (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0)
- )
+ Log.d(TAG, "Sending MO SMS to $proxy_number with messageId: $messageId")
+
+ return withTimeoutOrNull(30000) {
+ suspendCancellableCoroutine { continuation ->
+ val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == action) {
+ val receivedId = intent.getStringExtra("message_id")
+ if (receivedId != messageId) return
+
+ val resultCode = resultCode
+ val errorCode = intent.getIntExtra("errorCode", -1)
+
+ Log.d(TAG, "MO SMS sent result: $resultCode, error: $errorCode")
+
+ val status = when (resultCode) {
+ -1 -> MOChallengeResponseData.Status.COMPLETED
+ else -> MOChallengeResponseData.Status.FAILED_TO_SEND_MO
+ }
- Log.d(TAG, "Sending MO SMS to ${challenge.proxy_number} with messageId: $messageId")
-
- return withTimeoutOrNull(30000) {
- suspendCancellableCoroutine { continuation ->
- val receiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action == action) {
- val receivedId = intent.getStringExtra("message_id")
- if (receivedId != messageId) return
-
- val resultCode = resultCode
- val errorCode = intent.getIntExtra("errorCode", -1)
-
- Log.d(TAG, "MO SMS sent result: $resultCode, error: $errorCode")
-
- val status = when (resultCode) {
- -1 -> MOChallengeResponseData.Status.COMPLETED
- else -> MOChallengeResponseData.Status.FAILED_TO_SEND_MO
- }
-
- try {
- context.unregisterReceiver(this)
- } catch (_: Exception) {
- }
-
- if (continuation.isActive) {
- continuation.resume(
- ChallengeResponse(
- mo_response = MOChallengeResponseData(
- status = status,
- sms_result_code = resultCode.toLong(),
- sms_error_code = errorCode.toLong()
- )
+ try {
+ context.unregisterReceiver(this)
+ } catch (_: Exception) {
+ }
+
+ if (continuation.isActive) {
+ continuation.resume(
+ ChallengeResponse(
+ mo_response = MOChallengeResponseData(
+ status = status,
+ sms_result_code = resultCode.toLong(),
+ sms_error_code = errorCode.toLong()
)
)
- }
+ )
}
}
}
+ }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- context.registerReceiver(
- receiver,
- IntentFilter(action),
- Context.RECEIVER_NOT_EXPORTED
- )
- } else {
- context.registerReceiver(receiver, IntentFilter(action))
- }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ context.registerReceiver(
+ receiver,
+ IntentFilter(action),
+ Context.RECEIVER_NOT_EXPORTED
+ )
+ } else {
+ context.registerReceiver(receiver, IntentFilter(action))
+ }
- continuation.invokeOnCancellation {
- try {
- context.unregisterReceiver(receiver)
- } catch (_: Exception) {
- }
+ continuation.invokeOnCancellation {
+ try {
+ context.unregisterReceiver(receiver)
+ } catch (_: Exception) {
}
+ }
+ try {
+ if (isBinarySms) {
+ smsManager.sendDataMessage(
+ proxy_number,
+ null,
+ port.toShort(),
+ sms.encodeToByteArray(),
+ pendingIntent,
+ null
+ )
+ } else {
+ smsManager.sendTextMessage(
+ proxy_number,
+ null,
+ sms,
+ pendingIntent,
+ null
+ )
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to initiate MO SMS send", e)
try {
- if (isBinarySms) {
- smsManager.sendDataMessage(
- challenge.proxy_number,
- null,
- port.toShort(),
- challenge.sms.encodeToByteArray(),
- pendingIntent,
- null
- )
- } else {
- smsManager.sendTextMessage(
- challenge.proxy_number,
- null,
- challenge.sms,
- pendingIntent,
- null
- )
- }
- } catch (e: Exception) {
- Log.e(TAG, "Failed to initiate MO SMS send", e)
- try {
- context.unregisterReceiver(receiver)
- } catch (_: Exception) {
- }
- if (continuation.isActive) {
- continuation.resume(
- ChallengeResponse(
- mo_response = MOChallengeResponseData(
- status = MOChallengeResponseData.Status.FAILED_TO_SEND_MO
- )
+ context.unregisterReceiver(receiver)
+ } catch (_: Exception) {
+ }
+ if (continuation.isActive) {
+ continuation.resume(
+ ChallengeResponse(
+ mo_response = MOChallengeResponseData(
+ status = MOChallengeResponseData.Status.FAILED_TO_SEND_MO
)
)
- }
+ )
}
}
- } ?: ChallengeResponse(
- mo_response = MOChallengeResponseData(
- status = MOChallengeResponseData.Status.FAILED_TO_SEND_MO
- )
+ }
+ } ?: ChallengeResponse(
+ mo_response = MOChallengeResponseData(
+ status = MOChallengeResponseData.Status.FAILED_TO_SEND_MO
)
- }
+ )
+}
- private fun failureResponse(status: MOChallengeResponseData.Status): ChallengeResponse {
- return ChallengeResponse(
- mo_response = MOChallengeResponseData(
- status = status
- )
- )
+private fun isActiveSubscription(context: Context, subscriptionId: Int): Boolean {
+ if (subscriptionId == -1) return false
+ return try {
+ context.getSystemService()?.isActiveSubscriptionId(subscriptionId)
+ ?: false
+ } catch (e: Exception) {
+ Log.w(TAG, "Failed to query active subscription for $subscriptionId", e)
+ false
}
+}
- private fun isActiveSubscription(subscriptionId: Int): Boolean {
- if (subscriptionId == -1) return false
- return try {
- context.getSystemService()?.isActiveSubscriptionId(subscriptionId)
- ?: false
- } catch (e: Exception) {
- Log.w(TAG, "Failed to query active subscription for $subscriptionId", e)
- false
- }
+private fun resolveSmsManager(context: Context, subId: Int): SmsManager? {
+ if (subId != -1 && !isActiveSubscription(context, subId)) {
+ return null
}
-
- private fun resolveSmsManager(): SmsManager? {
- if (subId != -1 && !isActiveSubscription(subId)) {
- return null
+ return try {
+ val manager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ context.getSystemService(SmsManager::class.java)
+ } else {
+ null
}
- return try {
- val manager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- context.getSystemService(SmsManager::class.java)
- } else {
- null
+ when {
+ subId != -1 && manager != null -> manager.createForSubscriptionId(subId)
+ manager != null -> manager
+ subId != -1 -> {
+ @Suppress("DEPRECATION")
+ SmsManager.getSmsManagerForSubscriptionId(subId)
}
- when {
- subId != -1 && manager != null -> manager.createForSubscriptionId(subId)
- manager != null -> manager
- subId != -1 -> {
- @Suppress("DEPRECATION")
- SmsManager.getSmsManagerForSubscriptionId(subId)
- }
- else -> {
- @Suppress("DEPRECATION")
- SmsManager.getDefault()
- }
+ else -> {
+ @Suppress("DEPRECATION")
+ SmsManager.getDefault()
}
- } catch (e: Exception) {
- Log.e(TAG, "Failed to resolve SmsManager for subId: $subId", e)
- null
}
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to resolve SmsManager for subId: $subId", e)
+ null
}
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt
index 5d5cc838f7..4ad7e84e6a 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt
@@ -16,80 +16,78 @@ import kotlin.coroutines.resume
private const val TAG = "MtSmsVerifier"
-class MtSmsVerifier(private val context: Context, private val subId: Int) {
- suspend fun verify(challenge: MTChallenge?): ChallengeResponse? {
- val expectedBody = challenge?.sms?.takeIf { it.isNotEmpty() } ?: return null
+suspend fun MTChallenge.verify(context: Context, subId: Int): ChallengeResponse? {
+ val expectedBody = sms.takeIf { it.isNotEmpty() } ?: return null
- Log.d(TAG, "Waiting for MT SMS containing challenge string")
+ Log.d(TAG, "Waiting for MT SMS containing challenge string")
- val result = withTimeoutOrNull(300_000) {
- suspendCancellableCoroutine?> { continuation ->
- val receiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- try {
- if (intent.action == Telephony.Sms.Intents.SMS_RECEIVED_ACTION) {
- val receivedSubId = intent.getIntExtra(
- "android.telephony.extra.SUBSCRIPTION_INDEX",
- intent.getIntExtra("subscription", -1)
- )
- if (subId != -1 && receivedSubId != -1 && receivedSubId != subId) {
- return
- }
- val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
- for (msg in messages) {
- val body = msg.messageBody
- if (body != null && body.contains(expectedBody)) {
- Log.d(
- TAG,
- "Matching MT SMS received from ${msg.originatingAddress}"
- )
- context.unregisterReceiver(this)
- if (continuation.isActive) {
- continuation.resume(
- Pair(
- body,
- msg.originatingAddress ?: ""
- )
+ val result = withTimeoutOrNull(300_000) {
+ suspendCancellableCoroutine?> { continuation ->
+ val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ try {
+ if (intent.action == Telephony.Sms.Intents.SMS_RECEIVED_ACTION) {
+ val receivedSubId = intent.getIntExtra(
+ "android.telephony.extra.SUBSCRIPTION_INDEX",
+ intent.getIntExtra("subscription", -1)
+ )
+ if (subId != -1 && receivedSubId != -1 && receivedSubId != subId) {
+ return
+ }
+ val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
+ for (msg in messages) {
+ val body = msg.messageBody
+ if (body != null && body.contains(expectedBody)) {
+ Log.d(
+ TAG,
+ "Matching MT SMS received from ${msg.originatingAddress}"
+ )
+ context.unregisterReceiver(this)
+ if (continuation.isActive) {
+ continuation.resume(
+ Pair(
+ body,
+ msg.originatingAddress ?: ""
)
- }
- return
+ )
}
+ return
}
}
- } catch (e: Exception) {
- Log.e(TAG, "Error in MT SMS receiver", e)
}
+ } catch (e: Exception) {
+ Log.e(TAG, "Error in MT SMS receiver", e)
}
}
+ }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- context.registerReceiver(
- receiver,
- IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION),
- Context.RECEIVER_NOT_EXPORTED
- )
- } else {
- context.registerReceiver(
- receiver,
- IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)
- )
- }
- continuation.invokeOnCancellation {
- try {
- context.unregisterReceiver(receiver)
- } catch (_: Exception) {
- }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ context.registerReceiver(
+ receiver,
+ IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION),
+ Context.RECEIVER_NOT_EXPORTED
+ )
+ } else {
+ context.registerReceiver(
+ receiver,
+ IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)
+ )
+ }
+ continuation.invokeOnCancellation {
+ try {
+ context.unregisterReceiver(receiver)
+ } catch (_: Exception) {
}
}
}
+ }
- return result?.let { (body, sender) ->
- ChallengeResponse(
- mt_response = MTChallengeResponseData(
- sms = body,
- sender = sender
- )
+ return result?.let { (body, sender) ->
+ ChallengeResponse(
+ mt_response = MTChallengeResponseData(
+ sms = body,
+ sender = sender
)
- }
+ )
}
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
index 3e1ea8d37e..ceb4395bb1 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
@@ -19,141 +19,140 @@ import java.util.concurrent.TimeUnit
private const val TAG = "RegisteredSmsVerifier"
-class RegisteredSmsVerifier(private val context: Context, private val subId: Int) {
- fun verify(challenge: RegisteredSmsChallenge?): ChallengeResponse? {
- val expectedPayloads = challenge?.verified_senders
- ?.map { it.phone_number_id.toByteArray() }
- ?.filter { it.isNotEmpty() }
- .orEmpty()
- if (expectedPayloads.isEmpty()) return null
-
- val localNumbers = getLocalNumbers()
- if (localNumbers.isEmpty()) {
- Log.w(TAG, "No local phone numbers available for registered SMS verification")
- return emptyResponse()
- }
+fun RegisteredSmsChallenge.verify(context: Context, subId: Int): ChallengeResponse {
+ val expectedPayloads = verified_senders
+ .map { it.phone_number_id.toByteArray() }
+ .filter { it.isNotEmpty() }
+ if (expectedPayloads.isEmpty()) return ChallengeResponse(
+ registered_sms_response = RegisteredSmsChallengeResponse(
+ items = emptyList()
+ )
+ )
- val historyStart = System.currentTimeMillis() -
- TimeUnit.HOURS.toMillis(VerificationSettingsPhenotypes.A2P_HISTORY_WINDOW_HOURS)
- val bucketSizeMillis = (TimeUnit.HOURS.toMillis(
- VerificationSettingsPhenotypes.A2P_SMS_SIGNAL_GRANULARITY_HRS
- ) / 2).coerceAtLeast(1L)
-
- val responseItems = mutableListOf()
-
- try {
- val cursor = context.contentResolver.query(
- Telephony.Sms.Inbox.CONTENT_URI,
- arrayOf("date", "address", "body", "sub_id"),
- "date > ?",
- arrayOf(historyStart.toString()),
- "date DESC"
- ) ?: return emptyResponse()
-
- cursor.use {
- while (it.moveToNext()) {
- val date = it.getLong(it.getColumnIndexOrThrow("date"))
- val sender = it.getString(it.getColumnIndexOrThrow("address")) ?: continue
- val body = it.getString(it.getColumnIndexOrThrow("body")) ?: continue
- val messageSubId = runCatching {
- it.getInt(it.getColumnIndexOrThrow("sub_id"))
- }.getOrDefault(-1)
-
- val candidateNumbers = if (subId != -1 && messageSubId == subId) {
- localNumbers
- } else if (messageSubId == -1) {
- localNumbers
- } else {
- getLocalNumbers(messageSubId).ifEmpty { localNumbers }
- }
+ val localNumbers = getLocalNumbers(context, subId)
+ if (localNumbers.isEmpty()) {
+ Log.w(TAG, "No local phone numbers available for registered SMS verification")
+ return ChallengeResponse(registered_sms_response = RegisteredSmsChallengeResponse(items = emptyList()))
+ }
+
+ val historyStart = System.currentTimeMillis() -
+ TimeUnit.HOURS.toMillis(VerificationSettingsPhenotypes.A2P_HISTORY_WINDOW_HOURS)
+ val bucketSizeMillis = (TimeUnit.HOURS.toMillis(
+ VerificationSettingsPhenotypes.A2P_SMS_SIGNAL_GRANULARITY_HRS
+ ) / 2).coerceAtLeast(1L)
+
+ val responseItems = mutableListOf()
+
+ try {
+ val cursor = context.contentResolver.query(
+ Telephony.Sms.Inbox.CONTENT_URI,
+ arrayOf("date", "address", "body", "sub_id"),
+ "date > ?",
+ arrayOf(historyStart.toString()),
+ "date DESC"
+ ) ?: return ChallengeResponse(
+ registered_sms_response = RegisteredSmsChallengeResponse(
+ items = emptyList()
+ )
+ )
- val bucketStart = date - (date % bucketSizeMillis)
- for (localNumber in candidateNumbers) {
- val payload = computePayload(bucketStart, localNumber, sender, body)
- if (expectedPayloads.any { expected -> expected.contentEquals(payload) }) {
- responseItems += RegisteredSmsChallengeResponseItem(
- payload = RegisteredSmsChallengeResponsePayload(
- payload = payload.toByteString()
- )
+ cursor.use {
+ while (it.moveToNext()) {
+ val date = it.getLong(it.getColumnIndexOrThrow("date"))
+ val sender = it.getString(it.getColumnIndexOrThrow("address")) ?: continue
+ val body = it.getString(it.getColumnIndexOrThrow("body")) ?: continue
+ val messageSubId = runCatching {
+ it.getInt(it.getColumnIndexOrThrow("sub_id"))
+ }.getOrDefault(-1)
+
+ val candidateNumbers = if (subId != -1 && messageSubId == subId) {
+ localNumbers
+ } else if (messageSubId == -1) {
+ localNumbers
+ } else {
+ getLocalNumbers(context, messageSubId).ifEmpty { localNumbers }
+ }
+
+ val bucketStart = date - (date % bucketSizeMillis)
+ for (localNumber in candidateNumbers) {
+ val payload = computePayload(bucketStart, localNumber, sender, body)
+ if (expectedPayloads.any { expected -> expected.contentEquals(payload) }) {
+ responseItems += RegisteredSmsChallengeResponseItem(
+ payload = RegisteredSmsChallengeResponsePayload(
+ payload = payload.toByteString()
)
- }
+ )
}
}
}
- } catch (e: SecurityException) {
- Log.w(TAG, "SMS inbox access denied", e)
- return emptyResponse()
- } catch (e: Exception) {
- Log.e(TAG, "Registered SMS verification failed", e)
- return emptyResponse()
}
-
- return ChallengeResponse(
- registered_sms_response = RegisteredSmsChallengeResponse(items = responseItems.distinct())
- )
+ } catch (e: SecurityException) {
+ Log.w(TAG, "SMS inbox access denied", e)
+ return ChallengeResponse(registered_sms_response = RegisteredSmsChallengeResponse(items = emptyList()))
+ } catch (e: Exception) {
+ Log.e(TAG, "Registered SMS verification failed", e)
+ return ChallengeResponse(registered_sms_response = RegisteredSmsChallengeResponse(items = emptyList()))
}
- private fun emptyResponse(): ChallengeResponse {
- return ChallengeResponse(
- registered_sms_response = RegisteredSmsChallengeResponse(items = emptyList())
- )
- }
+ return ChallengeResponse(
+ registered_sms_response = RegisteredSmsChallengeResponse(items = responseItems.distinct())
+ )
+}
- private fun getLocalNumbers(targetSubId: Int = subId): List {
- val numbers = linkedSetOf()
- val subscriptionManager =
- context.getSystemService()
- val telephonyManager = context.getSystemService()
-
- try {
- subscriptionManager?.activeSubscriptionInfoList.orEmpty().forEach { info ->
- if (targetSubId != -1 && info.subscriptionId != targetSubId) return@forEach
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU && subscriptionManager != null) {
- numbers += subscriptionManager.getPhoneNumber(
- info.subscriptionId,
- SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER
- )
- numbers += subscriptionManager.getPhoneNumber(
- info.subscriptionId,
- SubscriptionManager.PHONE_NUMBER_SOURCE_UICC
- )
- numbers += subscriptionManager.getPhoneNumber(
- info.subscriptionId,
- SubscriptionManager.PHONE_NUMBER_SOURCE_IMS
- )
- }
- val targetManager =
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
- telephonyManager?.createForSubscriptionId(info.subscriptionId)
- } else {
- telephonyManager
- }
- numbers += targetManager?.line1Number.orEmpty()
- numbers += info.number.orEmpty()
+private fun getLocalNumbers(context: Context, targetSubId: Int): List {
+ val numbers = linkedSetOf()
+ val subscriptionManager =
+ context.getSystemService()
+ val telephonyManager = context.getSystemService()
+
+ try {
+ subscriptionManager?.activeSubscriptionInfoList.orEmpty().forEach { info ->
+ if (targetSubId != -1 && info.subscriptionId != targetSubId) return@forEach
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU && subscriptionManager != null) {
+ numbers += subscriptionManager.getPhoneNumber(
+ info.subscriptionId,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER
+ )
+ numbers += subscriptionManager.getPhoneNumber(
+ info.subscriptionId,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_UICC
+ )
+ numbers += subscriptionManager.getPhoneNumber(
+ info.subscriptionId,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_IMS
+ )
}
- } catch (e: Exception) {
- Log.w(TAG, "Unable to collect local phone numbers", e)
+ val targetManager =
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ telephonyManager?.createForSubscriptionId(info.subscriptionId)
+ } else {
+ telephonyManager
+ }
+ numbers += targetManager?.line1Number.orEmpty()
+ numbers += info.number.orEmpty()
}
-
- return numbers.filter { it.isNotBlank() }.distinct()
+ } catch (e: Exception) {
+ Log.w(TAG, "Unable to collect local phone numbers", e)
}
- private fun computePayload(
- bucketStart: Long,
- localNumber: String,
- sender: String,
- body: String
- ): ByteArray {
- val digest = MessageDigest.getInstance("SHA-512")
- digest.update(bucketStart.toString().toByteArray(StandardCharsets.UTF_8))
- digest.update(hashUtf8(localNumber))
- digest.update(hashUtf8(sender))
- digest.update(body.toByteArray(StandardCharsets.UTF_8))
- return digest.digest()
- }
+ return numbers.filter { it.isNotBlank() }.distinct()
+}
- private fun hashUtf8(value: String): ByteArray {
- return MessageDigest.getInstance("SHA-512")
- .digest(value.toByteArray(StandardCharsets.UTF_8))
- }
+private fun computePayload(
+ bucketStart: Long,
+ localNumber: String,
+ sender: String,
+ body: String
+): ByteArray {
+ val digest = MessageDigest.getInstance("SHA-512")
+ digest.update(bucketStart.toString().toByteArray(StandardCharsets.UTF_8))
+ digest.update(hashUtf8(localNumber))
+ digest.update(hashUtf8(sender))
+ digest.update(body.toByteArray(StandardCharsets.UTF_8))
+ return digest.digest()
+}
+
+private fun hashUtf8(value: String): ByteArray {
+ return MessageDigest.getInstance("SHA-512")
+ .digest(value.toByteArray(StandardCharsets.UTF_8))
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt
index 4066a5dffb..ebaa6733ef 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt
@@ -34,15 +34,12 @@ import javax.xml.parsers.DocumentBuilderFactory
private const val TAG = "Ts43Verifier"
-class Ts43Verifier(private val context: Context, private val subId: Int) {
-
- private class Ts43ApiException(
- val errorCode: Ts43ChallengeResponseError.Code,
- val httpStatus: Int,
- val requestType: Ts43ChallengeResponseError.RequestType,
- cause: Throwable? = null
- ) : Exception(cause)
+fun Ts43Challenge.verify(context: Context, subId: Int): ChallengeResponse {
+ val verifier = InternalTs43Verifier(context, subId)
+ return verifier.verify(this)
+}
+private class InternalTs43Verifier(private val context: Context, private val subId: Int) {
private val httpHistory = mutableListOf()
private val telephonyManager: TelephonyManager by lazy {
@@ -56,41 +53,7 @@ class Ts43Verifier(private val context: Context, private val subId: Int) {
private val eapAkaService by lazy { EapAkaService(telephonyManager) }
- private fun errorCode(rawCode: Int): Ts43ChallengeResponseError.Code =
- Ts43ChallengeResponseError.Code.fromValue(rawCode)
- ?: Ts43ChallengeResponseError.Code.TS43_ERROR_CODE_UNSPECIFIED
-
- private val okHttpClient by lazy {
- OkHttpClient.Builder()
- .connectTimeout(30, TimeUnit.SECONDS)
- .readTimeout(30, TimeUnit.SECONDS)
- .followRedirects(false)
- .followSslRedirects(false)
- .cookieJar(object : CookieJar {
- private val cookieStore = mutableMapOf>()
- override fun saveFromResponse(url: HttpUrl, cookies: List) {
- val existing = cookieStore.getOrPut(url.host) { mutableListOf() }
- for (cookie in cookies) {
- existing.removeAll {
- it.name == cookie.name &&
- it.domain == cookie.domain &&
- it.path == cookie.path
- }
- existing += cookie
- }
- }
-
- override fun loadForRequest(url: HttpUrl): List {
- return cookieStore[url.host]
- ?.filter { cookie -> cookie.matches(url) }
- .orEmpty()
- }
- })
- .build()
- }
-
- fun verify(challenge: Ts43Challenge?): ChallengeResponse? {
- if (challenge == null) return null
+ fun verify(challenge: Ts43Challenge): ChallengeResponse {
httpHistory.clear()
return try {
@@ -123,7 +86,15 @@ class Ts43Verifier(private val context: Context, private val subId: Int) {
challenge.app_id,
Ts43ChallengeResponseError.RequestType.TS43_REQUEST_TYPE_GET_PHONE_NUMBER_API
)
- buildClientResponse(challenge, responseBody)
+ ChallengeResponse(
+ ts43_challenge_response = Ts43ChallengeResponse(
+ ts43_type = challenge.ts43_type,
+ client_challenge_response = ClientChallengeResponse(
+ get_phone_number_response = responseBody
+ ),
+ http_history = httpHistory.toList()
+ )
+ )
}
}
}
@@ -156,7 +127,15 @@ class Ts43Verifier(private val context: Context, private val subId: Int) {
challenge.app_id,
Ts43ChallengeResponseError.RequestType.TS43_REQUEST_TYPE_ACQUIRE_TEMPORARY_TOKEN_API
)
- buildServerResponse(challenge, responseBody)
+ ChallengeResponse(
+ ts43_challenge_response = Ts43ChallengeResponse(
+ ts43_type = challenge.ts43_type,
+ server_challenge_response = ServerChallengeResponse(
+ acquire_temporary_token_response = responseBody
+ ),
+ http_history = httpHistory.toList()
+ )
+ )
}
}
}
@@ -168,7 +147,19 @@ class Ts43Verifier(private val context: Context, private val subId: Int) {
}
} catch (e: Ts43ApiException) {
Log.w(TAG, "TS43 API failure requestType=${e.requestType} http=${e.httpStatus}", e)
- buildApiErrorResponse(challenge, e)
+ ChallengeResponse(
+ ts43_challenge_response = Ts43ChallengeResponse(
+ ts43_type = challenge.ts43_type,
+ status = Ts43ChallengeResponseStatus(
+ error = Ts43ChallengeResponseError(
+ error_code = e.errorCode,
+ http_status = e.httpStatus,
+ request_type = e.requestType
+ )
+ ),
+ http_history = httpHistory.toList()
+ )
+ )
} catch (e: NullPointerException) {
Log.e(TAG, "TS43 null failure", e)
buildFailureResponse(
@@ -366,36 +357,6 @@ class Ts43Verifier(private val context: Context, private val subId: Int) {
}.getOrNull()?.takeIf { it.isNotEmpty() }
}
- private fun buildServerResponse(
- challenge: Ts43Challenge,
- responseBody: String
- ): ChallengeResponse {
- return ChallengeResponse(
- ts43_challenge_response = Ts43ChallengeResponse(
- ts43_type = challenge.ts43_type,
- server_challenge_response = ServerChallengeResponse(
- acquire_temporary_token_response = responseBody
- ),
- http_history = httpHistory.toList()
- )
- )
- }
-
- private fun buildClientResponse(
- challenge: Ts43Challenge,
- responseBody: String
- ): ChallengeResponse {
- return ChallengeResponse(
- ts43_challenge_response = Ts43ChallengeResponse(
- ts43_type = challenge.ts43_type,
- client_challenge_response = ClientChallengeResponse(
- get_phone_number_response = responseBody
- ),
- http_history = httpHistory.toList()
- )
- )
- }
-
private fun buildFailureResponse(
challenge: Ts43Challenge,
statusCode: Ts43ChallengeResponseStatus.Code
@@ -408,23 +369,44 @@ class Ts43Verifier(private val context: Context, private val subId: Int) {
)
)
}
+}
- private fun buildApiErrorResponse(
- challenge: Ts43Challenge,
- error: Ts43ApiException
- ): ChallengeResponse {
- return ChallengeResponse(
- ts43_challenge_response = Ts43ChallengeResponse(
- ts43_type = challenge.ts43_type,
- status = Ts43ChallengeResponseStatus(
- error = Ts43ChallengeResponseError(
- error_code = error.errorCode,
- http_status = error.httpStatus,
- request_type = error.requestType
- )
- ),
- http_history = httpHistory.toList()
- )
- )
- }
+private class Ts43ApiException(
+ val errorCode: Ts43ChallengeResponseError.Code,
+ val httpStatus: Int,
+ val requestType: Ts43ChallengeResponseError.RequestType,
+ cause: Throwable? = null
+) : Exception(cause)
+
+private fun errorCode(rawCode: Int): Ts43ChallengeResponseError.Code =
+ Ts43ChallengeResponseError.Code.fromValue(rawCode)
+ ?: Ts43ChallengeResponseError.Code.TS43_ERROR_CODE_UNSPECIFIED
+
+private val okHttpClient by lazy {
+ OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .followRedirects(false)
+ .followSslRedirects(false)
+ .cookieJar(object : CookieJar {
+ private val cookieStore = mutableMapOf>()
+ override fun saveFromResponse(url: HttpUrl, cookies: List) {
+ val existing = cookieStore.getOrPut(url.host) { mutableListOf() }
+ for (cookie in cookies) {
+ existing.removeAll {
+ it.name == cookie.name &&
+ it.domain == cookie.domain &&
+ it.path == cookie.path
+ }
+ existing += cookie
+ }
+ }
+
+ override fun loadForRequest(url: HttpUrl): List {
+ return cookieStore[url.host]
+ ?.filter { cookie -> cookie.matches(url) }
+ .orEmpty()
+ }
+ })
+ .build()
}
From 01400ff25b4a27b1f90905ada67ca04c1e86cda3 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Thu, 26 Mar 2026 01:38:08 -0400
Subject: [PATCH 04/31] More refactoring
---
.../gms/constellation/core/AuthManager.kt | 39 +++++++-------
.../gms/constellation/core/GetIidToken.kt | 2 +-
.../core/GetVerifiedPhoneNumbers.kt | 2 +-
.../gms/constellation/core/RpcClient.kt | 54 +++++++------------
.../core/proto/builders/ClientInfoBuilder.kt | 4 +-
.../core/proto/builders/CommonBuilders.kt | 3 +-
.../proto/builders/RequestBuildContext.kt | 3 +-
7 files changed, 49 insertions(+), 58 deletions(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
index 94b4bdb8a8..371e677448 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
@@ -13,8 +13,11 @@ import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
-class AuthManager(context: Context) {
+val Context.authManager: AuthManager get() = AuthManager.get(this)
+
+class AuthManager private constructor(context: Context) {
private val context = context.applicationContext
+ private val sharedPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
companion object {
private const val PREFS_NAME = "constellation_prefs"
@@ -24,22 +27,14 @@ class AuthManager(context: Context) {
@Volatile
private var instance: AuthManager? = null
- fun get(context: Context): AuthManager {
- val existing = instance
- if (existing != null) return existing
-
- return synchronized(this) {
- instance ?: AuthManager(context).also { instance = it }
- }
+ fun get(context: Context): AuthManager = instance ?: synchronized(this) {
+ instance ?: AuthManager(context).also { instance = it }
}
}
- private val sharedPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
-
// GMS signing format: {iidToken}:{seconds}:{nanos}
fun signIidToken(iidToken: String): Pair {
- val now = System.currentTimeMillis()
- val timestamp = Instant.ofEpochMilli(now)
+ val timestamp = Instant.ofEpochMilli(System.currentTimeMillis())
val content = "$iidToken:${timestamp.epochSecond}:${timestamp.nano}"
return sign(content) to timestamp
}
@@ -61,10 +56,20 @@ class AuthManager(context: Context) {
try {
val kf = KeyFactory.getInstance("EC")
val privateKey = kf.generatePrivate(
- PKCS8EncodedKeySpec(Base64.decode(privateKeyStr, Base64.DEFAULT))
+ PKCS8EncodedKeySpec(
+ Base64.decode(
+ privateKeyStr,
+ Base64.DEFAULT
+ )
+ )
)
val publicKey = kf.generatePublic(
- X509EncodedKeySpec(Base64.decode(publicKeyStr, Base64.DEFAULT))
+ X509EncodedKeySpec(
+ Base64.decode(
+ publicKeyStr,
+ Base64.DEFAULT
+ )
+ )
)
return KeyPair(publicKey, privateKey)
} catch (_: Exception) {
@@ -96,7 +101,5 @@ class AuthManager(context: Context) {
}
}
- fun getFid(): String {
- return InstanceID.getInstance(context).id
- }
-}
+ fun getFid(): String = InstanceID.getInstance(context).id
+}
\ No newline at end of file
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt
index 9ba0e0b8af..d7dc64cffe 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt
@@ -18,7 +18,7 @@ suspend fun handleGetIidToken(
request: GetIidTokenRequest
) = withContext(Dispatchers.IO) {
try {
- val authManager = AuthManager.get(context)
+ val authManager = context.authManager
val iidToken = authManager.getIidToken(request.projectNumber?.toString())
val fid = authManager.getFid()
val (signature, timestamp) = authManager.signIidToken(iidToken)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
index 5ab87e178c..d311bcf562 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
@@ -41,7 +41,7 @@ internal suspend fun fetchVerifiedPhoneNumbers(
bundle: Bundle,
callingPackage: String = bundle.getString("calling_package") ?: context.packageName
): List = withContext(Dispatchers.IO) {
- val authManager = AuthManager.get(context)
+ val authManager = context.authManager
val sessionId = UUID.randomUUID().toString()
val selections = extractPhoneNumberSelections(bundle)
val certificateHash = bundle.getString("certificate_hash") ?: ""
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/RpcClient.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/RpcClient.kt
index 57495656c3..e5030bac66 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/RpcClient.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/RpcClient.kt
@@ -1,47 +1,33 @@
package org.microg.gms.constellation.core
import com.squareup.wire.GrpcClient
-import okhttp3.Interceptor
import okhttp3.OkHttpClient
-import okhttp3.Response
import org.microg.gms.common.Constants
import org.microg.gms.constellation.core.proto.PhoneDeviceVerificationClient
import org.microg.gms.constellation.core.proto.PhoneNumberClient
-private class AuthInterceptor : Interceptor {
- override fun intercept(chain: Interceptor.Chain): Response {
- val originalRequest = chain.request()
-
- val builder = originalRequest.newBuilder()
- .header("X-Goog-Api-Key", "AIzaSyAP-gfH3qvi6vgHZbSYwQ_XHqV_mXHhzIk")
- .header("X-Android-Package", Constants.GMS_PACKAGE_NAME)
- .header("X-Android-Cert", Constants.GMS_PACKAGE_SIGNATURE_SHA1.uppercase())
-
- return chain.proceed(builder.build())
- }
-}
-
object RpcClient {
- private val client: OkHttpClient by lazy {
- OkHttpClient.Builder()
- .addInterceptor(AuthInterceptor())
- .build()
- }
-
- private val grpcClient: GrpcClient by lazy {
- GrpcClient.Builder()
- .client(client)
- // Google's constellationserver does NOT like compressed requests
- .minMessageToCompress(Long.MAX_VALUE)
- .baseUrl("https://phonedeviceverification-pa.googleapis.com/")
- .build()
- }
-
- val phoneDeviceVerificationClient: PhoneDeviceVerificationClient by lazy {
+ private val client: OkHttpClient = OkHttpClient.Builder()
+ .addInterceptor { chain ->
+ val originalRequest = chain.request()
+ val builder = originalRequest.newBuilder()
+ .header("X-Goog-Api-Key", "AIzaSyAP-gfH3qvi6vgHZbSYwQ_XHqV_mXHhzIk")
+ .header("X-Android-Package", Constants.GMS_PACKAGE_NAME)
+ .header("X-Android-Cert", Constants.GMS_PACKAGE_SIGNATURE_SHA1.uppercase())
+ chain.proceed(builder.build())
+ }
+ .build()
+
+ private val grpcClient: GrpcClient = GrpcClient.Builder()
+ .client(client)
+ // Google's constellationserver does NOT like compressed requests
+ .minMessageToCompress(Long.MAX_VALUE)
+ .baseUrl("https://phonedeviceverification-pa.googleapis.com/")
+ .build()
+
+ val phoneDeviceVerificationClient: PhoneDeviceVerificationClient =
grpcClient.create(PhoneDeviceVerificationClient::class)
- }
- val phoneNumberClient: PhoneNumberClient by lazy {
+ val phoneNumberClient: PhoneNumberClient =
grpcClient.create(PhoneNumberClient::class)
- }
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
index bc97172d63..8e61d1005b 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
@@ -14,6 +14,7 @@ import org.microg.gms.common.Constants
import org.microg.gms.constellation.core.AuthManager
import org.microg.gms.constellation.core.ConstellationStateStore
import org.microg.gms.constellation.core.GServices
+import org.microg.gms.constellation.core.authManager
import org.microg.gms.constellation.core.proto.ClientInfo
import org.microg.gms.constellation.core.proto.CountryInfo
import org.microg.gms.constellation.core.proto.DeviceID
@@ -47,11 +48,10 @@ suspend operator fun ClientInfo.Companion.invoke(
): ClientInfo {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val locale = Locale.getDefault().let { "${it.language}_${it.country}" }
- val authManager = AuthManager.get(context)
return ClientInfo(
device_id = DeviceID(context, buildContext.iidToken),
- client_public_key = authManager.getOrCreateKeyPair().public.encoded.toByteString(),
+ client_public_key = context.authManager.getOrCreateKeyPair().public.encoded.toByteString(),
locale = locale,
gmscore_version_number = Constants.GMS_VERSION_CODE / 1000,
gmscore_version = packageInfo.versionName ?: "",
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
index d2f6f1e0da..c79cdbff62 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.os.Bundle
import okio.ByteString.Companion.toByteString
import org.microg.gms.constellation.core.AuthManager
+import org.microg.gms.constellation.core.authManager
import org.microg.gms.constellation.core.proto.AuditToken
import org.microg.gms.constellation.core.proto.AuditTokenMetadata
import org.microg.gms.constellation.core.proto.AuditUuid
@@ -46,7 +47,7 @@ suspend operator fun RequestHeader.Companion.invoke(
triggerType: RequestTrigger.Type = RequestTrigger.Type.CONSENT_API_TRIGGER,
includeClientAuth: Boolean = false
): RequestHeader {
- val authManager = if (includeClientAuth) AuthManager.get(context) else null
+ val authManager = if (includeClientAuth) context.authManager else null
val clientAuth = if (includeClientAuth) {
val (signature, timestamp) = authManager!!.signIidToken(buildContext.iidToken)
org.microg.gms.constellation.core.proto.ClientAuth(
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt
index c581237c17..1262045264 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt
@@ -2,6 +2,7 @@ package org.microg.gms.constellation.core.proto.builders
import android.content.Context
import org.microg.gms.constellation.core.AuthManager
+import org.microg.gms.constellation.core.authManager
import org.microg.gms.constellation.core.proto.GaiaToken
data class RequestBuildContext(
@@ -11,7 +12,7 @@ data class RequestBuildContext(
suspend fun buildRequestContext(
context: Context,
- authManager: AuthManager = AuthManager.get(context)
+ authManager: AuthManager = context.authManager
): RequestBuildContext {
return RequestBuildContext(
iidToken = authManager.getIidToken(),
From 3f15e8bf4011898dce4b33deabecaba58b4349ea Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 27 Mar 2026 03:40:55 -0400
Subject: [PATCH 05/31] Add `android.permission.USE_CREDENTIALS` into
`AndroidManifest.xml`
---
play-services-constellation/core/src/main/AndroidManifest.xml | 1 +
.../gms/constellation/core/proto/builders/GaiaInfoBuilder.kt | 1 +
2 files changed, 2 insertions(+)
diff --git a/play-services-constellation/core/src/main/AndroidManifest.xml b/play-services-constellation/core/src/main/AndroidManifest.xml
index cbe65637d0..bd95282658 100644
--- a/play-services-constellation/core/src/main/AndroidManifest.xml
+++ b/play-services-constellation/core/src/main/AndroidManifest.xml
@@ -12,6 +12,7 @@
+
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
index febce16e53..31ee9146ab 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
@@ -23,6 +23,7 @@ operator fun GaiaSignals.Companion.invoke(context: Context): GaiaSignals? {
val md = MessageDigest.getInstance("SHA-256")
for (account in accounts) {
+ // Note: Simple implementation, maybe do actual obfuscated Gaia ID retrieval later?
val hash = md.digest(account.name.toByteArray(Charsets.UTF_8))
val number =
hash.take(8).fold(0L) { acc, byte -> (acc shl 8) or (byte.toLong() and 0xFF) }
From 65961f8491cbe0e8c7c898d39da33742f3cbec9b Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 27 Mar 2026 06:15:36 -0400
Subject: [PATCH 06/31] Fix `NewApi` lints
---
.../gms/constellation/core/AuthManager.kt | 21 ++++++-
.../core/ConstellationApiService.kt | 5 ++
.../core/ConstellationStateStore.kt | 4 ++
.../gms/constellation/core/GetIidToken.kt | 4 +-
.../core/GetVerifiedPhoneNumbers.kt | 17 ++++--
.../core/VerificationMappings.kt | 3 +
.../constellation/core/VerifyPhoneNumber.kt | 4 +-
.../core/proto/builders/ClientInfoBuilder.kt | 8 ++-
.../core/proto/builders/CommonBuilders.kt | 5 +-
.../core/proto/builders/GaiaInfoBuilder.kt | 5 ++
.../core/proto/builders/SyncRequestBuilder.kt | 10 +---
.../proto/builders/TelephonyInfoBuilder.kt | 7 ++-
.../core/verification/CarrierIdVerifier.kt | 3 +
.../core/verification/ChallengeProcessor.kt | 4 ++
.../core/verification/MoSmsVerifier.kt | 55 ++++++++++++-------
.../verification/RegisteredSmsVerifier.kt | 8 ++-
.../core/verification/Ts43Verifier.kt | 7 ++-
.../ts43/ServiceEntitlementExtension.kt | 6 ++
18 files changed, 127 insertions(+), 49 deletions(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
index 371e677448..ba601c3832 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
@@ -1,7 +1,10 @@
package org.microg.gms.constellation.core
+import android.annotation.SuppressLint
import android.content.Context
+import android.os.Build
import android.util.Base64
+import androidx.annotation.RequiresApi
import androidx.core.content.edit
import com.google.android.gms.iid.InstanceID
import com.squareup.wire.Instant
@@ -24,6 +27,8 @@ class AuthManager private constructor(context: Context) {
private const val KEY_PRIVATE = "private_key"
private const val KEY_PUBLIC = "public_key"
+ // This is safe as the Context is immediately converted to the application context.
+ @SuppressLint("StaticFieldLeak")
@Volatile
private var instance: AuthManager? = null
@@ -33,10 +38,20 @@ class AuthManager private constructor(context: Context) {
}
// GMS signing format: {iidToken}:{seconds}:{nanos}
+ fun signIidTokenCompat(iidToken: String): Pair {
+ val currentTimeMillis = System.currentTimeMillis()
+
+ val epochSecond = currentTimeMillis / 1000
+ val nano = (currentTimeMillis % 1000) * 1_000_000
+
+ val content = "$iidToken:$epochSecond:$nano"
+ return sign(content) to currentTimeMillis
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
fun signIidToken(iidToken: String): Pair {
- val timestamp = Instant.ofEpochMilli(System.currentTimeMillis())
- val content = "$iidToken:${timestamp.epochSecond}:${timestamp.nano}"
- return sign(content) to timestamp
+ val (bytes, millis) = signIidTokenCompat(iidToken)
+ return bytes to Instant.ofEpochMilli(millis)
}
fun getIidToken(projectNumber: String? = null): String {
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationApiService.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationApiService.kt
index f4ddbb4123..39334ec569 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationApiService.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationApiService.kt
@@ -1,6 +1,7 @@
package org.microg.gms.constellation.core
import android.content.Context
+import android.os.Build
import android.os.Bundle
import android.util.Log
import com.google.android.gms.common.Feature
@@ -76,6 +77,7 @@ class ConstellationApiServiceImpl(
}"
)
if (cb == null || bundle == null) return
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
serviceScope.launch { handleVerifyPhoneNumberV1(context, cb, bundle, packageName) }
}
@@ -86,6 +88,7 @@ class ConstellationApiServiceImpl(
) {
Log.i(TAG, "verifyPhoneNumberSingleUse()")
if (cb == null || bundle == null) return
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
serviceScope.launch { handleVerifyPhoneNumberSingleUse(context, cb, bundle, packageName) }
}
@@ -99,6 +102,7 @@ class ConstellationApiServiceImpl(
"verifyPhoneNumber(): apiVersion=${request?.apiVersion}, policy=${request?.policyId}"
)
if (cb == null || request == null) return
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
serviceScope.launch { handleVerifyPhoneNumberRequest(context, cb, request, packageName) }
}
@@ -119,6 +123,7 @@ class ConstellationApiServiceImpl(
) {
Log.i(TAG, "getPnvCapabilities(): $request")
if (cb == null || request == null) return
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) return
serviceScope.launch { handleGetPnvCapabilities(context, cb, request) }
}
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationStateStore.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationStateStore.kt
index dc802dfacc..f41739c10b 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationStateStore.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/ConstellationStateStore.kt
@@ -1,8 +1,12 @@
+@file:RequiresApi(Build.VERSION_CODES.O)
+
package org.microg.gms.constellation.core
import android.annotation.SuppressLint
import android.content.Context
+import android.os.Build
import android.util.Base64
+import androidx.annotation.RequiresApi
import androidx.core.content.edit
import com.squareup.wire.Instant
import okio.ByteString.Companion.toByteString
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt
index d7dc64cffe..5ee8a6ac71 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetIidToken.kt
@@ -21,11 +21,11 @@ suspend fun handleGetIidToken(
val authManager = context.authManager
val iidToken = authManager.getIidToken(request.projectNumber?.toString())
val fid = authManager.getFid()
- val (signature, timestamp) = authManager.signIidToken(iidToken)
+ val (signature, timestamp) = authManager.signIidTokenCompat(iidToken)
callbacks.onIidTokenGenerated(
Status.SUCCESS,
- GetIidTokenResponse(iidToken, fid, signature, timestamp.toEpochMilli()),
+ GetIidTokenResponse(iidToken, fid, signature, timestamp),
ApiMetadata.DEFAULT
)
} catch (e: Exception) {
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
index d311bcf562..e02b3565fa 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
@@ -1,6 +1,9 @@
+@file:SuppressLint("NewApi")
package org.microg.gms.constellation.core
+import android.annotation.SuppressLint
import android.content.Context
+import android.os.Build
import android.os.Bundle
import android.util.Log
import com.google.android.gms.common.api.ApiMetadata
@@ -17,7 +20,7 @@ import org.microg.gms.constellation.core.proto.GetVerifiedPhoneNumbersRequest.Ph
import org.microg.gms.constellation.core.proto.IIDTokenAuth
import org.microg.gms.constellation.core.proto.TokenOption
import java.util.UUID
-import org.microg.gms.constellation.core.proto.VerifiedPhoneNumber as RpcVerifiedPhoneNumber
+import org.microg.gms.constellation.core.proto.VerifiedPhoneNumber
private const val TAG = "GetVerifiedPhoneNumbers"
@@ -27,6 +30,10 @@ suspend fun handleGetVerifiedPhoneNumbers(
bundle: Bundle
) = withContext(Dispatchers.IO) {
try {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ throw Exception("Unsupported SDK")
+ }
+
val phoneNumbers = fetchVerifiedPhoneNumbers(context, bundle).map { it.toPhoneNumberInfo() }
callbacks.onPhoneNumberVerified(Status.SUCCESS, phoneNumbers, ApiMetadata.DEFAULT)
@@ -40,7 +47,7 @@ internal suspend fun fetchVerifiedPhoneNumbers(
context: Context,
bundle: Bundle,
callingPackage: String = bundle.getString("calling_package") ?: context.packageName
-): List = withContext(Dispatchers.IO) {
+): List = withContext(Dispatchers.IO) {
val authManager = context.authManager
val sessionId = UUID.randomUUID().toString()
val selections = extractPhoneNumberSelections(bundle)
@@ -78,14 +85,14 @@ internal suspend fun fetchVerifiedPhoneNumbers(
response.phone_numbers
}
-internal fun List.toVerifyPhoneNumberResponse(): VerifyPhoneNumberResponse {
+internal fun List.toVerifyPhoneNumberResponse(): VerifyPhoneNumberResponse {
return VerifyPhoneNumberResponse(
map { it.toPhoneNumberVerification() }.toTypedArray(),
Bundle.EMPTY
)
}
-private fun RpcVerifiedPhoneNumber.toPhoneNumberInfo(): PhoneNumberInfo {
+private fun VerifiedPhoneNumber.toPhoneNumberInfo(): PhoneNumberInfo {
val extras = Bundle().apply {
if (id_token.isNotEmpty()) {
putString("id_token", id_token)
@@ -101,7 +108,7 @@ private fun RpcVerifiedPhoneNumber.toPhoneNumberInfo(): PhoneNumberInfo {
)
}
-private fun RpcVerifiedPhoneNumber.toPhoneNumberVerification(): PhoneNumberVerification {
+private fun VerifiedPhoneNumber.toPhoneNumberVerification(): PhoneNumberVerification {
val extras = Bundle().apply {
putInt("rcs_state", rcs_state.value)
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
index 62255e1fc0..6102ca047a 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
@@ -1,6 +1,8 @@
package org.microg.gms.constellation.core
+import android.os.Build
import android.os.Bundle
+import androidx.annotation.RequiresApi
import com.google.android.gms.constellation.PhoneNumberVerification
import org.microg.gms.constellation.core.proto.Param
import org.microg.gms.constellation.core.proto.UnverifiedInfo
@@ -52,6 +54,7 @@ fun Verification.getVerificationStatus(): Verification.Status {
}
}
+@RequiresApi(Build.VERSION_CODES.O)
fun Verification.toClientVerification(imsiToSlotMap: Map): PhoneNumberVerification {
val verificationStatus = this.getVerificationStatus()
var phoneNumber: String? = null
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
index 1d07ea48c3..e456fb00c4 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
@@ -1,3 +1,5 @@
+@file:RequiresApi(Build.VERSION_CODES.O)
+
package org.microg.gms.constellation.core
import android.content.Context
@@ -234,7 +236,6 @@ private suspend fun handleVerifyPhoneNumberRequest(
}
}
-@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
private suspend fun runVerificationFlow(
context: Context,
request: VerifyPhoneNumberRequest,
@@ -292,7 +293,6 @@ private fun PhoneNumberVerification.toLegacyPhoneNumberInfoOrNull(): PhoneNumber
return PhoneNumberInfo(1, phoneNumber, timestampMillis, extras)
}
-@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
private suspend fun executeSyncFlow(
context: Context,
sessionId: String,
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
index 8e61d1005b..8a67cb84ad 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
@@ -1,3 +1,5 @@
+@file:RequiresApi(Build.VERSION_CODES.O)
+
package org.microg.gms.constellation.core.proto.builders
import android.annotation.SuppressLint
@@ -6,12 +8,12 @@ import android.os.Build
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
+import androidx.annotation.RequiresApi
import androidx.core.content.edit
import androidx.core.content.getSystemService
import com.google.android.gms.tasks.await
import okio.ByteString.Companion.toByteString
import org.microg.gms.common.Constants
-import org.microg.gms.constellation.core.AuthManager
import org.microg.gms.constellation.core.ConstellationStateStore
import org.microg.gms.constellation.core.GServices
import org.microg.gms.constellation.core.authManager
@@ -71,6 +73,7 @@ suspend operator fun ClientInfo.Companion.invoke(
)
}
+@SuppressLint("HardwareIds")
operator fun DeviceID.Companion.invoke(context: Context, iidToken: String): DeviceID {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
@@ -123,9 +126,8 @@ operator fun CountryInfo.Companion.invoke(context: Context): CountryInfo {
val tm = context.getSystemService()
sm?.activeSubscriptionInfoList?.forEach { info ->
- val targetTM = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ val targetTM =
tm?.createForSubscriptionId(info.subscriptionId)
- } else tm
targetTM?.networkCountryIso?.let { networkCountries.add(it.lowercase()) }
info.countryIso?.let { simCountries.add(it.lowercase()) }
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
index c79cdbff62..ae7e49ba83 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
@@ -1,9 +1,11 @@
+
package org.microg.gms.constellation.core.proto.builders
import android.content.Context
+import android.os.Build
import android.os.Bundle
+import androidx.annotation.RequiresApi
import okio.ByteString.Companion.toByteString
-import org.microg.gms.constellation.core.AuthManager
import org.microg.gms.constellation.core.authManager
import org.microg.gms.constellation.core.proto.AuditToken
import org.microg.gms.constellation.core.proto.AuditTokenMetadata
@@ -40,6 +42,7 @@ fun AuditToken.Companion.generate(): AuditToken {
)
}
+@RequiresApi(Build.VERSION_CODES.O)
suspend operator fun RequestHeader.Companion.invoke(
context: Context,
sessionId: String,
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
index 31ee9146ab..60ff924562 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
@@ -1,8 +1,11 @@
+
package org.microg.gms.constellation.core.proto.builders
import android.accounts.AccountManager
import android.content.Context
+import android.os.Build
import android.util.Log
+import androidx.annotation.RequiresApi
import com.squareup.wire.Instant
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -16,6 +19,8 @@ import kotlin.math.absoluteValue
private const val TAG = "GaiaInfoBuilder"
+
+@RequiresApi(Build.VERSION_CODES.O)
operator fun GaiaSignals.Companion.invoke(context: Context): GaiaSignals? {
val entries = mutableListOf()
try {
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
index 61bc46d639..e2553dcb9b 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
@@ -1,3 +1,6 @@
+@file:RequiresApi(Build.VERSION_CODES.O)
+@file:SuppressLint("HardwareIds")
+
package org.microg.gms.constellation.core.proto.builders
import android.annotation.SuppressLint
@@ -30,8 +33,6 @@ import org.microg.gms.constellation.core.proto.VerificationPolicy
private const val TAG = "SyncRequestBuilder"
-@RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
-@SuppressLint("HardwareIds", "MissingPermission")
fun buildImsiToSubscriptionInfoMap(context: Context): Map {
val subscriptionManager =
context.getSystemService() ?: return emptyMap()
@@ -56,7 +57,6 @@ fun buildImsiToSubscriptionInfoMap(context: Context): Map()
val targetTm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && subscriptionId >= 0) {
@@ -107,7 +111,6 @@ fun NetworkSignal.Companion.getList(context: Context): List {
return connectivityInfos
}
-@SuppressLint("HardwareIds")
fun SimOperatorInfo.Companion.getList(context: Context): List {
val infos = mutableListOf()
try {
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt
index 53c1bcc72d..00d0fb8919 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/CarrierIdVerifier.kt
@@ -1,9 +1,12 @@
+@file:RequiresApi(Build.VERSION_CODES.N)
+
package org.microg.gms.constellation.core.verification
import android.content.Context
import android.os.Build
import android.telephony.TelephonyManager
import android.util.Log
+import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import org.microg.gms.constellation.core.proto.CarrierIdChallengeResponse
import org.microg.gms.constellation.core.proto.CarrierIdError
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
index ac79655766..f01997d252 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
@@ -1,8 +1,12 @@
+@file:RequiresApi(Build.VERSION_CODES.O)
+
package org.microg.gms.constellation.core.verification
import android.content.Context
+import android.os.Build
import android.telephony.SubscriptionInfo
import android.util.Log
+import androidx.annotation.RequiresApi
import com.squareup.wire.GrpcException
import com.squareup.wire.GrpcStatus
import org.microg.gms.constellation.core.ConstellationStateStore
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
index 19fd2d0305..5ff49b42c9 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
@@ -1,3 +1,5 @@
+@file:RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
+
package org.microg.gms.constellation.core.verification
import android.Manifest
@@ -11,6 +13,8 @@ import android.os.Build
import android.telephony.SmsManager
import android.telephony.SubscriptionManager
import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
@@ -21,7 +25,7 @@ import java.util.UUID
import kotlin.coroutines.resume
private const val TAG = "MoSmsVerifier"
-private const val ACTION_MO_SMS_SENT = "org.microg.gms.constellation.coreMO_SMS_SENT"
+private const val ACTION_MO_SMS_SENT = "org.microg.gms.constellation.core.MO_SMS_SENT"
suspend fun MoChallenge.verify(context: Context, subId: Int): ChallengeResponse {
if (proxy_number.isEmpty() || sms.isEmpty()) {
@@ -105,7 +109,12 @@ suspend fun MoChallenge.verify(context: Context, subId: Int): ChallengeResponse
Context.RECEIVER_NOT_EXPORTED
)
} else {
- context.registerReceiver(receiver, IntentFilter(action))
+ ContextCompat.registerReceiver(
+ context,
+ receiver,
+ IntentFilter(action),
+ ContextCompat.RECEIVER_NOT_EXPORTED
+ )
}
continuation.invokeOnCancellation {
@@ -158,37 +167,41 @@ suspend fun MoChallenge.verify(context: Context, subId: Int): ChallengeResponse
)
}
-private fun isActiveSubscription(context: Context, subscriptionId: Int): Boolean {
- if (subscriptionId == -1) return false
+private fun isActiveSubscription(context: Context, subId: Int): Boolean {
+ if (subId == -1) return true
+
return try {
- context.getSystemService()?.isActiveSubscriptionId(subscriptionId)
- ?: false
+ val subManager = context.getSystemService() ?: return false
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ subManager.isActiveSubscriptionId(subId)
+ } else {
+ subManager.getActiveSubscriptionInfo(subId) != null
+ }
} catch (e: Exception) {
- Log.w(TAG, "Failed to query active subscription for $subscriptionId", e)
+ Log.w(TAG, "Failed to query active subscription for $subId", e)
false
}
}
+@Suppress("DEPRECATION")
private fun resolveSmsManager(context: Context, subId: Int): SmsManager? {
if (subId != -1 && !isActiveSubscription(context, subId)) {
return null
}
+
return try {
- val manager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- context.getSystemService(SmsManager::class.java)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ val manager = context.getSystemService(SmsManager::class.java)
+ if (subId != -1) {
+ manager?.createForSubscriptionId(subId)
+ } else {
+ manager
+ }
} else {
- null
- }
- when {
- subId != -1 && manager != null -> manager.createForSubscriptionId(subId)
- manager != null -> manager
- subId != -1 -> {
- @Suppress("DEPRECATION")
+ if (subId != -1) {
SmsManager.getSmsManagerForSubscriptionId(subId)
- }
-
- else -> {
- @Suppress("DEPRECATION")
+ } else {
SmsManager.getDefault()
}
}
@@ -196,4 +209,4 @@ private fun resolveSmsManager(context: Context, subId: Int): SmsManager? {
Log.e(TAG, "Failed to resolve SmsManager for subId: $subId", e)
null
}
-}
+}
\ No newline at end of file
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
index ceb4395bb1..14e4e42f87 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
@@ -1,10 +1,14 @@
+@file:RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1)
+
package org.microg.gms.constellation.core.verification
import android.content.Context
+import android.os.Build
import android.provider.Telephony
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
+import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import okio.ByteString.Companion.toByteString
import org.microg.gms.constellation.core.VerificationSettingsPhenotypes
@@ -108,7 +112,7 @@ private fun getLocalNumbers(context: Context, targetSubId: Int): List {
try {
subscriptionManager?.activeSubscriptionInfoList.orEmpty().forEach { info ->
if (targetSubId != -1 && info.subscriptionId != targetSubId) return@forEach
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU && subscriptionManager != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && subscriptionManager != null) {
numbers += subscriptionManager.getPhoneNumber(
info.subscriptionId,
SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER
@@ -123,7 +127,7 @@ private fun getLocalNumbers(context: Context, targetSubId: Int): List {
)
}
val targetManager =
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
telephonyManager?.createForSubscriptionId(info.subscriptionId)
} else {
telephonyManager
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt
index ebaa6733ef..a43ef69f95 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/Ts43Verifier.kt
@@ -1,9 +1,14 @@
+@file:RequiresApi(Build.VERSION_CODES.O)
+@file:SuppressLint("HardwareIds")
+
package org.microg.gms.constellation.core.verification
+import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.telephony.TelephonyManager
import android.util.Log
+import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import okhttp3.Cookie
import okhttp3.CookieJar
@@ -46,7 +51,7 @@ private class InternalTs43Verifier(private val context: Context, private val sub
val tm = requireNotNull(context.getSystemService()) {
"TelephonyManager unavailable"
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && subId >= 0) {
+ if (subId >= 0) {
tm.createForSubscriptionId(subId)
} else tm
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
index 69e7e846cb..6fe7256578 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
@@ -1,7 +1,13 @@
+@file:RequiresApi(Build.VERSION_CODES.O)
+@file:SuppressLint("HardwareIds")
+
package org.microg.gms.constellation.core.verification.ts43
+import android.annotation.SuppressLint
import android.content.Context
+import android.os.Build
import android.telephony.TelephonyManager
+import androidx.annotation.RequiresApi
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.microg.gms.constellation.core.proto.OdsaOperation
From 52a58d4e9b54232b0e72e295a0f24b399f7ee507 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 27 Mar 2026 06:55:23 -0400
Subject: [PATCH 07/31] Fix the rest of the lints
---
.../core/proto/builders/SyncRequestBuilder.kt | 11 ++++++----
.../proto/builders/TelephonyInfoBuilder.kt | 1 +
.../core/verification/MoSmsVerifier.kt | 5 +++++
.../verification/RegisteredSmsVerifier.kt | 21 ++++++++++++++++---
.../ts43/ServiceEntitlementExtension.kt | 2 +-
5 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
index e2553dcb9b..2be034dba2 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
@@ -3,14 +3,17 @@
package org.microg.gms.constellation.core.proto.builders
+import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
+import android.content.pm.PackageManager
import android.os.Build
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import com.google.android.gms.constellation.VerifyPhoneNumberRequest
import com.google.android.gms.constellation.verificationMethods
@@ -42,11 +45,8 @@ fun buildImsiToSubscriptionInfoMap(context: Context): Map
- val subsTelephonyManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ val subsTelephonyManager =
telephonyManager.createForSubscriptionId(info.subscriptionId)
- } else {
- telephonyManager
- }
subsTelephonyManager.subscriberId?.let { imsi ->
map[imsi] = info
}
@@ -63,6 +63,9 @@ fun getTelephonyPhoneNumbers(
): List {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return emptyList()
+ val permissions = listOf(Manifest.permission.READ_PHONE_NUMBERS, Manifest.permission.READ_PHONE_STATE)
+ if (permissions.all { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_DENIED }) return emptyList()
+
val subscriptionManager =
context.getSystemService() ?: return emptyList()
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt
index fabda054a5..a66a562957 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt
@@ -22,6 +22,7 @@ import java.security.MessageDigest
private const val TAG = "TelephonyInfoBuilder"
+@SuppressLint("MissingPermission")
operator fun TelephonyInfo.Companion.invoke(context: Context, subscriptionId: Int): TelephonyInfo {
val tm = context.getSystemService()
val targetTm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && subscriptionId >= 0) {
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
index 5ff49b42c9..c1f746d109 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
@@ -170,6 +170,11 @@ suspend fun MoChallenge.verify(context: Context, subId: Int): ChallengeResponse
private fun isActiveSubscription(context: Context, subId: Int): Boolean {
if (subId == -1) return true
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_DENIED) {
+ Log.e(TAG, "Permission not granted")
+ return false
+ }
+
return try {
val subManager = context.getSystemService() ?: return false
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
index 14e4e42f87..aa8f8044b8 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
@@ -2,13 +2,17 @@
package org.microg.gms.constellation.core.verification
+import android.Manifest
+import android.annotation.SuppressLint
import android.content.Context
+import android.content.pm.PackageManager
import android.os.Build
import android.provider.Telephony
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import okio.ByteString.Companion.toByteString
import org.microg.gms.constellation.core.VerificationSettingsPhenotypes
@@ -90,9 +94,6 @@ fun RegisteredSmsChallenge.verify(context: Context, subId: Int): ChallengeRespon
}
}
}
- } catch (e: SecurityException) {
- Log.w(TAG, "SMS inbox access denied", e)
- return ChallengeResponse(registered_sms_response = RegisteredSmsChallengeResponse(items = emptyList()))
} catch (e: Exception) {
Log.e(TAG, "Registered SMS verification failed", e)
return ChallengeResponse(registered_sms_response = RegisteredSmsChallengeResponse(items = emptyList()))
@@ -103,12 +104,26 @@ fun RegisteredSmsChallenge.verify(context: Context, subId: Int): ChallengeRespon
)
}
+@SuppressLint("HardwareIds")
private fun getLocalNumbers(context: Context, targetSubId: Int): List {
val numbers = linkedSetOf()
val subscriptionManager =
context.getSystemService()
val telephonyManager = context.getSystemService()
+ val hasState = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
+ val isCarrier = telephonyManager?.hasCarrierPrivileges() == true
+ val hasNumbers = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_NUMBERS) == PackageManager.PERMISSION_GRANTED
+ } else {
+ hasState
+ }
+
+ if (!isCarrier && (!hasState || !hasNumbers)) {
+ Log.e(TAG, "Permission not granted")
+ return emptyList()
+ }
+
try {
subscriptionManager?.activeSubscriptionInfoList.orEmpty().forEach { info ->
if (targetSubId != -1 && info.subscriptionId != targetSubId) return@forEach
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
index 6fe7256578..56d8114022 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
@@ -1,5 +1,5 @@
@file:RequiresApi(Build.VERSION_CODES.O)
-@file:SuppressLint("HardwareIds")
+@file:SuppressLint("HardwareIds", "MissingPermission")
package org.microg.gms.constellation.core.verification.ts43
From 3917eeba97fa5bae9ec0ea164044e97f4bb463b3 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 27 Mar 2026 07:20:33 -0400
Subject: [PATCH 08/31] Convert parcelables into Java
---
.../gms/constellation/GetPnvCapabilities.kt | 9 +-
.../constellation/core/GetPnvCapabilities.kt | 7 +-
.../core/GetVerifiedPhoneNumbers.kt | 2 +-
.../core/VerificationMappings.kt | 2 +-
.../constellation/core/VerifyPhoneNumber.kt | 52 ++++----
.../gms/constellation/GetIidTokenRequest.java | 32 +++++
.../constellation/GetIidTokenResponse.java | 47 +++++++
.../GetPnvCapabilitiesRequest.java | 42 ++++++
.../GetPnvCapabilitiesResponse.java | 101 +++++++++++++++
.../gms/constellation/PhoneNumberInfo.java | 49 +++++++
.../VerifyPhoneNumberRequest.java | 120 ++++++++++++++++++
.../VerifyPhoneNumberResponse.java | 96 ++++++++++++++
.../android/gms/constellation/GetIidToken.kt | 43 -------
.../gms/constellation/GetPnvCapabilities.kt | 63 ---------
.../gms/constellation/PhoneNumberInfo.kt | 27 ----
.../gms/constellation/VerifyPhoneNumber.kt | 91 -------------
16 files changed, 520 insertions(+), 263 deletions(-)
create mode 100644 play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java
create mode 100644 play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java
create mode 100644 play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java
create mode 100644 play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java
create mode 100644 play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java
create mode 100644 play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
create mode 100644 play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java
delete mode 100644 play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetIidToken.kt
delete mode 100644 play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
delete mode 100644 play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt
delete mode 100644 play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
diff --git a/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt b/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
index 3bd89b61e0..79565ca454 100644
--- a/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
+++ b/play-services-constellation/core/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
@@ -1,5 +1,7 @@
package com.google.android.gms.constellation
+import com.google.android.gms.constellation.GetPnvCapabilitiesResponse.VerificationCapability
+
enum class VerificationStatus(val value: Int) {
SUPPORTED(1),
UNSUPPORTED_CARRIER(2),
@@ -16,14 +18,11 @@ enum class VerificationStatus(val value: Int) {
}
}
-operator fun VerificationCapability.Companion.invoke(
+fun verificationCapability(
verificationMethod: Int,
status: VerificationStatus
): VerificationCapability {
- return VerificationCapability(
- verificationMethod = verificationMethod,
- statusValue = status.value
- )
+ return VerificationCapability(verificationMethod, status.value)
}
val VerificationCapability.status: VerificationStatus
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilities.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilities.kt
index 72d4617f2e..5aeacfcf12 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilities.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetPnvCapabilities.kt
@@ -13,11 +13,10 @@ import com.google.android.gms.common.api.ApiMetadata
import com.google.android.gms.common.api.Status
import com.google.android.gms.constellation.GetPnvCapabilitiesRequest
import com.google.android.gms.constellation.GetPnvCapabilitiesResponse
-import com.google.android.gms.constellation.SimCapability
-import com.google.android.gms.constellation.VerificationCapability
+import com.google.android.gms.constellation.GetPnvCapabilitiesResponse.SimCapability
import com.google.android.gms.constellation.VerificationStatus
import com.google.android.gms.constellation.internal.IConstellationCallbacks
-import com.google.android.gms.constellation.invoke
+import com.google.android.gms.constellation.verificationCapability
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.security.MessageDigest
@@ -57,7 +56,7 @@ suspend fun handleGetPnvCapabilities(
// GMS hardcodes public verification method 9 for the Firebase PNV TS43 capability path.
val verificationCapabilities = if (9 in request.verificationTypes) {
listOf(
- VerificationCapability(
+ verificationCapability(
9,
when {
!GetPnvCapabilitiesApiPhenotype.FPNV_ALLOWED_CARRIER_IDS.contains(
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
index e02b3565fa..b344d37d86 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
@@ -9,7 +9,7 @@ import android.util.Log
import com.google.android.gms.common.api.ApiMetadata
import com.google.android.gms.common.api.Status
import com.google.android.gms.constellation.PhoneNumberInfo
-import com.google.android.gms.constellation.PhoneNumberVerification
+import com.google.android.gms.constellation.VerifyPhoneNumberResponse.PhoneNumberVerification
import com.google.android.gms.constellation.VerifyPhoneNumberResponse
import com.google.android.gms.constellation.internal.IConstellationCallbacks
import kotlinx.coroutines.Dispatchers
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
index 6102ca047a..2fae9c903b 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
@@ -3,7 +3,7 @@ package org.microg.gms.constellation.core
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi
-import com.google.android.gms.constellation.PhoneNumberVerification
+import com.google.android.gms.constellation.VerifyPhoneNumberResponse.PhoneNumberVerification
import org.microg.gms.constellation.core.proto.Param
import org.microg.gms.constellation.core.proto.UnverifiedInfo
import org.microg.gms.constellation.core.proto.Verification
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
index e456fb00c4..32c5bd82d8 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
@@ -10,9 +10,9 @@ import android.util.Log
import androidx.annotation.RequiresApi
import com.google.android.gms.common.api.ApiMetadata
import com.google.android.gms.common.api.Status
-import com.google.android.gms.constellation.IdTokenRequest
+import com.google.android.gms.constellation.VerifyPhoneNumberRequest.IdTokenRequest
import com.google.android.gms.constellation.PhoneNumberInfo
-import com.google.android.gms.constellation.PhoneNumberVerification
+import com.google.android.gms.constellation.VerifyPhoneNumberResponse.PhoneNumberVerification
import com.google.android.gms.constellation.VerifyPhoneNumberRequest
import com.google.android.gms.constellation.VerifyPhoneNumberResponse
import com.google.android.gms.constellation.internal.IConstellationCallbacks
@@ -54,17 +54,17 @@ suspend fun handleVerifyPhoneNumberV1(
else -> 300L
}
val request = VerifyPhoneNumberRequest(
- policyId = extras.getString("policy_id", ""),
- timeout = timeout,
- idTokenRequest = IdTokenRequest(
+ extras.getString("policy_id", ""),
+ timeout,
+ IdTokenRequest(
extras.getString("certificate_hash", ""),
extras.getString("token_nonce", "")
),
- extras = extras,
- targetedSims = emptyList(),
- silent = false,
- apiVersion = 2,
- verificationMethodsValues = emptyList()
+ extras,
+ emptyList(),
+ false,
+ 2,
+ emptyList()
)
val policyId = bundle.getString("policy_id", "")
val mode = bundle.getInt("verification_mode", 0)
@@ -103,17 +103,17 @@ suspend fun handleVerifyPhoneNumberSingleUse(
else -> 300L
}
val request = VerifyPhoneNumberRequest(
- policyId = extras.getString("policy_id", ""),
- timeout = timeout,
- idTokenRequest = IdTokenRequest(
+ extras.getString("policy_id", ""),
+ timeout,
+ IdTokenRequest(
extras.getString("certificate_hash", ""),
extras.getString("token_nonce", "")
),
- extras = extras,
- targetedSims = emptyList(),
- silent = false,
- apiVersion = 2,
- verificationMethodsValues = emptyList()
+ extras,
+ emptyList(),
+ false,
+ 2,
+ emptyList()
)
handleVerifyPhoneNumberRequest(
@@ -133,25 +133,21 @@ suspend fun handleVerifyPhoneNumberRequest(
packageName: String?
) {
val callingPackage = packageName ?: context.packageName
- val requestWithExtras = request.copy(
- extras = Bundle(request.extras).apply {
- putString("calling_api", "verifyPhoneNumber")
- }
- )
- val useReadPath = when (requestWithExtras.apiVersion) {
- 0 -> requestWithExtras.policyId in VerifyPhoneNumberApiPhenotypes.READ_ONLY_POLICY_IDS
+ request.extras.putString("calling_api", "verifyPhoneNumber")
+ val useReadPath = when (request.apiVersion) {
+ 0 -> request.policyId in VerifyPhoneNumberApiPhenotypes.READ_ONLY_POLICY_IDS
2 -> VerifyPhoneNumberApiPhenotypes.ENABLE_READ_FLOW
- 3 -> requestWithExtras.policyId in VerifyPhoneNumberApiPhenotypes.POLICY_IDS_ALLOWED_FOR_LOCAL_READ
+ 3 -> request.policyId in VerifyPhoneNumberApiPhenotypes.POLICY_IDS_ALLOWED_FOR_LOCAL_READ
else -> false
}
handleVerifyPhoneNumberRequest(
context,
callbacks,
- requestWithExtras,
+ request,
callingPackage,
if (useReadPath) ReadCallbackMode.TYPED else ReadCallbackMode.NONE,
- localReadFallback = requestWithExtras.apiVersion == 3 && useReadPath,
+ localReadFallback = request.apiVersion == 3 && useReadPath,
legacyCallbackOnFullFlow = false
)
}
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java
new file mode 100644
index 0000000000..5467842558
--- /dev/null
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java
@@ -0,0 +1,32 @@
+package com.google.android.gms.constellation;
+
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
+
+@SafeParcelable.Class
+public class GetIidTokenRequest extends AbstractSafeParcelable {
+ @Field(1)
+ @Nullable
+ public final Long projectNumber;
+
+ @Constructor
+ public GetIidTokenRequest(
+ @Param(1) @Nullable Long projectNumber
+ ) {
+ this.projectNumber = projectNumber;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(GetIidTokenRequest.class);
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java
new file mode 100644
index 0000000000..0c58b7238b
--- /dev/null
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java
@@ -0,0 +1,47 @@
+package com.google.android.gms.constellation;
+
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
+
+@SafeParcelable.Class
+public class GetIidTokenResponse extends AbstractSafeParcelable {
+ @Field(1)
+ public final String iidToken;
+
+ @Field(2)
+ public final String fid;
+
+ @Field(3)
+ @Nullable
+ public final byte[] signature;
+
+ @Field(4)
+ public final long timestamp;
+
+ @Constructor
+ public GetIidTokenResponse(
+ @Param(1) String iidToken,
+ @Param(2) String fid,
+ @Param(3) @Nullable byte[] signature,
+ @Param(4) long timestamp
+ ) {
+ this.iidToken = iidToken;
+ this.fid = fid;
+ this.signature = signature;
+ this.timestamp = timestamp;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(GetIidTokenResponse.class);
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java
new file mode 100644
index 0000000000..aeafd2b357
--- /dev/null
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java
@@ -0,0 +1,42 @@
+package com.google.android.gms.constellation;
+
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
+
+import java.util.List;
+
+@SafeParcelable.Class
+public class GetPnvCapabilitiesRequest extends AbstractSafeParcelable {
+ @Field(1)
+ public final String policyId;
+
+ @Field(2)
+ public final List verificationTypes;
+
+ @Field(3)
+ public final List simSlotIndices;
+
+ @Constructor
+ public GetPnvCapabilitiesRequest(
+ @Param(1) String policyId,
+ @Param(2) List verificationTypes,
+ @Param(3) List simSlotIndices
+ ) {
+ this.policyId = policyId;
+ this.verificationTypes = verificationTypes;
+ this.simSlotIndices = simSlotIndices;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(GetPnvCapabilitiesRequest.class);
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java
new file mode 100644
index 0000000000..e5dea8c34c
--- /dev/null
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java
@@ -0,0 +1,101 @@
+package com.google.android.gms.constellation;
+
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
+
+import java.util.List;
+
+@SafeParcelable.Class
+public class GetPnvCapabilitiesResponse extends AbstractSafeParcelable {
+ @Field(1)
+ public final List simCapabilities;
+
+ @Constructor
+ public GetPnvCapabilitiesResponse(
+ @Param(1) List simCapabilities
+ ) {
+ this.simCapabilities = simCapabilities;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(GetPnvCapabilitiesResponse.class);
+
+ @SafeParcelable.Class
+ public static class SimCapability extends AbstractSafeParcelable {
+
+ @Field(1)
+ public final int slotValue;
+
+ @Field(2)
+ public final String subscriberIdDigest;
+
+ @Field(3)
+ public final int carrierId;
+
+ @Field(4)
+ public final String operatorName;
+
+ @Field(5)
+ public final List verificationCapabilities;
+
+ @Constructor
+ public SimCapability(
+ @Param(1) int slotValue,
+ @Param(2) String subscriberIdDigest,
+ @Param(3) int carrierId,
+ @Param(4) String operatorName,
+ @Param(5) List verificationCapabilities
+ ) {
+ this.slotValue = slotValue;
+ this.subscriberIdDigest = subscriberIdDigest;
+ this.carrierId = carrierId;
+ this.operatorName = operatorName;
+ this.verificationCapabilities = verificationCapabilities;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(SimCapability.class);
+ }
+
+ @SafeParcelable.Class
+ public static class VerificationCapability extends AbstractSafeParcelable {
+
+ @Field(1)
+ public final int verificationMethod;
+
+ @Field(2)
+ public final int statusValue;
+
+ @Constructor
+ public VerificationCapability(
+ @Param(1) int verificationMethod,
+ @Param(2) int statusValue
+ ) {
+ this.verificationMethod = verificationMethod;
+ this.statusValue = statusValue;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(VerificationCapability.class);
+ }
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java
new file mode 100644
index 0000000000..82b4ce0268
--- /dev/null
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java
@@ -0,0 +1,49 @@
+package com.google.android.gms.constellation;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
+
+@SafeParcelable.Class
+public class PhoneNumberInfo extends AbstractSafeParcelable {
+ @Field(1)
+ public final int version;
+
+ @Field(2)
+ @Nullable
+ public final String phoneNumber;
+
+ @Field(3)
+ public final long verificationTime;
+
+ @Field(4)
+ @Nullable
+ public final Bundle extras;
+
+ @Constructor
+ public PhoneNumberInfo(
+ @SafeParcelable.Param(1) int version,
+ @SafeParcelable.Param(2) @Nullable String phoneNumber,
+ @SafeParcelable.Param(3) long verificationTime,
+ @SafeParcelable.Param(4) @Nullable Bundle extras
+ ) {
+ this.version = version;
+ this.phoneNumber = phoneNumber;
+ this.verificationTime = verificationTime;
+ this.extras = extras;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(PhoneNumberInfo.class);
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
new file mode 100644
index 0000000000..e554934ae6
--- /dev/null
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
@@ -0,0 +1,120 @@
+package com.google.android.gms.constellation;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
+
+import java.util.List;
+
+@SafeParcelable.Class
+public class VerifyPhoneNumberRequest extends AbstractSafeParcelable {
+ @Field(1)
+ public final String policyId;
+
+ @Field(2)
+ public final long timeout;
+
+ @Field(3)
+ public final IdTokenRequest idTokenRequest;
+
+ @Field(4)
+ public final Bundle extras;
+
+ @Field(5)
+ public final List targetedSims;
+
+ @Field(6)
+ public final boolean silent;
+
+ @Field(7)
+ public final int apiVersion;
+
+ @Field(8)
+ public final List verificationMethodsValues;
+
+ @Constructor
+ public VerifyPhoneNumberRequest(
+ @Param(1) String policyId,
+ @Param(2) long timeout,
+ @Param(3) IdTokenRequest idTokenRequest,
+ @Param(4) Bundle extras,
+ @Param(5) List targetedSims,
+ @Param(6) boolean silent,
+ @Param(7) int apiVersion,
+ @Param(8) List verificationMethodsValues
+ ) {
+ this.policyId = policyId;
+ this.timeout = timeout;
+ this.idTokenRequest = idTokenRequest;
+ this.extras = extras;
+ this.targetedSims = targetedSims;
+ this.silent = silent;
+ this.apiVersion = apiVersion;
+ this.verificationMethodsValues = verificationMethodsValues;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(VerifyPhoneNumberRequest.class);
+
+ @SafeParcelable.Class
+ public static class IdTokenRequest extends AbstractSafeParcelable {
+ @Field(1)
+ public final String idToken;
+
+ @Field(2)
+ public final String subscriberHash;
+
+ @Constructor
+ public IdTokenRequest(
+ @Param(1) String idToken,
+ @Param(2) String subscriberHash
+ ) {
+ this.idToken = idToken;
+ this.subscriberHash = subscriberHash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(IdTokenRequest.class);
+ }
+
+ @SafeParcelable.Class
+ public static class ImsiRequest extends AbstractSafeParcelable {
+ @Field(1)
+ public final String imsi;
+
+ @Field(2)
+ public final String phoneNumberHint;
+
+ @Constructor
+ public ImsiRequest(
+ @Param(1) String imsi,
+ @Param(2) String phoneNumberHint
+ ) {
+ this.imsi = imsi;
+ this.phoneNumberHint = phoneNumberHint;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(ImsiRequest.class);
+ }
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java
new file mode 100644
index 0000000000..4a94badd74
--- /dev/null
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java
@@ -0,0 +1,96 @@
+package com.google.android.gms.constellation;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
+import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
+
+@SafeParcelable.Class
+public class VerifyPhoneNumberResponse extends AbstractSafeParcelable {
+ @Field(1)
+ public final PhoneNumberVerification[] verifications;
+
+ @Field(2)
+ public final Bundle extras;
+
+ @Constructor
+ public VerifyPhoneNumberResponse(
+ @Param(1) PhoneNumberVerification[] verifications,
+ @Param(2) Bundle extras
+ ) {
+ this.verifications = verifications;
+ this.extras = extras;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(VerifyPhoneNumberResponse.class);
+
+ @SafeParcelable.Class
+ public static class PhoneNumberVerification extends AbstractSafeParcelable {
+ @Field(1)
+ @Nullable
+ public final String phoneNumber;
+
+ @Field(2)
+ public final long timestampMillis;
+
+ @Field(3)
+ public final int verificationMethod;
+
+ @Field(4)
+ public final int simSlot;
+
+ @Field(5)
+ @Nullable
+ public final String verificationToken;
+
+ @Field(6)
+ @Nullable
+ public final Bundle extras;
+
+ @Field(7)
+ public final int verificationStatus;
+
+ @Field(8)
+ public final long retryAfterSeconds;
+
+ @Constructor
+ public PhoneNumberVerification(
+ @Param(1) @Nullable String phoneNumber,
+ @Param(2) long timestampMillis,
+ @Param(3) int verificationMethod,
+ @Param(4) int simSlot,
+ @Param(5) @Nullable String verificationToken,
+ @Param(6) @Nullable Bundle extras,
+ @Param(7) int verificationStatus,
+ @Param(8) long retryAfterSeconds
+ ) {
+ this.phoneNumber = phoneNumber;
+ this.timestampMillis = timestampMillis;
+ this.verificationMethod = verificationMethod;
+ this.simSlot = simSlot;
+ this.verificationToken = verificationToken;
+ this.extras = extras;
+ this.verificationStatus = verificationStatus;
+ this.retryAfterSeconds = retryAfterSeconds;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ CREATOR.writeToParcel(this, dest, flags);
+ }
+
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(PhoneNumberVerification.class);
+ }
+}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetIidToken.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetIidToken.kt
deleted file mode 100644
index 53760184ab..0000000000
--- a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetIidToken.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.google.android.gms.constellation
-
-import android.os.Parcel
-import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Constructor
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
-import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter
-
-@SafeParcelable.Class
-data class GetIidTokenRequest @Constructor constructor(
- @JvmField @Param(1) @Field(1) val projectNumber: Long?
-) : AbstractSafeParcelable() {
- override fun writeToParcel(out: Parcel, flags: Int) {
- CREATOR.writeToParcel(this, out, flags)
- }
-
- companion object {
- @JvmField
- val CREATOR: SafeParcelableCreatorAndWriter =
- findCreator(GetIidTokenRequest::class.java)
- }
-}
-
-@SafeParcelable.Class
-data class GetIidTokenResponse @Constructor constructor(
- @JvmField @Param(1) @Field(1) val iidToken: String,
- @JvmField @Param(2) @Field(2) val fid: String,
- @JvmField @Param(3) @Field(value = 3, type = "byte[]") val signature: ByteArray?,
- @JvmField @Param(4) @Field(4) val timestamp: Long
-) : AbstractSafeParcelable() {
-
- override fun writeToParcel(out: Parcel, flags: Int) {
- CREATOR.writeToParcel(this, out, flags)
- }
-
- companion object {
- @JvmField
- val CREATOR: SafeParcelableCreatorAndWriter =
- findCreator(GetIidTokenResponse::class.java)
- }
-}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
deleted file mode 100644
index fe80003950..0000000000
--- a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/GetPnvCapabilities.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.google.android.gms.constellation
-
-import android.os.Parcel
-import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Constructor
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
-
-@SafeParcelable.Class
-data class GetPnvCapabilitiesRequest @Constructor constructor(
- @JvmField @Param(1) @Field(1) val policyId: String,
- @JvmField @Param(2) @Field(2) val verificationTypes: List,
- @JvmField @Param(3) @Field(3) val simSlotIndices: List
-) : AbstractSafeParcelable() {
- override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
-
- companion object {
- @JvmField
- val CREATOR = findCreator(GetPnvCapabilitiesRequest::class.java)
- }
-}
-
-@SafeParcelable.Class
-data class GetPnvCapabilitiesResponse @Constructor constructor(
- @JvmField @Param(1) @Field(1) val simCapabilities: List
-) : AbstractSafeParcelable() {
- override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
-
- companion object {
- @JvmField
- val CREATOR = findCreator(GetPnvCapabilitiesResponse::class.java)
- }
-}
-
-@SafeParcelable.Class
-data class SimCapability @Constructor constructor(
- @JvmField @Param(1) @Field(1) val slotValue: Int,
- @JvmField @Param(2) @Field(2) val subscriberIdDigest: String,
- @JvmField @Param(3) @Field(3) val carrierId: Int,
- @JvmField @Param(4) @Field(4) val operatorName: String,
- @JvmField @Param(5) @Field(5) val verificationCapabilities: List
-) : AbstractSafeParcelable() {
- override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
-
- companion object {
- @JvmField
- val CREATOR = findCreator(SimCapability::class.java)
- }
-}
-
-@SafeParcelable.Class
-data class VerificationCapability @Constructor constructor(
- @JvmField @Param(1) @Field(1) val verificationMethod: Int,
- @JvmField @Param(2) @Field(2) val statusValue: Int
-) : AbstractSafeParcelable() {
- override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
-
- companion object {
- @JvmField
- val CREATOR = findCreator(VerificationCapability::class.java)
- }
-}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt
deleted file mode 100644
index f16110f7f9..0000000000
--- a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/PhoneNumberInfo.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.google.android.gms.constellation
-
-import android.os.Bundle
-import android.os.Parcel
-import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Constructor
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
-
-@SafeParcelable.Class
-data class PhoneNumberInfo @Constructor constructor(
- @JvmField @Param(1) @Field(1) val version: Int,
- @JvmField @Param(2) @Field(2) val phoneNumber: String?,
- @JvmField @Param(3) @Field(3) val verificationTime: Long,
- @JvmField @Param(4) @Field(4) val extras: Bundle?
-) : AbstractSafeParcelable() {
-
- override fun writeToParcel(out: Parcel, flags: Int) {
- CREATOR.writeToParcel(this, out, flags)
- }
-
- companion object {
- @JvmField
- val CREATOR = findCreator(PhoneNumberInfo::class.java)
- }
-}
diff --git a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt b/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
deleted file mode 100644
index c2a0ef8115..0000000000
--- a/play-services-constellation/src/main/kotlin/com/google/android/gms/constellation/VerifyPhoneNumber.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-package com.google.android.gms.constellation
-
-import android.os.Bundle
-import android.os.Parcel
-import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Constructor
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Field
-import com.google.android.gms.common.internal.safeparcel.SafeParcelable.Param
-import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter
-
-@SafeParcelable.Class
-data class VerifyPhoneNumberRequest @Constructor constructor(
- @JvmField @Param(1) @Field(1) val policyId: String,
- @JvmField @Param(2) @Field(2) val timeout: Long,
- @JvmField @Param(3) @Field(3) val idTokenRequest: IdTokenRequest,
- @JvmField @Param(4) @Field(4) val extras: Bundle,
- @JvmField @Param(5) @Field(5) val targetedSims: List,
- @JvmField @Param(6) @Field(6) val silent: Boolean,
- @JvmField @Param(7) @Field(7) val apiVersion: Int,
- @JvmField @Param(8) @Field(8) val verificationMethodsValues: List
-) : AbstractSafeParcelable() {
- override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
-
- companion object {
- @JvmField
- val CREATOR = findCreator(VerifyPhoneNumberRequest::class.java)
- }
-}
-
-@SafeParcelable.Class
-data class IdTokenRequest @Constructor constructor(
- @JvmField @Param(1) @Field(1) val idToken: String,
- @JvmField @Param(2) @Field(2) val subscriberHash: String
-) : AbstractSafeParcelable() {
-
- override fun writeToParcel(out: Parcel, flags: Int) {
- CREATOR.writeToParcel(this, out, flags)
- }
-
- companion object {
- @JvmField
- val CREATOR: SafeParcelableCreatorAndWriter =
- findCreator(IdTokenRequest::class.java)
- }
-}
-
-@SafeParcelable.Class
-data class ImsiRequest @Constructor constructor(
- @JvmField @Param(1) @Field(1) val imsi: String,
- @JvmField @Param(2) @Field(2) val phoneNumberHint: String
-) : AbstractSafeParcelable() {
- override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
-
- companion object {
- @JvmField
- val CREATOR = findCreator(ImsiRequest::class.java)
- }
-}
-
-@SafeParcelable.Class
-data class VerifyPhoneNumberResponse @Constructor constructor(
- @JvmField @Param(1) @Field(1) val verifications: Array,
- @JvmField @Param(2) @Field(2) val extras: Bundle
-) : AbstractSafeParcelable() {
- override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
-
- companion object {
- @JvmField
- val CREATOR = findCreator(VerifyPhoneNumberResponse::class.java)
- }
-}
-
-@SafeParcelable.Class
-data class PhoneNumberVerification @Constructor constructor(
- @JvmField @Param(1) @Field(1) val phoneNumber: String?,
- @JvmField @Param(2) @Field(2) val timestampMillis: Long,
- @JvmField @Param(3) @Field(3) val verificationMethod: Int,
- @JvmField @Param(4) @Field(4) val simSlot: Int,
- @JvmField @Param(5) @Field(5) val verificationToken: String?,
- @JvmField @Param(6) @Field(6) val extras: Bundle?,
- @JvmField @Param(7) @Field(7) val verificationStatus: Int,
- @JvmField @Param(8) @Field(8) val retryAfterSeconds: Long
-) : AbstractSafeParcelable() {
- override fun writeToParcel(out: Parcel, flags: Int) = CREATOR.writeToParcel(this, out, flags)
-
- companion object {
- @JvmField
- val CREATOR = findCreator(PhoneNumberVerification::class.java)
- }
-}
\ No newline at end of file
From aec46b8221eaf9a7e419bb65c426bec10870737c Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 27 Mar 2026 07:42:52 -0400
Subject: [PATCH 09/31] Small fixes
---
.../gms/constellation/core/AuthManager.kt | 2 +-
.../core/GetVerifiedPhoneNumbers.kt | 2 +-
.../constellation/core/IidTokenPhenotypes.kt | 10 +++++-----
.../core/proto/builders/GaiaInfoBuilder.kt | 19 ++++++++++---------
4 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
index ba601c3832..6f2a151427 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/AuthManager.kt
@@ -56,7 +56,7 @@ class AuthManager private constructor(context: Context) {
fun getIidToken(projectNumber: String? = null): String {
return try {
- val sender = projectNumber ?: IidTokenPhenotypes.DEFAULT_PROJECT_NUMBER.toString()
+ val sender = projectNumber ?: IidTokenPhenotypes.DEFAULT_PROJECT_NUMBER
InstanceID.getInstance(context).getToken(sender, "GCM")
} catch (_: Exception) {
""
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
index b344d37d86..224e7a4f9a 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
@@ -54,7 +54,7 @@ internal suspend fun fetchVerifiedPhoneNumbers(
val certificateHash = bundle.getString("certificate_hash") ?: ""
val tokenNonce = bundle.getString("token_nonce") ?: ""
- val iidToken = authManager.getIidToken()
+ val iidToken = authManager.getIidToken(IidTokenPhenotypes.READ_ONLY_PROJECT_NUMBER)
val iidTokenAuth = if (VerifyPhoneNumberApiPhenotypes.ENABLE_CLIENT_SIGNATURE) {
val (signatureBytes, signTimestamp) = authManager.signIidToken(iidToken)
IIDTokenAuth(
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/IidTokenPhenotypes.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/IidTokenPhenotypes.kt
index 984c902321..4b42087420 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/IidTokenPhenotypes.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/IidTokenPhenotypes.kt
@@ -1,9 +1,9 @@
package org.microg.gms.constellation.core
object IidTokenPhenotypes {
- const val ASTERISM_PROJECT_NUMBER = 496232013492
- const val DEFAULT_PROJECT_NUMBER = 496232013492
- const val EXTERNAL_CONSENT_ACTIVITY_PROJECT_NUMBER = 496232013492
- const val MESSAGES_PROJECT_NUMBER = 496232013492
- const val READ_ONLY_PROJECT_NUMBER = 745476177629
+ const val ASTERISM_PROJECT_NUMBER = "496232013492"
+ const val DEFAULT_PROJECT_NUMBER = "496232013492"
+ const val EXTERNAL_CONSENT_ACTIVITY_PROJECT_NUMBER = "496232013492"
+ const val MESSAGES_PROJECT_NUMBER = "496232013492"
+ const val READ_ONLY_PROJECT_NUMBER = "745476177629"
}
\ No newline at end of file
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
index 60ff924562..bf76f353e6 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
@@ -55,12 +55,11 @@ operator fun GaiaSignals.Companion.invoke(context: Context): GaiaSignals? {
@Suppress("DEPRECATION")
suspend fun GaiaToken.Companion.getList(context: Context): List =
withContext(Dispatchers.IO) {
- val gaiaTokens = mutableListOf()
- try {
- val accounts = AccountManager.get(context).getAccountsByType("com.google")
- if (accounts.isNotEmpty()) {
+ val accounts = AccountManager.get(context).getAccountsByType("com.google")
+ accounts.mapNotNull { account ->
+ try {
val future = AccountManager.get(context).getAuthToken(
- accounts.first(),
+ account,
"oauth2:https://www.googleapis.com/auth/numberer",
null,
false,
@@ -68,10 +67,12 @@ suspend fun GaiaToken.Companion.getList(context: Context): List =
null
)
val token = future.result?.getString(AccountManager.KEY_AUTHTOKEN)
- if (!token.isNullOrBlank()) gaiaTokens.add(GaiaToken(token = token))
+
+ if (!token.isNullOrBlank()) GaiaToken(token = token) else null
+
+ } catch (e: Exception) {
+ Log.w(TAG, "Could not retrieve Gaia token for an account", e)
+ null
}
- } catch (e: Exception) {
- Log.w(TAG, "Could not retrieve Gaia tokens", e)
}
- gaiaTokens
}
From 23e80ba63c1028c896afbd0b8f2ee81ce65b2dbe Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 27 Mar 2026 07:47:27 -0400
Subject: [PATCH 10/31] Formatting
---
.../core/src/main/AndroidManifest.xml | 9 +++----
.../core/GetVerifiedPhoneNumbers.kt | 5 ++--
.../constellation/core/VerifyPhoneNumber.kt | 6 +++--
.../core/proto/builders/CommonBuilders.kt | 1 -
.../core/proto/builders/GaiaInfoBuilder.kt | 1 -
.../core/proto/builders/SyncRequestBuilder.kt | 11 +++++++--
.../core/verification/MoSmsVerifier.kt | 6 ++++-
.../verification/RegisteredSmsVerifier.kt | 11 +++++++--
.../gms/constellation/GetIidTokenRequest.java | 5 ++--
.../constellation/GetIidTokenResponse.java | 8 ++-----
.../GetPnvCapabilitiesRequest.java | 7 ++----
.../GetPnvCapabilitiesResponse.java | 20 +++++-----------
.../gms/constellation/PhoneNumberInfo.java | 8 ++-----
.../VerifyPhoneNumberRequest.java | 24 +++++--------------
.../VerifyPhoneNumberResponse.java | 18 ++++----------
15 files changed, 59 insertions(+), 81 deletions(-)
diff --git a/play-services-constellation/core/src/main/AndroidManifest.xml b/play-services-constellation/core/src/main/AndroidManifest.xml
index bd95282658..8ad6d5d3fe 100644
--- a/play-services-constellation/core/src/main/AndroidManifest.xml
+++ b/play-services-constellation/core/src/main/AndroidManifest.xml
@@ -6,16 +6,17 @@
+
-
-
-
+
+
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
index 224e7a4f9a..28f5df0669 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
@@ -1,4 +1,5 @@
@file:SuppressLint("NewApi")
+
package org.microg.gms.constellation.core
import android.annotation.SuppressLint
@@ -9,8 +10,8 @@ import android.util.Log
import com.google.android.gms.common.api.ApiMetadata
import com.google.android.gms.common.api.Status
import com.google.android.gms.constellation.PhoneNumberInfo
-import com.google.android.gms.constellation.VerifyPhoneNumberResponse.PhoneNumberVerification
import com.google.android.gms.constellation.VerifyPhoneNumberResponse
+import com.google.android.gms.constellation.VerifyPhoneNumberResponse.PhoneNumberVerification
import com.google.android.gms.constellation.internal.IConstellationCallbacks
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -19,8 +20,8 @@ import org.microg.gms.constellation.core.proto.GetVerifiedPhoneNumbersRequest
import org.microg.gms.constellation.core.proto.GetVerifiedPhoneNumbersRequest.PhoneNumberSelection
import org.microg.gms.constellation.core.proto.IIDTokenAuth
import org.microg.gms.constellation.core.proto.TokenOption
-import java.util.UUID
import org.microg.gms.constellation.core.proto.VerifiedPhoneNumber
+import java.util.UUID
private const val TAG = "GetVerifiedPhoneNumbers"
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
index 32c5bd82d8..cf5871186e 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
@@ -10,11 +10,11 @@ import android.util.Log
import androidx.annotation.RequiresApi
import com.google.android.gms.common.api.ApiMetadata
import com.google.android.gms.common.api.Status
-import com.google.android.gms.constellation.VerifyPhoneNumberRequest.IdTokenRequest
import com.google.android.gms.constellation.PhoneNumberInfo
-import com.google.android.gms.constellation.VerifyPhoneNumberResponse.PhoneNumberVerification
import com.google.android.gms.constellation.VerifyPhoneNumberRequest
+import com.google.android.gms.constellation.VerifyPhoneNumberRequest.IdTokenRequest
import com.google.android.gms.constellation.VerifyPhoneNumberResponse
+import com.google.android.gms.constellation.VerifyPhoneNumberResponse.PhoneNumberVerification
import com.google.android.gms.constellation.internal.IConstellationCallbacks
import com.squareup.wire.GrpcException
import com.squareup.wire.GrpcStatus
@@ -37,6 +37,7 @@ private enum class ReadCallbackMode {
TYPED
}
+@Suppress("DEPRECATION")
suspend fun handleVerifyPhoneNumberV1(
context: Context,
callbacks: IConstellationCallbacks,
@@ -85,6 +86,7 @@ suspend fun handleVerifyPhoneNumberV1(
)
}
+@Suppress("DEPRECATION")
suspend fun handleVerifyPhoneNumberSingleUse(
context: Context,
callbacks: IConstellationCallbacks,
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
index ae7e49ba83..d3a4e4beb5 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
@@ -1,4 +1,3 @@
-
package org.microg.gms.constellation.core.proto.builders
import android.content.Context
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
index bf76f353e6..4c6dd5987d 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
@@ -1,4 +1,3 @@
-
package org.microg.gms.constellation.core.proto.builders
import android.accounts.AccountManager
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
index 2be034dba2..3d1dca1acf 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
@@ -63,8 +63,14 @@ fun getTelephonyPhoneNumbers(
): List {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return emptyList()
- val permissions = listOf(Manifest.permission.READ_PHONE_NUMBERS, Manifest.permission.READ_PHONE_STATE)
- if (permissions.all { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_DENIED }) return emptyList()
+ val permissions =
+ listOf(Manifest.permission.READ_PHONE_NUMBERS, Manifest.permission.READ_PHONE_STATE)
+ if (permissions.all {
+ ContextCompat.checkSelfPermission(
+ context,
+ it
+ ) == PackageManager.PERMISSION_DENIED
+ }) return emptyList()
val subscriptionManager =
context.getSystemService() ?: return emptyList()
@@ -122,6 +128,7 @@ suspend operator fun SyncRequest.Companion.invoke(
)
}
+@Suppress("DEPRECATION")
suspend operator fun SyncRequest.Companion.invoke(
context: Context,
sessionId: String,
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
index c1f746d109..2ab7176df7 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MoSmsVerifier.kt
@@ -170,7 +170,11 @@ suspend fun MoChallenge.verify(context: Context, subId: Int): ChallengeResponse
private fun isActiveSubscription(context: Context, subId: Int): Boolean {
if (subId == -1) return true
- if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_DENIED) {
+ if (ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.READ_PHONE_STATE
+ ) == PackageManager.PERMISSION_DENIED
+ ) {
Log.e(TAG, "Permission not granted")
return false
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
index aa8f8044b8..b010277b41 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/RegisteredSmsVerifier.kt
@@ -105,16 +105,23 @@ fun RegisteredSmsChallenge.verify(context: Context, subId: Int): ChallengeRespon
}
@SuppressLint("HardwareIds")
+@Suppress("DEPRECATION")
private fun getLocalNumbers(context: Context, targetSubId: Int): List {
val numbers = linkedSetOf()
val subscriptionManager =
context.getSystemService()
val telephonyManager = context.getSystemService()
- val hasState = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
+ val hasState = ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.READ_PHONE_STATE
+ ) == PackageManager.PERMISSION_GRANTED
val isCarrier = telephonyManager?.hasCarrierPrivileges() == true
val hasNumbers = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_NUMBERS) == PackageManager.PERMISSION_GRANTED
+ ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.READ_PHONE_NUMBERS
+ ) == PackageManager.PERMISSION_GRANTED
} else {
hasState
}
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java
index 5467842558..311dcb0fd9 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java
@@ -11,6 +11,8 @@
@SafeParcelable.Class
public class GetIidTokenRequest extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(GetIidTokenRequest.class);
@Field(1)
@Nullable
public final Long projectNumber;
@@ -26,7 +28,4 @@ public GetIidTokenRequest(
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
-
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(GetIidTokenRequest.class);
}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java
index 0c58b7238b..fd2a3c66d6 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java
@@ -11,16 +11,15 @@
@SafeParcelable.Class
public class GetIidTokenResponse extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(GetIidTokenResponse.class);
@Field(1)
public final String iidToken;
-
@Field(2)
public final String fid;
-
@Field(3)
@Nullable
public final byte[] signature;
-
@Field(4)
public final long timestamp;
@@ -41,7 +40,4 @@ public GetIidTokenResponse(
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
-
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(GetIidTokenResponse.class);
}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java
index aeafd2b357..9fbc98b5c0 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java
@@ -12,12 +12,12 @@
@SafeParcelable.Class
public class GetPnvCapabilitiesRequest extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(GetPnvCapabilitiesRequest.class);
@Field(1)
public final String policyId;
-
@Field(2)
public final List verificationTypes;
-
@Field(3)
public final List simSlotIndices;
@@ -36,7 +36,4 @@ public GetPnvCapabilitiesRequest(
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
-
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(GetPnvCapabilitiesRequest.class);
}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java
index e5dea8c34c..b1382969ca 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java
@@ -12,6 +12,8 @@
@SafeParcelable.Class
public class GetPnvCapabilitiesResponse extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(GetPnvCapabilitiesResponse.class);
@Field(1)
public final List simCapabilities;
@@ -27,24 +29,19 @@ public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(GetPnvCapabilitiesResponse.class);
-
@SafeParcelable.Class
public static class SimCapability extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(SimCapability.class);
@Field(1)
public final int slotValue;
-
@Field(2)
public final String subscriberIdDigest;
-
@Field(3)
public final int carrierId;
-
@Field(4)
public final String operatorName;
-
@Field(5)
public final List verificationCapabilities;
@@ -67,17 +64,15 @@ public SimCapability(
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
-
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(SimCapability.class);
}
@SafeParcelable.Class
public static class VerificationCapability extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(VerificationCapability.class);
@Field(1)
public final int verificationMethod;
-
@Field(2)
public final int statusValue;
@@ -94,8 +89,5 @@ public VerificationCapability(
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
-
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(VerificationCapability.class);
}
}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java
index 82b4ce0268..0dc2c9fbfe 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java
@@ -12,16 +12,15 @@
@SafeParcelable.Class
public class PhoneNumberInfo extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(PhoneNumberInfo.class);
@Field(1)
public final int version;
-
@Field(2)
@Nullable
public final String phoneNumber;
-
@Field(3)
public final long verificationTime;
-
@Field(4)
@Nullable
public final Bundle extras;
@@ -43,7 +42,4 @@ public PhoneNumberInfo(
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
-
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(PhoneNumberInfo.class);
}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
index e554934ae6..c68ecc7e53 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
@@ -13,27 +13,22 @@
@SafeParcelable.Class
public class VerifyPhoneNumberRequest extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(VerifyPhoneNumberRequest.class);
@Field(1)
public final String policyId;
-
@Field(2)
public final long timeout;
-
@Field(3)
public final IdTokenRequest idTokenRequest;
-
@Field(4)
public final Bundle extras;
-
@Field(5)
public final List targetedSims;
-
@Field(6)
public final boolean silent;
-
@Field(7)
public final int apiVersion;
-
@Field(8)
public final List verificationMethodsValues;
@@ -63,14 +58,12 @@ public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(VerifyPhoneNumberRequest.class);
-
@SafeParcelable.Class
public static class IdTokenRequest extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(IdTokenRequest.class);
@Field(1)
public final String idToken;
-
@Field(2)
public final String subscriberHash;
@@ -87,16 +80,14 @@ public IdTokenRequest(
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
-
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(IdTokenRequest.class);
}
@SafeParcelable.Class
public static class ImsiRequest extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(ImsiRequest.class);
@Field(1)
public final String imsi;
-
@Field(2)
public final String phoneNumberHint;
@@ -113,8 +104,5 @@ public ImsiRequest(
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
-
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(ImsiRequest.class);
}
}
\ No newline at end of file
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java
index 4a94badd74..d66fca5c19 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java
@@ -12,9 +12,10 @@
@SafeParcelable.Class
public class VerifyPhoneNumberResponse extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(VerifyPhoneNumberResponse.class);
@Field(1)
public final PhoneNumberVerification[] verifications;
-
@Field(2)
public final Bundle extras;
@@ -32,35 +33,27 @@ public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(VerifyPhoneNumberResponse.class);
-
@SafeParcelable.Class
public static class PhoneNumberVerification extends AbstractSafeParcelable {
+ public static SafeParcelableCreatorAndWriter CREATOR =
+ findCreator(PhoneNumberVerification.class);
@Field(1)
@Nullable
public final String phoneNumber;
-
@Field(2)
public final long timestampMillis;
-
@Field(3)
public final int verificationMethod;
-
@Field(4)
public final int simSlot;
-
@Field(5)
@Nullable
public final String verificationToken;
-
@Field(6)
@Nullable
public final Bundle extras;
-
@Field(7)
public final int verificationStatus;
-
@Field(8)
public final long retryAfterSeconds;
@@ -89,8 +82,5 @@ public PhoneNumberVerification(
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
-
- public static SafeParcelableCreatorAndWriter CREATOR =
- findCreator(PhoneNumberVerification.class);
}
}
\ No newline at end of file
From 59e189566908bf8beeea334f3936555c5184f72f Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 27 Mar 2026 08:01:21 -0400
Subject: [PATCH 11/31] why did i write it like this
---
.../gms/constellation/core/proto/builders/GaiaInfoBuilder.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
index 4c6dd5987d..cb9c9e508f 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
@@ -41,7 +41,7 @@ operator fun GaiaSignals.Companion.invoke(context: Context): GaiaSignals? {
GaiaSignalEntry(
gaia_id = obfuscatedId,
signal_type = GaiaAccountSignalType.GAIA_ACCOUNT_SIGNAL_AUTHENTICATED,
- timestamp = Instant.ofEpochMilli(System.currentTimeMillis())
+ timestamp = Instant.now()
)
)
}
From b20cc3caeded72faf3f7fd02bb95483b6f8a1f9d Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 27 Mar 2026 19:39:24 -0400
Subject: [PATCH 12/31] Remove full name and use import instead
---
.../gms/constellation/core/proto/builders/ClientInfoBuilder.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
index 8a67cb84ad..8ef77487f8 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
@@ -11,6 +11,7 @@ import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.content.edit
import androidx.core.content.getSystemService
+import com.google.android.gms.droidguard.DroidGuard
import com.google.android.gms.tasks.await
import okio.ByteString.Companion.toByteString
import org.microg.gms.common.Constants
@@ -157,7 +158,7 @@ suspend operator fun DroidGuardSignals.Companion.invoke(context: Context): Droid
}
return try {
- val client = com.google.android.gms.droidguard.DroidGuard.getClient(context)
+ val client = DroidGuard.getClient(context)
val data = mapOf(
"package_name" to context.packageName,
"timestamp" to System.currentTimeMillis().toString()
From 2552a9e8937d28766ce17f25e1172c442f50b907 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Sun, 29 Mar 2026 00:54:33 -0400
Subject: [PATCH 13/31] Use GMS package name instead of our own
---
.../gms/constellation/core/GetVerifiedPhoneNumbers.kt | 3 ++-
.../microg/gms/constellation/core/VerifyPhoneNumber.kt | 9 ++++++---
.../core/proto/builders/ClientInfoBuilder.kt | 2 +-
.../core/proto/builders/SyncRequestBuilder.kt | 5 +++--
.../verification/ts43/ServiceEntitlementExtension.kt | 3 +--
5 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
index 28f5df0669..5f73229910 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/GetVerifiedPhoneNumbers.kt
@@ -16,6 +16,7 @@ import com.google.android.gms.constellation.internal.IConstellationCallbacks
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okio.ByteString.Companion.toByteString
+import org.microg.gms.common.Constants
import org.microg.gms.constellation.core.proto.GetVerifiedPhoneNumbersRequest
import org.microg.gms.constellation.core.proto.GetVerifiedPhoneNumbersRequest.PhoneNumberSelection
import org.microg.gms.constellation.core.proto.IIDTokenAuth
@@ -47,7 +48,7 @@ suspend fun handleGetVerifiedPhoneNumbers(
internal suspend fun fetchVerifiedPhoneNumbers(
context: Context,
bundle: Bundle,
- callingPackage: String = bundle.getString("calling_package") ?: context.packageName
+ callingPackage: String = bundle.getString("calling_package") ?: Constants.GMS_PACKAGE_NAME
): List = withContext(Dispatchers.IO) {
val authManager = context.authManager
val sessionId = UUID.randomUUID().toString()
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
index cf5871186e..572d8dcf8f 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
@@ -20,6 +20,7 @@ import com.squareup.wire.GrpcException
import com.squareup.wire.GrpcStatus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import org.microg.gms.common.Constants
import org.microg.gms.constellation.core.proto.SyncRequest
import org.microg.gms.constellation.core.proto.Verification
import org.microg.gms.constellation.core.proto.builders.RequestBuildContext
@@ -44,7 +45,8 @@ suspend fun handleVerifyPhoneNumberV1(
bundle: Bundle,
packageName: String?
) {
- val callingPackage = packageName ?: bundle.getString("calling_package") ?: context.packageName
+ val callingPackage =
+ packageName ?: bundle.getString("calling_package") ?: Constants.GMS_PACKAGE_NAME
val extras = Bundle(bundle).apply {
putString("calling_package", callingPackage)
putString("calling_api", "verifyPhoneNumber")
@@ -93,7 +95,8 @@ suspend fun handleVerifyPhoneNumberSingleUse(
bundle: Bundle,
packageName: String?
) {
- val callingPackage = packageName ?: bundle.getString("calling_package") ?: context.packageName
+ val callingPackage =
+ packageName ?: bundle.getString("calling_package") ?: Constants.GMS_PACKAGE_NAME
val extras = Bundle(bundle).apply {
putString("calling_package", callingPackage)
putString("calling_api", "verifyPhoneNumberSingleUse")
@@ -134,7 +137,7 @@ suspend fun handleVerifyPhoneNumberRequest(
request: VerifyPhoneNumberRequest,
packageName: String?
) {
- val callingPackage = packageName ?: context.packageName
+ val callingPackage = packageName ?: Constants.GMS_PACKAGE_NAME
request.extras.putString("calling_api", "verifyPhoneNumber")
val useReadPath = when (request.apiVersion) {
0 -> request.policyId in VerifyPhoneNumberApiPhenotypes.READ_ONLY_POLICY_IDS
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
index 8ef77487f8..a456974288 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
@@ -160,7 +160,7 @@ suspend operator fun DroidGuardSignals.Companion.invoke(context: Context): Droid
return try {
val client = DroidGuard.getClient(context)
val data = mapOf(
- "package_name" to context.packageName,
+ "package_name" to Constants.GMS_PACKAGE_NAME,
"timestamp" to System.currentTimeMillis().toString()
)
val result = client.getResults("constellation_verify", data, null).await()
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
index 3d1dca1acf..0dc25d93b6 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
@@ -17,6 +17,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import com.google.android.gms.constellation.VerifyPhoneNumberRequest
import com.google.android.gms.constellation.verificationMethods
+import org.microg.gms.common.Constants
import org.microg.gms.constellation.core.ConstellationStateStore
import org.microg.gms.constellation.core.proto.ChallengePreference
import org.microg.gms.constellation.core.proto.ChallengePreferenceMetadata
@@ -113,7 +114,7 @@ suspend operator fun SyncRequest.Companion.invoke(
sessionId: String,
request: VerifyPhoneNumberRequest,
includeClientAuth: Boolean = false,
- callingPackage: String = context.packageName,
+ callingPackage: String = Constants.GMS_PACKAGE_NAME,
triggerType: RequestTrigger.Type = RequestTrigger.Type.TRIGGER_API_CALL
): SyncRequest {
val buildContext = buildRequestContext(context)
@@ -136,7 +137,7 @@ suspend operator fun SyncRequest.Companion.invoke(
buildContext: RequestBuildContext,
imsiToInfoMap: Map = buildImsiToSubscriptionInfoMap(context),
includeClientAuth: Boolean = false,
- callingPackage: String = context.packageName,
+ callingPackage: String = Constants.GMS_PACKAGE_NAME,
triggerType: RequestTrigger.Type = RequestTrigger.Type.TRIGGER_API_CALL
): SyncRequest {
val apiParamsList = Param.getList(request.extras)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
index 56d8114022..f5966f6691 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ts43/ServiceEntitlementExtension.kt
@@ -32,9 +32,8 @@ fun ServiceEntitlementRequest.builder(
)
fun ServiceEntitlementRequest.userAgent(context: Context): String {
- val packageVersion = runCatching {
+ val packageVersion =
context.packageManager.getPackageInfo(context.packageName, 0).versionName.orEmpty()
- }.getOrDefault("")
val vendor = terminal_vendor.take(4)
val model = terminal_model.take(10)
val swVersion = terminal_software_version.take(20)
From d08e4d8cb3f92c5d97f6b0e3e8e7a620ab50fbec Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Sun, 29 Mar 2026 01:07:57 -0400
Subject: [PATCH 14/31] Remove unnecessary dependencies
---
play-services-constellation/build.gradle | 9 +--------
play-services-constellation/core/build.gradle | 4 +---
2 files changed, 2 insertions(+), 11 deletions(-)
diff --git a/play-services-constellation/build.gradle b/play-services-constellation/build.gradle
index e0cc981be3..f2bacc16a6 100644
--- a/play-services-constellation/build.gradle
+++ b/play-services-constellation/build.gradle
@@ -5,9 +5,6 @@
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-parcelize'
-apply plugin: 'kotlin-kapt'
apply plugin: 'signing'
android {
@@ -30,10 +27,6 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
-
- kotlinOptions {
- jvmTarget = 1.8
- }
}
apply from: '../gradle/publish-android.gradle'
@@ -43,5 +36,5 @@ description = 'microG implementation of play-services-constellation'
dependencies {
implementation project(':play-services-basement')
- kapt project(":safe-parcel-processor")
+ annotationProcessor project(":safe-parcel-processor")
}
diff --git a/play-services-constellation/core/build.gradle b/play-services-constellation/core/build.gradle
index c51c1f39ea..cd7df4f4db 100644
--- a/play-services-constellation/core/build.gradle
+++ b/play-services-constellation/core/build.gradle
@@ -6,8 +6,6 @@
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-parcelize'
-apply plugin: 'kotlin-kapt'
apply plugin: 'signing'
apply plugin: 'com.squareup.wire'
@@ -58,7 +56,7 @@ dependencies {
implementation project(':play-services-droidguard')
implementation project(':play-services-tasks-ktx')
+ implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
api "com.squareup.wire:wire-runtime:$wireVersion"
api "com.squareup.wire:wire-grpc-client:$wireVersion"
- api "com.squareup.okhttp3:okhttp:$okHttpVersion"
}
From 05cd22d05449097c6d6b12cccf318e0e6496075b Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Sun, 29 Mar 2026 01:54:18 -0400
Subject: [PATCH 15/31] Refactor package name from `builders` to `builder`
---
.../microg/gms/constellation/core/VerifyPhoneNumber.kt | 8 ++++----
.../core/proto/{builders => builder}/ClientInfoBuilder.kt | 2 +-
.../core/proto/{builders => builder}/CommonBuilders.kt | 2 +-
.../core/proto/{builders => builder}/GaiaInfoBuilder.kt | 2 +-
.../proto/{builders => builder}/RequestBuildContext.kt | 2 +-
.../proto/{builders => builder}/SyncRequestBuilder.kt | 2 +-
.../proto/{builders => builder}/TelephonyInfoBuilder.kt | 2 +-
.../constellation/core/verification/ChallengeProcessor.kt | 4 ++--
8 files changed, 12 insertions(+), 12 deletions(-)
rename play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/{builders => builder}/ClientInfoBuilder.kt (99%)
rename play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/{builders => builder}/CommonBuilders.kt (97%)
rename play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/{builders => builder}/GaiaInfoBuilder.kt (98%)
rename play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/{builders => builder}/RequestBuildContext.kt (90%)
rename play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/{builders => builder}/SyncRequestBuilder.kt (99%)
rename play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/{builders => builder}/TelephonyInfoBuilder.kt (99%)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
index 572d8dcf8f..afdb361a67 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
@@ -23,10 +23,10 @@ import kotlinx.coroutines.withContext
import org.microg.gms.common.Constants
import org.microg.gms.constellation.core.proto.SyncRequest
import org.microg.gms.constellation.core.proto.Verification
-import org.microg.gms.constellation.core.proto.builders.RequestBuildContext
-import org.microg.gms.constellation.core.proto.builders.buildImsiToSubscriptionInfoMap
-import org.microg.gms.constellation.core.proto.builders.buildRequestContext
-import org.microg.gms.constellation.core.proto.builders.invoke
+import org.microg.gms.constellation.core.proto.builder.RequestBuildContext
+import org.microg.gms.constellation.core.proto.builder.buildImsiToSubscriptionInfoMap
+import org.microg.gms.constellation.core.proto.builder.buildRequestContext
+import org.microg.gms.constellation.core.proto.builder.invoke
import org.microg.gms.constellation.core.verification.ChallengeProcessor
import java.util.UUID
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/ClientInfoBuilder.kt
similarity index 99%
rename from play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/ClientInfoBuilder.kt
index a456974288..e3265a57b1 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/ClientInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/ClientInfoBuilder.kt
@@ -1,6 +1,6 @@
@file:RequiresApi(Build.VERSION_CODES.O)
-package org.microg.gms.constellation.core.proto.builders
+package org.microg.gms.constellation.core.proto.builder
import android.annotation.SuppressLint
import android.content.Context
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/CommonBuilders.kt
similarity index 97%
rename from play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/CommonBuilders.kt
index d3a4e4beb5..b34b11010f 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/CommonBuilders.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/CommonBuilders.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.core.proto.builders
+package org.microg.gms.constellation.core.proto.builder
import android.content.Context
import android.os.Build
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/GaiaInfoBuilder.kt
similarity index 98%
rename from play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/GaiaInfoBuilder.kt
index cb9c9e508f..6eca28cdf7 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/GaiaInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/GaiaInfoBuilder.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.core.proto.builders
+package org.microg.gms.constellation.core.proto.builder
import android.accounts.AccountManager
import android.content.Context
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/RequestBuildContext.kt
similarity index 90%
rename from play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/RequestBuildContext.kt
index 1262045264..951255aa86 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/RequestBuildContext.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/RequestBuildContext.kt
@@ -1,4 +1,4 @@
-package org.microg.gms.constellation.core.proto.builders
+package org.microg.gms.constellation.core.proto.builder
import android.content.Context
import org.microg.gms.constellation.core.AuthManager
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/SyncRequestBuilder.kt
similarity index 99%
rename from play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/SyncRequestBuilder.kt
index 0dc25d93b6..d58eaeb8f7 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/SyncRequestBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/SyncRequestBuilder.kt
@@ -1,7 +1,7 @@
@file:RequiresApi(Build.VERSION_CODES.O)
@file:SuppressLint("HardwareIds")
-package org.microg.gms.constellation.core.proto.builders
+package org.microg.gms.constellation.core.proto.builder
import android.Manifest
import android.annotation.SuppressLint
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/TelephonyInfoBuilder.kt
similarity index 99%
rename from play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt
rename to play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/TelephonyInfoBuilder.kt
index a66a562957..307ae6a24e 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builders/TelephonyInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/TelephonyInfoBuilder.kt
@@ -2,7 +2,7 @@
@file:SuppressLint("HardwareIds")
@file:Suppress("DEPRECATION")
-package org.microg.gms.constellation.core.proto.builders
+package org.microg.gms.constellation.core.proto.builder
import android.annotation.SuppressLint
import android.content.Context
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
index f01997d252..61105217b2 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
@@ -18,8 +18,8 @@ import org.microg.gms.constellation.core.proto.RequestHeader
import org.microg.gms.constellation.core.proto.RequestTrigger
import org.microg.gms.constellation.core.proto.Verification
import org.microg.gms.constellation.core.proto.VerificationMethod
-import org.microg.gms.constellation.core.proto.builders.RequestBuildContext
-import org.microg.gms.constellation.core.proto.builders.invoke
+import org.microg.gms.constellation.core.proto.builder.RequestBuildContext
+import org.microg.gms.constellation.core.proto.builder.invoke
object ChallengeProcessor {
private const val TAG = "ChallengeProcessor"
From 117312df213e08ab08eba7329573697565a2875f Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Sun, 29 Mar 2026 02:53:47 -0400
Subject: [PATCH 16/31] Clean up `constellation.proto`
---
.../core/src/main/proto/constellation.proto | 125 +++++++++++++-----
1 file changed, 93 insertions(+), 32 deletions(-)
diff --git a/play-services-constellation/core/src/main/proto/constellation.proto b/play-services-constellation/core/src/main/proto/constellation.proto
index 93a10c652c..de4e950b6e 100644
--- a/play-services-constellation/core/src/main/proto/constellation.proto
+++ b/play-services-constellation/core/src/main/proto/constellation.proto
@@ -5,7 +5,6 @@ package google.internal.communications.phonedeviceverification.v1;
import "google/protobuf/timestamp.proto";
-// Verification and consent RPCs.
service PhoneDeviceVerification {
rpc GetConsent(GetConsentRequest) returns (GetConsentResponse);
rpc SetConsent(SetConsentRequest) returns (SetConsentResponse);
@@ -13,30 +12,31 @@ service PhoneDeviceVerification {
rpc Proceed(ProceedRequest) returns (ProceedResponse);
}
-// Read-only phone number retrieval RPC.
service PhoneNumber {
rpc GetVerifiedPhoneNumbers(GetVerifiedPhoneNumbersRequest) returns (GetVerifiedPhoneNumbersResponse);
}
-// Shared request and response messages.
-// Header for each client request.
message RequestHeader {
ClientInfo client_info = 1;
ClientAuth client_auth = 2;
string session_id = 3;
RequestTrigger trigger = 4;
}
+
message ResponseHeader {
StatusProtoSimple status = 1;
}
+
message StatusProtoSimple {
int32 code = 1;
}
+
enum StatusCode {
STATUS_CODE_UNSPECIFIED = 0;
STATUS_CODE_OK = 1;
STATUS_CODE_ERROR = 2;
}
+
message ClientInfo {
DeviceID device_id = 1;
bytes client_public_key = 2;
@@ -78,25 +78,30 @@ enum DeviceType {
DEVICE_TYPE_DESKTOP = 9;
DEVICE_TYPE_XR_PERIPHERAL = 10;
}
+
message ClientAuth {
DeviceID device_id = 1;
bytes signature = 2;
google.protobuf.Timestamp sign_timestamp = 3;
}
+
message CountryInfo {
repeated string sim_countries = 1;
repeated string network_countries = 2;
}
+
message DroidGuardSignals {
string droidguard_result = 1;
string droidguard_token = 2;
}
+
message DeviceID {
string iid_token = 1;
int64 primary_device_id = 2;
int64 user_serial = 3;
int64 gms_android_id = 4;
}
+
message RequestTrigger {
enum Type {
UNKNOWN = 0;
@@ -115,20 +120,25 @@ message RequestTrigger {
}
Type type = 1;
}
+
message DroidGuardAttestation {
string droidguard_result = 1;
string droidguard_token = 2;
}
+
message Experiment {
string key = 1;
string value = 2;
}
+
message GaiaID {
string id = 1;
}
+
message GaiaToken {
string token = 1;
}
+
message GaiaSignalEntry {
string gaia_id = 1;
GaiaAccountSignalType signal_type = 2;
@@ -141,14 +151,15 @@ enum GaiaAccountSignalType {
GAIA_ACCOUNT_SIGNAL_UNAUTHENTICATED = 2;
GAIA_ACCOUNT_SIGNAL_REMOVED_WITH_GMS_AUTH_BROADCAST = 3;
}
+
message GaiaSignals {
repeated GaiaSignalEntry gaia_signals = 1;
}
+
message Status {
int32 code = 1;
}
-// Named API params used by verification RPCs.
message VerificationPolicy {
string policy_id = 1;
int64 max_verification_age_hours = 2;
@@ -157,20 +168,22 @@ message VerificationPolicy {
repeated VerificationParam params = 5;
}
-// Parameters for ID token generation.
message IdTokenRequest {
string certificate_hash = 1;
string token_nonce = 2;
}
+
message VerificationParam {
string key = 1;
string value = 2;
}
+
message SimOperatorInfo {
string imsi_hash = 1;
string sim_operator = 3;
}
+
message NetworkSignal {
enum Type {
TYPE_UNKNOWN = 0;
@@ -194,6 +207,7 @@ message NetworkSignal {
State state = 2;
Availability availability = 3;
}
+
message MobileOperatorInfo {
string country_code = 1;
uint64 nil_since_usec = 2;
@@ -202,12 +216,12 @@ message MobileOperatorInfo {
string operator_name = 5;
}
-// Sync and proceed flow.
message SyncRequest {
repeated Verification verifications = 3;
RequestHeader header = 4;
repeated VerificationToken verification_tokens = 5;
}
+
message SyncResponse {
repeated VerificationResponse responses = 1;
ServerTimestamp next_sync_time = 2;
@@ -215,11 +229,13 @@ message SyncResponse {
DroidguardToken droidguard_token = 4;
repeated VerificationToken verification_tokens = 5;
}
+
message ProceedRequest {
Verification verification = 2;
ChallengeResponse challenge_response = 3;
RequestHeader header = 4;
}
+
message ProceedResponse {
Verification verification = 1;
ResponseHeader header = 2;
@@ -227,17 +243,16 @@ message ProceedResponse {
DroidguardToken droidguard_token = 4;
}
-// Server time plus the time when the server wrote this message.
message ServerTimestamp {
google.protobuf.Timestamp timestamp = 1;
google.protobuf.Timestamp now = 2;
}
+
message VerificationToken {
bytes token = 1;
google.protobuf.Timestamp expiration_time = 2;
}
-// Verification state and challenge model.
message Verification {
enum State {
UNKNOWN = 0;
@@ -273,18 +288,22 @@ message Verification {
ChallengePreference challenge_preference = 7;
VerificationPolicy structured_api_params = 8;
}
+
message VerificationResponse {
Verification verification = 1;
StatusProto error = 2;
}
+
message StatusProto {
int32 code = 1;
string message = 3;
}
+
message SIMSlotInfo {
int32 slot_index = 1;
int32 subscription_id = 2;
}
+
message SIMAssociation {
message SIMInfo {
@@ -303,8 +322,10 @@ message SIMAssociation {
repeated GaiaToken gaia_tokens = 2;
SIMSlotInfo sim_slot = 4;
}
+
message GaiaAssociation {
}
+
message VerificationAssociation {
oneof association {
SIMAssociation sim = 1;
@@ -312,18 +333,16 @@ message VerificationAssociation {
}
}
-// Information for a verified record.
message VerificationInfo {
string phone_number = 1;
google.protobuf.Timestamp verification_time = 2;
VerificationMethod challenge_method = 6;
}
+
message PendingVerificationInfo {
Challenge challenge = 2;
}
-// Challenge payloads and responses.
-// Challenge issued by the server.
message Challenge {
ChallengeID challenge_id = 1;
VerificationMethod type = 2;
@@ -336,24 +355,25 @@ message Challenge {
Ts43Challenge ts43_challenge = 12;
}
-// Carrier ID challenge used for SS7 traffic.
message CarrierIdChallenge {
string isim_request = 3;
int32 auth_type = 5;
int32 app_type = 6;
}
+
message MTChallenge {
string sms = 1;
}
+
message ChallengeID {
string id = 1;
}
+
message Capabilities {
string droidguard_result = 1;
string droidguard_token = 2;
}
-// MO SMS challenge.
message MoChallenge {
string proxy_number = 1;
DataSmsInfo data_sms_info = 3;
@@ -361,15 +381,19 @@ message MoChallenge {
string polling_intervals = 5;
string sms_without_persisting = 6;
}
+
message DataSmsInfo {
int32 destination_port = 1;
}
+
message RegisteredSmsChallenge {
repeated PhoneNumberID verified_senders = 1;
}
+
message PhoneNumberID {
bytes phone_number_id = 1;
}
+
message FlashCallChallenge {
repeated PhoneRange phone_ranges = 1;
FlashCallState state = 2;
@@ -377,6 +401,7 @@ message FlashCallChallenge {
repeated ChallengeResponse previous_challenge_responses = 4;
int64 millis_between_interceptions = 5;
}
+
message PhoneRange {
string phone_number_prefix = 1;
string phone_number_suffix = 2;
@@ -391,6 +416,7 @@ enum FlashCallState {
FLASH_CALL_STATE_VERIFIED = 3;
FLASH_CALL_STATE_FAILED = 4;
}
+
message UnverifiedInfo {
enum Reason {
UNKNOWN_REASON = 0;
@@ -408,7 +434,6 @@ message UnverifiedInfo {
VerificationMethod challenge_method = 3;
}
-// Challenge response.
message ChallengeResponse {
MTChallengeResponseData mt_response = 1;
CarrierIdChallengeResponse carrier_id_response = 2;
@@ -417,10 +442,12 @@ message ChallengeResponse {
FlashCallChallengeResponse flash_call_response = 7;
Ts43ChallengeResponse ts43_challenge_response = 9;
}
+
message MTChallengeResponseData {
string sms = 1;
string sender = 2;
}
+
message CarrierIdChallengeResponse {
string isim_response = 3;
CarrierIdError carrier_id_error = 4;
@@ -440,6 +467,7 @@ enum CarrierIdError {
CARRIER_ID_ERROR_INTERNAL_ERROR = 10;
CARRIER_ID_ERROR_INVALID_ARGUMENT = 11;
}
+
message MOChallengeResponseData {
enum Status {
UNKNOWN_STATUS = 0;
@@ -452,15 +480,19 @@ message MOChallengeResponseData {
int64 sms_result_code = 2;
int64 sms_error_code = 3;
}
+
message RegisteredSmsChallengeResponse {
repeated RegisteredSmsChallengeResponseItem items = 1;
}
+
message RegisteredSmsChallengeResponseItem {
RegisteredSmsChallengeResponsePayload payload = 2;
}
+
message RegisteredSmsChallengeResponsePayload {
bytes payload = 1;
}
+
message Ts43ChallengeResponse {
Ts43Type ts43_type = 1;
ClientChallengeResponse client_challenge_response = 2;
@@ -468,6 +500,7 @@ message Ts43ChallengeResponse {
Ts43ChallengeResponseStatus status = 4;
repeated string http_history = 5;
}
+
message Ts43ChallengeResponseStatus {
enum Code {
TS43_STATUS_UNSPECIFIED = 0;
@@ -484,8 +517,6 @@ message Ts43ChallengeResponseStatus {
}
}
-// The numeric 1..9 range is intentionally left generic because those values are
-// overloaded across multiple TS.43 request stages.
message Ts43ChallengeResponseError {
enum Code {
TS43_ERROR_CODE_UNSPECIFIED = 0;
@@ -521,12 +552,15 @@ message Ts43ChallengeResponseError {
int32 http_status = 2;
RequestType request_type = 3;
}
+
message ServerChallengeResponse {
string acquire_temporary_token_response = 2;
}
+
message ClientChallengeResponse {
string get_phone_number_response = 2;
}
+
message FlashCallChallengeResponse {
enum Error {
NO_ERROR = 0;
@@ -549,6 +583,7 @@ message FlashCallChallengeResponse {
string caller = 1;
Error error = 2;
}
+
message Ts43Type {
enum Integrator {
TS43_INTEGRATOR_UNSPECIFIED = 0;
@@ -571,6 +606,7 @@ message Ts43Type {
}
Integrator integrator = 1;
}
+
message CellularNetworkEvent {
google.protobuf.Timestamp timestamp = 1;
bool mobile_data_enabled = 2;
@@ -579,10 +615,12 @@ message CellularNetworkEvent {
bool mobile_data_always_on = 5;
repeated NetworkState networks = 6;
}
+
message NetworkState {
repeated int32 types = 1;
bool available = 2;
}
+
message ServiceStateEvent {
google.protobuf.Timestamp timestamp = 1;
bool airplane_mode_enabled = 2;
@@ -593,11 +631,13 @@ message ServiceStateEvent {
int32 data_network_type = 7;
int32 signal_strength = 8;
}
+
message SMSEvent {
google.protobuf.Timestamp timestamp = 1;
EventDirection direction = 2;
EventPhoneNumberType number_type = 3;
}
+
message CallEvent {
google.protobuf.Timestamp timestamp = 1;
EventDirection direction = 2;
@@ -615,6 +655,7 @@ enum EventPhoneNumberType {
LONG_NUMBER = 1;
SHORT_CODE = 2;
}
+
message TelephonyInfo {
enum PhoneType {
PHONE_TYPE_UNKNOWN = 0;
@@ -680,18 +721,19 @@ message TelephonyInfo {
ToggleState carrier_id_capability = 23;
int64 sim_carrier_id = 25;
}
+
message SimNetworkInfo {
string country_iso = 1;
string operator = 2;
string operator_name = 3;
int32 inactive_time_diff_ms = 4;
}
+
message DroidguardToken {
string token = 1;
google.protobuf.Timestamp ttl = 2;
}
-// Consent and Asterism messages.
message GetConsentRequest {
DeviceID device_id = 1;
repeated GaiaToken gaia_tokens = 2;
@@ -703,6 +745,7 @@ message GetConsentRequest {
string session_id = 9;
bool unknown_flag = 10;
}
+
message GetConsentResponse {
RcsConsent rcs_consent = 1;
ServerTimestamp next_check_time = 5;
@@ -712,8 +755,6 @@ message GetConsentResponse {
PermissionState permission_state = 10;
}
-// These values intentionally alias because the same field is reused across
-// multiple consent surfaces.
enum FlowContext {
option allow_alias = true;
FLOW_CONTEXT_UNSPECIFIED = 0;
@@ -724,6 +765,7 @@ enum FlowContext {
FLOW_CONTEXT_RCS_DEFAULT_ON_LEGAL_FYI_IN_SETTINGS = 4;
FLOW_CONTEXT_RCS_UNKNOWN_5 = 5;
}
+
message SetConsentRequest {
RequestHeader header = 1;
RcsConsent rcs_consent = 3;
@@ -736,6 +778,7 @@ message SetConsentRequest {
AsterismConsent device_consent = 10;
}
}
+
message SetConsentResponse {
}
@@ -746,8 +789,6 @@ enum Consent {
EXPIRED = 3;
}
-// These values intentionally alias because RCS and device consent versions
-// share the same field.
enum ConsentVersion {
option allow_alias = true;
CONSENT_VERSION_UNSPECIFIED = 0;
@@ -759,41 +800,47 @@ enum ConsentVersion {
PHONE_VERIFICATION_INTL_SMS_CALLS = 3;
PHONE_VERIFICATION_REACHABILITY_INTL_SMS_CALLS = 4;
}
+
message AsterismConsent {
Consent consent = 1;
ConsentSource consent_source = 2;
ConsentVersion consent_version = 3;
}
+
message ConsentState {
Consent state = 2;
}
-// `flags` is still an opaque server-defined bitfield.
message PermissionState {
int32 flags = 1;
PermissionType type = 2;
}
+
enum PermissionType {
PERMISSION_TYPE_UNSPECIFIED = 0;
LEGACY_DPNV = 1;
PNVR = 2;
NOT_ALLOWED = 3;
}
+
message RcsConsent {
Consent consent = 2;
ConsentVersion consent_version = 3;
}
+
message GaiaConsent {
AsterismClient asterism_client = 1;
Consent consent = 2;
ConsentVersion consent_version = 3;
}
+
message OnDemandConsent {
Consent consent = 1;
GaiaToken gaia_token = 2;
string consent_variant = 3;
string trigger = 4;
}
+
enum ConsentSource {
SOURCE_UNSPECIFIED = 0;
ANDROID_DEVICE_SETTINGS = 1;
@@ -805,6 +852,7 @@ enum ConsentSource {
MEET_ON_DEMAND_CONSENT = 7;
GPAY_ON_DEMAND_CONSENT = 8;
}
+
enum AsterismClient {
UNKNOWN_CLIENT = 0;
CONSTELLATION = 1;
@@ -812,8 +860,6 @@ enum AsterismClient {
ONE_TIME_VERIFICATION = 3;
}
-// Read-only phone number RPC.
-// Request message for GetVerifiedPhoneNumbers.
message GetVerifiedPhoneNumbersRequest {
enum PhoneNumberSelection {
SELECTION_UNSPECIFIED = 0;
@@ -830,20 +876,22 @@ message GetVerifiedPhoneNumbersRequest {
RequestInfo request_info = 7;
}
-// Response message for GetVerifiedPhoneNumbers.
message GetVerifiedPhoneNumbersResponse {
repeated VerifiedPhoneNumber phone_numbers = 2;
}
+
message IIDTokenAuth {
string iid_token = 1;
bytes client_sign = 2;
google.protobuf.Timestamp sign_timestamp = 3;
}
+
message TokenOption {
string certificate_hash = 1;
string token_nonce = 2;
string package_name = 3;
}
+
message VerifiedPhoneNumber {
string phone_number = 1;
google.protobuf.Timestamp verification_time = 2;
@@ -863,8 +911,6 @@ enum TelephonyPhoneNumberType {
PHONE_NUMBER_SOURCE_IMS = 3;
}
-// Shared helpers.
-// Data consistency requirement for read-only RPCs.
message ConsistencyOption {
enum Consistency {
CONSISTENCY_UNSPECIFIED = 0;
@@ -874,23 +920,24 @@ message ConsistencyOption {
Consistency consistency = 1;
}
-// Request metadata for analytics and legacy policy attribution.
message RequestInfo {
string policy_id = 1;
}
+
message Param {
string key = 1;
string value = 2;
}
-// Client challenge preference.
message ChallengePreference {
repeated VerificationMethod capabilities = 1;
ChallengePreferenceMetadata metadata = 2;
}
+
message ChallengePreferenceMetadata {
string sms_signature = 2;
}
+
enum VerificationMethod {
UNKNOWN = 0;
MO_SMS = 1;
@@ -902,31 +949,36 @@ enum VerificationMethod {
TS43 = 11;
}
-// Audit payloads.
message AuditToken {
AuditTokenMetadata metadata = 2;
}
+
message AuditTokenMetadata {
AuditUuid uuid = 1;
}
+
message AuditUuid {
int64 uuid_msb = 1;
int64 uuid_lsb = 2;
}
+
message AuditPayload {
AuditDeviceInfo device_info = 1;
AuditDeviceId device_id = 2;
AuditEventMetadata event_metadata = 10;
}
+
message AuditDeviceInfo {
oneof device_identifier {
string android_id_hash = 1;
string instance_id = 2;
}
}
+
message AuditDeviceId {
string android_id_hash = 1;
}
+
message AuditEventMetadata {
AuditEventType event_type = 1;
string session_id = 2;
@@ -941,32 +993,37 @@ message AuditEventMetadata {
AuditComponentInfo component_info = 11;
string trigger = 12;
}
+
enum AuditEventType {
EVENT_TYPE_UNSPECIFIED = 0;
ASTERISM_CONSENT_CHANGE = 1;
RCS_CONSENT_CHANGE = 2;
VERIFICATION_COMPLETE = 3;
}
+
message AuditConsentDetails {
repeated int32 sim_slot_ids = 1;
}
+
message AuditComponentInfo {
AuditComponentType component_type = 1;
}
+
enum AuditComponentType {
COMPONENT_UNSPECIFIED = 0;
ASTERISM_CONSTELLATION = 119;
ASTERISM_RCS = 120;
}
+
message AuditConsentState {
Consent state = 1;
}
+
enum AuditEventTypeValue {
EVENT_VALUE_UNSPECIFIED = 0;
ASTERISM_CLIENT_CONSENT_CHANGE = 187;
}
-// TS.43 challenge messages.
message Ts43Challenge {
Ts43Type ts43_type = 1;
string entitlement_url = 2;
@@ -976,12 +1033,15 @@ message Ts43Challenge {
string app_id = 6;
string eap_aka_realm = 7;
}
+
message ServerChallenge {
OdsaOperation operation = 1;
}
+
message ClientChallenge {
OdsaOperation operation = 1;
}
+
message OdsaOperation {
string operation = 1;
int32 operation_type = 2;
@@ -1005,6 +1065,7 @@ message OdsaOperation {
string old_terminal_id = 20;
string old_terminal_iccid = 21;
}
+
message ServiceEntitlementRequest {
int32 notification_action = 1;
string entitlement_version = 2;
From db60565d3822c9da99d6d53037dde1960800b731 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Sun, 29 Mar 2026 04:16:31 -0400
Subject: [PATCH 17/31] More consistent formatting
---
.../android/gms/constellation/GetIidTokenRequest.java | 1 +
.../android/gms/constellation/GetIidTokenResponse.java | 4 ++++
.../gms/constellation/GetPnvCapabilitiesRequest.java | 3 +++
.../gms/constellation/GetPnvCapabilitiesResponse.java | 1 +
.../google/android/gms/constellation/PhoneNumberInfo.java | 4 ++++
.../gms/constellation/VerifyPhoneNumberRequest.java | 8 ++++++++
.../gms/constellation/VerifyPhoneNumberResponse.java | 2 ++
7 files changed, 23 insertions(+)
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java
index 311dcb0fd9..c24de63130 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java
@@ -13,6 +13,7 @@
public class GetIidTokenRequest extends AbstractSafeParcelable {
public static SafeParcelableCreatorAndWriter CREATOR =
findCreator(GetIidTokenRequest.class);
+
@Field(1)
@Nullable
public final Long projectNumber;
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java
index fd2a3c66d6..19b97ec9cc 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java
@@ -13,13 +13,17 @@
public class GetIidTokenResponse extends AbstractSafeParcelable {
public static SafeParcelableCreatorAndWriter CREATOR =
findCreator(GetIidTokenResponse.class);
+
@Field(1)
public final String iidToken;
+
@Field(2)
public final String fid;
+
@Field(3)
@Nullable
public final byte[] signature;
+
@Field(4)
public final long timestamp;
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java
index 9fbc98b5c0..a0c13b3306 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java
@@ -14,10 +14,13 @@
public class GetPnvCapabilitiesRequest extends AbstractSafeParcelable {
public static SafeParcelableCreatorAndWriter CREATOR =
findCreator(GetPnvCapabilitiesRequest.class);
+
@Field(1)
public final String policyId;
+
@Field(2)
public final List verificationTypes;
+
@Field(3)
public final List simSlotIndices;
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java
index b1382969ca..ee522b446e 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java
@@ -14,6 +14,7 @@
public class GetPnvCapabilitiesResponse extends AbstractSafeParcelable {
public static SafeParcelableCreatorAndWriter CREATOR =
findCreator(GetPnvCapabilitiesResponse.class);
+
@Field(1)
public final List simCapabilities;
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java
index 0dc2c9fbfe..77f4fd7753 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java
@@ -14,13 +14,17 @@
public class PhoneNumberInfo extends AbstractSafeParcelable {
public static SafeParcelableCreatorAndWriter CREATOR =
findCreator(PhoneNumberInfo.class);
+
@Field(1)
public final int version;
+
@Field(2)
@Nullable
public final String phoneNumber;
+
@Field(3)
public final long verificationTime;
+
@Field(4)
@Nullable
public final Bundle extras;
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
index c68ecc7e53..8c2327f091 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
@@ -15,20 +15,28 @@
public class VerifyPhoneNumberRequest extends AbstractSafeParcelable {
public static SafeParcelableCreatorAndWriter CREATOR =
findCreator(VerifyPhoneNumberRequest.class);
+
@Field(1)
public final String policyId;
+
@Field(2)
public final long timeout;
+
@Field(3)
public final IdTokenRequest idTokenRequest;
+
@Field(4)
public final Bundle extras;
+
@Field(5)
public final List targetedSims;
+
@Field(6)
public final boolean silent;
+
@Field(7)
public final int apiVersion;
+
@Field(8)
public final List verificationMethodsValues;
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java
index d66fca5c19..263461debf 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java
@@ -14,8 +14,10 @@
public class VerifyPhoneNumberResponse extends AbstractSafeParcelable {
public static SafeParcelableCreatorAndWriter CREATOR =
findCreator(VerifyPhoneNumberResponse.class);
+
@Field(1)
public final PhoneNumberVerification[] verifications;
+
@Field(2)
public final Bundle extras;
From 0621ee71712dc07a3ee0b98142da033d553c4fa9 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Wed, 1 Apr 2026 10:19:31 -0400
Subject: [PATCH 18/31] Use actual Gaia ID
---
.../core/src/main/AndroidManifest.xml | 1 +
.../core/proto/builder/GaiaInfoBuilder.kt | 70 +++++++++++--------
2 files changed, 40 insertions(+), 31 deletions(-)
diff --git a/play-services-constellation/core/src/main/AndroidManifest.xml b/play-services-constellation/core/src/main/AndroidManifest.xml
index 8ad6d5d3fe..aa877ea5e5 100644
--- a/play-services-constellation/core/src/main/AndroidManifest.xml
+++ b/play-services-constellation/core/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
xmlns:tools="http://schemas.android.com/tools">
+
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/GaiaInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/GaiaInfoBuilder.kt
index 6eca28cdf7..9c7c7134b0 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/GaiaInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/GaiaInfoBuilder.kt
@@ -12,52 +12,60 @@ import org.microg.gms.constellation.core.proto.GaiaAccountSignalType
import org.microg.gms.constellation.core.proto.GaiaSignalEntry
import org.microg.gms.constellation.core.proto.GaiaSignals
import org.microg.gms.constellation.core.proto.GaiaToken
-import java.security.MessageDigest
-import kotlin.math.abs
-import kotlin.math.absoluteValue
private const val TAG = "GaiaInfoBuilder"
@RequiresApi(Build.VERSION_CODES.O)
-operator fun GaiaSignals.Companion.invoke(context: Context): GaiaSignals? {
- val entries = mutableListOf()
- try {
- val accounts = AccountManager.get(context).getAccountsByType("com.google")
- val md = MessageDigest.getInstance("SHA-256")
+suspend operator fun GaiaSignals.Companion.invoke(context: Context): GaiaSignals? =
+ withContext(Dispatchers.IO) {
+ val entries = mutableListOf()
+ val accountManager = AccountManager.get(context)
- for (account in accounts) {
- // Note: Simple implementation, maybe do actual obfuscated Gaia ID retrieval later?
- val hash = md.digest(account.name.toByteArray(Charsets.UTF_8))
- val number =
- hash.take(8).fold(0L) { acc, byte -> (acc shl 8) or (byte.toLong() and 0xFF) }
- val obfuscatedId = try {
- number.absoluteValue.toString()
- } catch (_: Exception) {
- abs(account.name.hashCode().toLong()).toString()
- }
+ try {
+ val accounts = accountManager.getAccountsByType("com.google")
- entries.add(
- GaiaSignalEntry(
- gaia_id = obfuscatedId,
- signal_type = GaiaAccountSignalType.GAIA_ACCOUNT_SIGNAL_AUTHENTICATED,
- timestamp = Instant.now()
+ for (account in accounts) {
+ var id = accountManager.getUserData(account, "GoogleUserId")
+ if (id == "") {
+ try {
+ val future = accountManager.getAuthToken(
+ account,
+ "^^_account_id_^^",
+ null,
+ false,
+ null,
+ null
+ )
+ id = future.result?.getString(AccountManager.KEY_AUTHTOKEN)
+ accountManager.setUserData(account, "GoogleUserId", id)
+ } catch (e: Exception) {
+ Log.w(TAG, "Could not retrieve Gaia ID for account ${account.name}", e)
+ continue
+ }
+ }
+ entries.add(
+ GaiaSignalEntry(
+ gaia_id = id,
+ signal_type = GaiaAccountSignalType.GAIA_ACCOUNT_SIGNAL_AUTHENTICATED,
+ timestamp = Instant.now()
+ )
)
- )
+ }
+ } catch (e: Exception) {
+ Log.w(TAG, "Could not build Gaia signals", e)
}
- } catch (e: Exception) {
- Log.w(TAG, "Could not build Gaia signals", e)
+ if (entries.isNotEmpty()) GaiaSignals(gaia_signals = entries) else null
}
- return if (entries.isNotEmpty()) GaiaSignals(gaia_signals = entries) else null
-}
@Suppress("DEPRECATION")
suspend fun GaiaToken.Companion.getList(context: Context): List =
withContext(Dispatchers.IO) {
- val accounts = AccountManager.get(context).getAccountsByType("com.google")
+ val accountManager = AccountManager.get(context)
+ val accounts = accountManager.getAccountsByType("com.google")
accounts.mapNotNull { account ->
try {
- val future = AccountManager.get(context).getAuthToken(
+ val future = accountManager.getAuthToken(
account,
"oauth2:https://www.googleapis.com/auth/numberer",
null,
@@ -70,7 +78,7 @@ suspend fun GaiaToken.Companion.getList(context: Context): List =
if (!token.isNullOrBlank()) GaiaToken(token = token) else null
} catch (e: Exception) {
- Log.w(TAG, "Could not retrieve Gaia token for an account", e)
+ Log.w(TAG, "Could not retrieve Gaia token for account ${account.name}", e)
null
}
}
From b82676c0d9b90804b45a186dbdb0e9fdba79fe65 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 3 Apr 2026 03:52:42 -0400
Subject: [PATCH 19/31] Align more with official behavior
---
.../core/VerificationMappings.kt | 87 ++++++++++++-------
.../constellation/core/VerifyPhoneNumber.kt | 27 ++++--
.../core/verification/ChallengeProcessor.kt | 6 +-
.../VerifyPhoneNumberRequest.java | 6 +-
4 files changed, 79 insertions(+), 47 deletions(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
index 2fae9c903b..edf9a5de0b 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerificationMappings.kt
@@ -11,7 +11,7 @@ import org.microg.gms.constellation.core.proto.VerificationMethod
fun UnverifiedInfo.Reason.toVerificationStatus(): Verification.Status {
return when (this) {
- UnverifiedInfo.Reason.UNKNOWN_REASON -> Verification.Status.STATUS_PENDING
+ UnverifiedInfo.Reason.UNKNOWN_REASON -> Verification.Status.STATUS_UNKNOWN
UnverifiedInfo.Reason.THROTTLED -> Verification.Status.STATUS_THROTTLED
UnverifiedInfo.Reason.FAILED -> Verification.Status.STATUS_FAILED
UnverifiedInfo.Reason.SKIPPED -> Verification.Status.STATUS_SKIPPED
@@ -25,72 +25,76 @@ fun UnverifiedInfo.Reason.toVerificationStatus(): Verification.Status {
}
}
-fun Verification.getState(): Verification.State {
- return when {
+val Verification.state: Verification.State
+ get() = when {
verification_info != null -> Verification.State.VERIFIED
pending_verification_info != null -> Verification.State.PENDING
unverified_info != null -> Verification.State.NONE
else -> Verification.State.UNKNOWN
}
-}
-fun Verification.getVerificationStatus(): Verification.Status {
- return when (getState()) {
+val Verification.effectiveStatus: Verification.Status
+ get() = when (state) {
Verification.State.VERIFIED -> Verification.Status.STATUS_VERIFIED
-
- Verification.State.PENDING -> {
+ Verification.State.PENDING ->
if (status != Verification.Status.STATUS_UNKNOWN) {
status
} else {
Verification.Status.STATUS_PENDING
}
- }
- Verification.State.NONE -> {
- unverified_info?.reason?.toVerificationStatus() ?: Verification.Status.STATUS_PENDING
- }
+ Verification.State.NONE ->
+ unverified_info?.reason?.toVerificationStatus() ?: Verification.Status.STATUS_UNKNOWN
Verification.State.UNKNOWN -> Verification.Status.STATUS_UNKNOWN
}
-}
@RequiresApi(Build.VERSION_CODES.O)
fun Verification.toClientVerification(imsiToSlotMap: Map): PhoneNumberVerification {
- val verificationStatus = this.getVerificationStatus()
- var phoneNumber: String? = null
- var timestampMillis = System.currentTimeMillis()
- var verificationMethod = VerificationMethod.UNKNOWN
- var retryAfterSeconds = 0L
+ val clientStatus = effectiveStatus.toClientStatus()
val extras = buildClientExtras()
+ val simImsi = association?.sim?.sim_info?.imsi?.firstOrNull()
+ val simSlot = if (simImsi != null) imsiToSlotMap[simImsi] ?: -1 else -1
- when (this.getState()) {
+ var phoneNumber = ""
+ var timestampMillis = 0L
+ var verificationMethod = VerificationMethod.UNKNOWN
+ var verificationToken: String? = null
+ var retryAfterSeconds = -1L
+
+ when (state) {
Verification.State.VERIFIED -> {
- val info = this.verification_info
- phoneNumber = info?.phone_number
- timestampMillis = info?.verification_time?.toEpochMilli() ?: System.currentTimeMillis()
- verificationMethod = info?.challenge_method ?: VerificationMethod.UNKNOWN
+ val info = verification_info
+ val verifiedPhoneNumber = info?.phone_number
+ require(!verifiedPhoneNumber.isNullOrEmpty()) { "Verified phone number is empty" }
+ phoneNumber = verifiedPhoneNumber
+ timestampMillis = info.verification_time?.toEpochMilli() ?: 0L
+ verificationMethod = info.challenge_method
+ verificationToken = extras.getString("id_token")
+ extras.remove("phone_number")
+ extras.remove("id_token")
+ extras.remove("verification_time_millis")
}
Verification.State.PENDING -> {
verificationMethod =
- this.pending_verification_info?.challenge?.type ?: VerificationMethod.UNKNOWN
+ pending_verification_info?.challenge?.type ?: VerificationMethod.UNKNOWN
}
Verification.State.NONE -> {
- val info = this.unverified_info
+ val info = unverified_info
verificationMethod = info?.challenge_method ?: VerificationMethod.UNKNOWN
retryAfterSeconds = info?.retry_after_time?.let { ts ->
val now = System.currentTimeMillis() / 1000L
(ts.epochSecond - now).coerceAtLeast(0L)
- } ?: 0L
+ } ?: -1L
}
- else -> {}
+ Verification.State.UNKNOWN -> Unit
}
- val simImsi = this.association?.sim?.sim_info?.imsi?.firstOrNull()
- val simSlot = if (simImsi != null) imsiToSlotMap[simImsi] ?: 0 else 0
- val verificationToken = extras.getString("id_token")
+ extras.remove("verification_method")
+ extras.remove("sim_slot_index")
return PhoneNumberVerification(
phoneNumber,
@@ -99,7 +103,7 @@ fun Verification.toClientVerification(imsiToSlotMap: Map): PhoneNum
simSlot,
verificationToken,
extras,
- verificationStatus.value,
+ clientStatus,
retryAfterSeconds
)
}
@@ -112,7 +116,6 @@ private fun Verification.buildClientExtras(): Bundle {
val slotIndex = association?.sim?.sim_slot?.slot_index
if (slotIndex != null && slotIndex >= 0) {
- // GMS exposes this as a string inside the extras bundle.
bundle.putString("sim_slot_index", slotIndex.toString())
}
return bundle
@@ -130,8 +133,26 @@ private fun Bundle.putParam(param: Param) {
fun VerificationMethod.toClientMethod(): Int {
return when (this) {
- VerificationMethod.TS43 -> 9
VerificationMethod.UNKNOWN -> 0
+ VerificationMethod.TS43 -> 9
else -> value
}
}
+
+fun Verification.Status.toClientStatus(): Int {
+ return when (this) {
+ Verification.Status.STATUS_UNKNOWN,
+ Verification.Status.STATUS_NONE -> 0
+
+ Verification.Status.STATUS_PENDING -> 6
+ Verification.Status.STATUS_VERIFIED -> 1
+ Verification.Status.STATUS_THROTTLED -> 3
+ Verification.Status.STATUS_FAILED -> 2
+ Verification.Status.STATUS_SKIPPED -> 4
+ Verification.Status.STATUS_NOT_REQUIRED -> 5
+ Verification.Status.STATUS_PHONE_NUMBER_ENTRY_REQUIRED -> 7
+ Verification.Status.STATUS_INELIGIBLE -> 8
+ Verification.Status.STATUS_DENIED -> 9
+ Verification.Status.STATUS_NOT_IN_SERVICE -> 10
+ }
+}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
index afdb361a67..a1901667f6 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
@@ -257,7 +257,7 @@ private suspend fun runVerificationFlow(
includeClientAuth = ConstellationStateStore.isPublicKeyAcked(context),
callingPackage = callingPackage
)
- val (verifications, isPublicKeyAcked) = executeSyncFlow(
+ val verifications = executeSyncFlow(
context,
sessionId,
request,
@@ -265,9 +265,6 @@ private suspend fun runVerificationFlow(
buildContext,
imsiToInfoMap
)
- if (isPublicKeyAcked) {
- ConstellationStateStore.setPublicKeyAcked(context, true)
- }
if (legacyCallback) {
callbacks.onPhoneNumberVerified(
@@ -285,7 +282,10 @@ private suspend fun runVerificationFlow(
}
private fun PhoneNumberVerification.toLegacyPhoneNumberInfoOrNull(): PhoneNumberInfo? {
- if (verificationStatus != Verification.Status.STATUS_VERIFIED.value || phoneNumber.isNullOrEmpty()) {
+ if (
+ verificationStatus != Verification.Status.STATUS_VERIFIED.toClientStatus() ||
+ phoneNumber.isNullOrEmpty()
+ ) {
return null
}
val extras = Bundle(this.extras ?: Bundle.EMPTY).apply {
@@ -301,7 +301,7 @@ private suspend fun executeSyncFlow(
syncRequest: SyncRequest,
buildContext: RequestBuildContext,
imsiToInfoMap: Map
-): Pair, Boolean> = withContext(Dispatchers.IO) {
+): Array = withContext(Dispatchers.IO) {
Log.d(TAG, "Sending Sync request")
val syncResponse = try {
RpcClient.phoneDeviceVerificationClient.Sync().execute(syncRequest)
@@ -335,7 +335,7 @@ private suspend fun executeSyncFlow(
return@mapNotNull null
}
- val finalVerification = if (verification.getState() == Verification.State.PENDING) {
+ val finalVerification = if (verification.state == Verification.State.PENDING) {
ChallengeProcessor.process(
context,
sessionId,
@@ -347,12 +347,23 @@ private suspend fun executeSyncFlow(
verification
}
+ if (finalVerification.state != Verification.State.VERIFIED) {
+ Log.w(TAG, "Unverified. State: ${finalVerification.state}")
+ (finalVerification.pending_verification_info
+ ?: finalVerification.unverified_info)?.let { Log.w(TAG, it.toString()) }
+
+ if (!request.includeUnverified) {
+ return@mapNotNull null
+ }
+ }
+
finalVerification.toClientVerification(imsiToSlotMap)
}.toTypedArray()
if (isPublicKeyAcked) {
Log.d(TAG, "Server acknowledged client public key")
+ ConstellationStateStore.setPublicKeyAcked(context, true)
}
- verifications to isPublicKeyAcked
+ verifications
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
index 61105217b2..532c5733a4 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
@@ -11,7 +11,6 @@ import com.squareup.wire.GrpcException
import com.squareup.wire.GrpcStatus
import org.microg.gms.constellation.core.ConstellationStateStore
import org.microg.gms.constellation.core.RpcClient
-import org.microg.gms.constellation.core.getState
import org.microg.gms.constellation.core.proto.ChallengeResponse
import org.microg.gms.constellation.core.proto.ProceedRequest
import org.microg.gms.constellation.core.proto.RequestHeader
@@ -20,6 +19,7 @@ import org.microg.gms.constellation.core.proto.Verification
import org.microg.gms.constellation.core.proto.VerificationMethod
import org.microg.gms.constellation.core.proto.builder.RequestBuildContext
import org.microg.gms.constellation.core.proto.builder.invoke
+import org.microg.gms.constellation.core.state
object ChallengeProcessor {
private const val TAG = "ChallengeProcessor"
@@ -42,10 +42,10 @@ object ChallengeProcessor {
var currentVerification = verification
for (attempt in 1..MAX_PROCEED_ROUNDS) {
- if (currentVerification.getState() != Verification.State.PENDING) {
+ if (currentVerification.state != Verification.State.PENDING) {
Log.d(
TAG,
- "Verification state: ${currentVerification.getState()}. Stopping sequential verification."
+ "Verification state: ${currentVerification.state}. Stopping sequential verification."
)
return currentVerification
}
diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
index 8c2327f091..a0ced6ac30 100644
--- a/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
+++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java
@@ -32,7 +32,7 @@ public class VerifyPhoneNumberRequest extends AbstractSafeParcelable {
public final List targetedSims;
@Field(6)
- public final boolean silent;
+ public final boolean includeUnverified;
@Field(7)
public final int apiVersion;
@@ -47,7 +47,7 @@ public VerifyPhoneNumberRequest(
@Param(3) IdTokenRequest idTokenRequest,
@Param(4) Bundle extras,
@Param(5) List targetedSims,
- @Param(6) boolean silent,
+ @Param(6) boolean includeUnverified,
@Param(7) int apiVersion,
@Param(8) List verificationMethodsValues
) {
@@ -56,7 +56,7 @@ public VerifyPhoneNumberRequest(
this.idTokenRequest = idTokenRequest;
this.extras = extras;
this.targetedSims = targetedSims;
- this.silent = silent;
+ this.includeUnverified = includeUnverified;
this.apiVersion = apiVersion;
this.verificationMethodsValues = verificationMethodsValues;
}
From 58732b56d982785798e1b4d8257c44f542da5fb7 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 3 Apr 2026 04:15:32 -0400
Subject: [PATCH 20/31] Add more infos to `TelephonyInfo`
---
.../core/src/main/AndroidManifest.xml | 1 +
.../proto/builder/TelephonyInfoBuilder.kt | 86 ++++++++++++++++++-
2 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/play-services-constellation/core/src/main/AndroidManifest.xml b/play-services-constellation/core/src/main/AndroidManifest.xml
index aa877ea5e5..db636fcc6e 100644
--- a/play-services-constellation/core/src/main/AndroidManifest.xml
+++ b/play-services-constellation/core/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/TelephonyInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/TelephonyInfoBuilder.kt
index 307ae6a24e..5bc26d6157 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/TelephonyInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/TelephonyInfoBuilder.kt
@@ -4,15 +4,20 @@
package org.microg.gms.constellation.core.proto.builder
+import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
+import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.os.Build
+import android.os.UserManager
+import android.telephony.ServiceState
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Base64
import android.util.Log
import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import org.microg.gms.constellation.core.proto.NetworkSignal
import org.microg.gms.constellation.core.proto.SimNetworkInfo
@@ -25,6 +30,7 @@ private const val TAG = "TelephonyInfoBuilder"
@SuppressLint("MissingPermission")
operator fun TelephonyInfo.Companion.invoke(context: Context, subscriptionId: Int): TelephonyInfo {
val tm = context.getSystemService()
+ val subscriptionManager = context.getSystemService()
val targetTm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && subscriptionId >= 0) {
tm?.createForSubscriptionId(subscriptionId)
} else tm
@@ -67,6 +73,8 @@ operator fun TelephonyInfo.Companion.invoke(context: Context, subscriptionId: In
operator_name = targetTm?.networkOperatorName ?: ""
)
+ val (activeSubCount, activeSubCountMax) = getActiveSubCounts(subscriptionManager)
+
return TelephonyInfo(
phone_type = phoneType,
group_id_level1 = targetTm?.groupIdLevel1 ?: "",
@@ -74,12 +82,88 @@ operator fun TelephonyInfo.Companion.invoke(context: Context, subscriptionId: In
network_info = networkInfo,
network_roaming = networkRoaming,
connectivity_state = connectivityState,
- sms_capability = if (targetTm?.isSmsCapable == true) TelephonyInfo.SmsCapability.SMS_CAPABLE else TelephonyInfo.SmsCapability.SMS_NOT_CAPABLE,
+ sms_capability = getSmsCapability(context, targetTm),
+ active_sub_count = activeSubCount,
+ active_sub_count_max = activeSubCountMax,
sim_state = simState,
+ service_state = getServiceState(targetTm),
+ sim_carrier_id = getSimCarrierId(targetTm),
is_embedded = false
)
}
+private fun getSmsCapability(
+ context: Context,
+ telephonyManager: TelephonyManager?
+): TelephonyInfo.SmsCapability {
+ val hasReadSms =
+ ContextCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) ==
+ PackageManager.PERMISSION_GRANTED
+ val hasSendSms =
+ ContextCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS) ==
+ PackageManager.PERMISSION_GRANTED
+
+ if (!hasReadSms || !hasSendSms) {
+ return TelephonyInfo.SmsCapability.SMS_DEFAULT_CAPABILITY
+ }
+
+ val isSmsRestricted =
+ context.getSystemService()?.userRestrictions?.getBoolean(UserManager.DISALLOW_SMS) ==
+ true
+ if (isSmsRestricted) {
+ return TelephonyInfo.SmsCapability.SMS_RESTRICTED
+ }
+
+ return if (telephonyManager?.isSmsCapable == true) {
+ TelephonyInfo.SmsCapability.SMS_CAPABLE
+ } else {
+ TelephonyInfo.SmsCapability.SMS_NOT_CAPABLE
+ }
+}
+
+private fun getActiveSubCounts(subscriptionManager: SubscriptionManager?): Pair {
+ if (subscriptionManager == null) {
+ return 0 to 0
+ }
+
+ return try {
+ subscriptionManager.activeSubscriptionInfoCount to
+ subscriptionManager.activeSubscriptionInfoCountMax
+ } catch (e: SecurityException) {
+ Log.w(TAG, "Could not retrieve active subscription counts", e)
+ 0 to 0
+ }
+}
+
+private fun getServiceState(telephonyManager: TelephonyManager?): TelephonyInfo.ServiceState {
+ val state = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ try {
+ telephonyManager?.serviceState?.state
+ } catch (e: SecurityException) {
+ Log.w(TAG, "Could not retrieve service state", e)
+ null
+ }
+ } else {
+ null
+ }
+
+ return when (state) {
+ ServiceState.STATE_IN_SERVICE -> TelephonyInfo.ServiceState.SERVICE_STATE_IN_SERVICE
+ ServiceState.STATE_OUT_OF_SERVICE -> TelephonyInfo.ServiceState.SERVICE_STATE_OUT_OF_SERVICE
+ ServiceState.STATE_EMERGENCY_ONLY -> TelephonyInfo.ServiceState.SERVICE_STATE_EMERGENCY_ONLY
+ ServiceState.STATE_POWER_OFF -> TelephonyInfo.ServiceState.SERVICE_STATE_POWER_OFF
+ else -> TelephonyInfo.ServiceState.SERVICE_STATE_UNKNOWN
+ }
+}
+
+private fun getSimCarrierId(telephonyManager: TelephonyManager?): Long {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ telephonyManager?.simCarrierId?.toLong() ?: -1L
+ } else {
+ -1L
+ }
+}
+
fun NetworkSignal.Companion.getList(context: Context): List {
val connectivityInfos = mutableListOf()
try {
From edb6de4ec6fb01351fe3e180232ca3a46792de73 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 3 Apr 2026 04:49:13 -0400
Subject: [PATCH 21/31] Improve DroidGuard generation
---
.../core/proto/builder/ClientInfoBuilder.kt | 36 ++++++++++---------
.../core/proto/builder/CommonBuilders.kt | 5 +--
.../core/proto/builder/SyncRequestBuilder.kt | 9 ++++-
.../core/verification/ChallengeProcessor.kt | 3 +-
4 files changed, 32 insertions(+), 21 deletions(-)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/ClientInfoBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/ClientInfoBuilder.kt
index e3265a57b1..ae9d3a9e9a 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/ClientInfoBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/ClientInfoBuilder.kt
@@ -7,6 +7,7 @@ import android.content.Context
import android.os.Build
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
+import android.util.Base64
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.content.edit
@@ -24,30 +25,20 @@ import org.microg.gms.constellation.core.proto.DeviceID
import org.microg.gms.constellation.core.proto.DeviceType
import org.microg.gms.constellation.core.proto.DroidGuardSignals
import org.microg.gms.constellation.core.proto.GaiaSignals
-import org.microg.gms.constellation.core.proto.GaiaToken
import org.microg.gms.constellation.core.proto.NetworkSignal
import org.microg.gms.constellation.core.proto.SimOperatorInfo
import org.microg.gms.constellation.core.proto.UserProfileType
+import java.security.MessageDigest
import java.util.Locale
private const val TAG = "ClientInfoBuilder"
private const val PREFS_NAME = "constellation_prefs"
-@SuppressLint("HardwareIds")
-suspend operator fun ClientInfo.Companion.invoke(context: Context, iidToken: String): ClientInfo {
- return ClientInfo(
- context,
- RequestBuildContext(
- iidToken = iidToken,
- gaiaTokens = GaiaToken.getList(context)
- )
- )
-}
-
@SuppressLint("HardwareIds")
suspend operator fun ClientInfo.Companion.invoke(
context: Context,
- buildContext: RequestBuildContext
+ buildContext: RequestBuildContext,
+ rpc: String
): ClientInfo {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val locale = Locale.getDefault().let { "${it.language}_${it.country}" }
@@ -70,7 +61,7 @@ suspend operator fun ClientInfo.Companion.invoke(
is_wearable_standalone = false,
gaia_signals = GaiaSignals(context),
device_fingerprint = Build.FINGERPRINT,
- droidguard_signals = DroidGuardSignals(context),
+ droidguard_signals = DroidGuardSignals(context, buildContext.iidToken, rpc),
)
}
@@ -151,17 +142,28 @@ operator fun CountryInfo.Companion.invoke(context: Context): CountryInfo {
return CountryInfo(sim_countries = simCountries, network_countries = networkCountries)
}
-suspend operator fun DroidGuardSignals.Companion.invoke(context: Context): DroidGuardSignals? {
+suspend operator fun DroidGuardSignals.Companion.invoke(
+ context: Context,
+ iidToken: String,
+ rpc: String
+): DroidGuardSignals? {
val cachedToken = ConstellationStateStore.loadDroidGuardToken(context)
if (!cachedToken.isNullOrBlank()) {
return DroidGuardSignals(droidguard_token = cachedToken, droidguard_result = "")
}
+ val md = MessageDigest.getInstance("SHA-256")
+ val digest = md.digest(iidToken.toByteArray(Charsets.UTF_8))
+ val iidHash = Base64.encodeToString(
+ digest.copyOf(64),
+ Base64.NO_PADDING or Base64.NO_WRAP
+ ).substring(0, 32)
+
return try {
val client = DroidGuard.getClient(context)
val data = mapOf(
- "package_name" to Constants.GMS_PACKAGE_NAME,
- "timestamp" to System.currentTimeMillis().toString()
+ "iidHash" to iidHash,
+ "rpc" to rpc
)
val result = client.getResults("constellation_verify", data, null).await()
DroidGuardSignals(droidguard_result = result, droidguard_token = "")
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/CommonBuilders.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/CommonBuilders.kt
index b34b11010f..8cf436b2b6 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/CommonBuilders.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/CommonBuilders.kt
@@ -47,7 +47,8 @@ suspend operator fun RequestHeader.Companion.invoke(
sessionId: String,
buildContext: RequestBuildContext,
triggerType: RequestTrigger.Type = RequestTrigger.Type.CONSENT_API_TRIGGER,
- includeClientAuth: Boolean = false
+ includeClientAuth: Boolean = false,
+ rpc: String
): RequestHeader {
val authManager = if (includeClientAuth) context.authManager else null
val clientAuth = if (includeClientAuth) {
@@ -62,7 +63,7 @@ suspend operator fun RequestHeader.Companion.invoke(
}
return RequestHeader(
- client_info = ClientInfo(context, buildContext),
+ client_info = ClientInfo(context, buildContext, rpc),
client_auth = clientAuth,
session_id = sessionId,
trigger = RequestTrigger(type = triggerType)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/SyncRequestBuilder.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/SyncRequestBuilder.kt
index d58eaeb8f7..27eb45e747 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/SyncRequestBuilder.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/proto/builder/SyncRequestBuilder.kt
@@ -192,7 +192,14 @@ suspend operator fun SyncRequest.Companion.invoke(
return SyncRequest(
verifications = verifications,
- header_ = RequestHeader(context, sessionId, buildContext, triggerType, includeClientAuth),
+ header_ = RequestHeader(
+ context,
+ sessionId,
+ buildContext,
+ triggerType,
+ includeClientAuth,
+ "sync"
+ ),
verification_tokens = ConstellationStateStore.loadVerificationTokens(context)
)
}
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
index 532c5733a4..eb708cfd8d 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
@@ -107,7 +107,8 @@ object ChallengeProcessor {
sessionId,
buildContext,
RequestTrigger.Type.TRIGGER_API_CALL,
- includeClientAuth = true
+ includeClientAuth = true,
+ "proceed"
)
val proceedRequest = ProceedRequest(
verification = currentVerification,
From 94c442ae107d060d03779f4ed0cf23343e05d2c6 Mon Sep 17 00:00:00 2001
From: Opstic <46141527+opstic@users.noreply.github.com>
Date: Fri, 3 Apr 2026 17:55:51 -0400
Subject: [PATCH 22/31] Improve `MtSmsVerifier`
---
.../core/src/main/AndroidManifest.xml | 3 +
.../constellation/core/VerifyPhoneNumber.kt | 26 ++-
.../core/verification/ChallengeProcessor.kt | 2 +-
.../core/verification/MtSmsVerifier.kt | 201 ++++++++++++------
4 files changed, 163 insertions(+), 69 deletions(-)
diff --git a/play-services-constellation/core/src/main/AndroidManifest.xml b/play-services-constellation/core/src/main/AndroidManifest.xml
index db636fcc6e..943034c187 100644
--- a/play-services-constellation/core/src/main/AndroidManifest.xml
+++ b/play-services-constellation/core/src/main/AndroidManifest.xml
@@ -18,6 +18,9 @@
+
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
index a1901667f6..2578baf068 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/VerifyPhoneNumber.kt
@@ -28,6 +28,7 @@ import org.microg.gms.constellation.core.proto.builder.buildImsiToSubscriptionIn
import org.microg.gms.constellation.core.proto.builder.buildRequestContext
import org.microg.gms.constellation.core.proto.builder.invoke
import org.microg.gms.constellation.core.verification.ChallengeProcessor
+import org.microg.gms.constellation.core.verification.MtSmsInboxRegistry
import java.util.UUID
private const val TAG = "VerifyPhoneNumber"
@@ -257,14 +258,23 @@ private suspend fun runVerificationFlow(
includeClientAuth = ConstellationStateStore.isPublicKeyAcked(context),
callingPackage = callingPackage
)
- val verifications = executeSyncFlow(
- context,
- sessionId,
- request,
- syncRequest,
- buildContext,
- imsiToInfoMap
- )
+
+ MtSmsInboxRegistry.prepare(
+ context.applicationContext,
+ imsiToInfoMap.values.map { it.subscriptionId })
+
+ val verifications = try {
+ executeSyncFlow(
+ context,
+ sessionId,
+ request,
+ syncRequest,
+ buildContext,
+ imsiToInfoMap
+ )
+ } finally {
+ MtSmsInboxRegistry.dispose()
+ }
if (legacyCallback) {
callbacks.onPhoneNumberVerified(
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
index eb708cfd8d..17a2a0f37b 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/ChallengeProcessor.kt
@@ -85,7 +85,7 @@ object ChallengeProcessor {
}
}
- VerificationMethod.MT_SMS -> challenge.mt_challenge?.verify(context, subId)
+ VerificationMethod.MT_SMS -> challenge.mt_challenge?.verify(subId, remainingMillis)
VerificationMethod.MO_SMS -> challenge.mo_challenge?.verify(context, subId)
diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt
index 4ad7e84e6a..fe7abe47a1 100644
--- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt
+++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/core/verification/MtSmsVerifier.kt
@@ -6,88 +6,169 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.provider.Telephony
+import android.telephony.SmsMessage
import android.util.Log
+import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import org.microg.gms.constellation.core.proto.ChallengeResponse
import org.microg.gms.constellation.core.proto.MTChallenge
import org.microg.gms.constellation.core.proto.MTChallengeResponseData
+import java.util.concurrent.ConcurrentHashMap
import kotlin.coroutines.resume
private const val TAG = "MtSmsVerifier"
-suspend fun MTChallenge.verify(context: Context, subId: Int): ChallengeResponse? {
+suspend fun MTChallenge.verify(
+ subId: Int,
+ timeoutMillis: Long? = null
+): ChallengeResponse? {
val expectedBody = sms.takeIf { it.isNotEmpty() } ?: return null
+ val inbox = MtSmsInboxRegistry.get(subId)
+ val effectiveTimeoutMillis = timeoutMillis?.coerceIn(0L..300_000L) ?: 300_000L
Log.d(TAG, "Waiting for MT SMS containing challenge string")
- val result = withTimeoutOrNull(300_000) {
- suspendCancellableCoroutine?> { continuation ->
- val receiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- try {
- if (intent.action == Telephony.Sms.Intents.SMS_RECEIVED_ACTION) {
- val receivedSubId = intent.getIntExtra(
- "android.telephony.extra.SUBSCRIPTION_INDEX",
- intent.getIntExtra("subscription", -1)
- )
- if (subId != -1 && receivedSubId != -1 && receivedSubId != subId) {
- return
- }
- val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
- for (msg in messages) {
- val body = msg.messageBody
- if (body != null && body.contains(expectedBody)) {
- Log.d(
- TAG,
- "Matching MT SMS received from ${msg.originatingAddress}"
- )
- context.unregisterReceiver(this)
- if (continuation.isActive) {
- continuation.resume(
- Pair(
- body,
- msg.originatingAddress ?: ""
- )
- )
- }
- return
- }
- }
- }
- } catch (e: Exception) {
- Log.e(TAG, "Error in MT SMS receiver", e)
+ val match = withTimeoutOrNull(effectiveTimeoutMillis) {
+ inbox.awaitMatch(expectedBody)
+ }
+
+ if (match == null) {
+ Log.w(TAG, "Timed out waiting for MT SMS, proceeding with empty response")
+ }
+
+ val response = match ?: ReceivedSms(body = "", sender = "")
+ return ChallengeResponse(
+ mt_response = MTChallengeResponseData(
+ sms = response.body,
+ sender = response.sender
+ )
+ )
+}
+
+internal object MtSmsInboxRegistry {
+ private val inboxes = ConcurrentHashMap()
+
+ fun prepare(context: Context, subIds: Iterable) {
+ dispose()
+
+ val effectiveSubIds = subIds.distinct().ifEmpty { listOf(-1) }
+ for (subId in effectiveSubIds) {
+ inboxes[subId] = MtSmsInbox(context.applicationContext, subId)
+ }
+ }
+
+ fun get(subId: Int): MtSmsInbox {
+ return inboxes[subId]
+ ?: error("MT SMS inbox for subId=$subId was not initialized")
+ }
+
+ fun dispose() {
+ val currentInboxes = inboxes.values.toList()
+ inboxes.clear()
+ for (inbox in currentInboxes) {
+ inbox.dispose()
+ }
+ }
+}
+
+internal data class ReceivedSms(
+ val body: String,
+ val sender: String
+)
+
+private data class PendingMatch(
+ val expectedBody: String,
+ val continuation: CancellableContinuation