From e5ac919d768d89076c60c51e429dac52fefd9ebb Mon Sep 17 00:00:00 2001 From: vikramships Date: Tue, 10 Feb 2026 18:27:59 +0530 Subject: [PATCH 1/4] Constellation: add phone verification module and harden verification flow. Wire the dedicated Constellation service into GmsCore and replace placeholder behavior with real sync/proceed handling, including msisdn token propagation, Spatula header gating, and safer TS43/IID fallback paths for real-device validation. --- .gitignore | 1 + play-services-constellation/build.gradle | 42 + play-services-constellation/core/build.gradle | 57 + .../core/src/main/AndroidManifest.xml | 16 + .../gms/constellation/ConstellationService.kt | 31 + .../constellation/ConstellationServiceImpl.kt | 915 +++++++++++ .../constellation/PhoneVerificationClient.kt | 80 + .../src/main/AndroidManifest.xml | 8 + .../gms/constellation/GetIidTokenRequest.aidl | 8 + .../constellation/GetIidTokenResponse.aidl | 8 + .../GetPnvCapabilitiesRequest.aidl | 8 + .../GetPnvCapabilitiesResponse.aidl | 8 + .../gms/constellation/IdTokenRequest.aidl | 8 + .../gms/constellation/ImsiRequest.aidl | 8 + .../gms/constellation/PhoneNumberInfo.aidl | 8 + .../PhoneNumberVerification.aidl | 8 + .../VerifyPhoneNumberRequest.aidl | 8 + .../VerifyPhoneNumberResponse.aidl | 8 + .../internal/IConstellationApiService.aidl | 19 + .../internal/IConstellationCallbacks.aidl | 19 + .../gms/constellation/GetIidTokenRequest.java | 43 + .../constellation/GetIidTokenResponse.java | 56 + .../GetPnvCapabilitiesRequest.java | 53 + .../GetPnvCapabilitiesResponse.java | 40 + .../gms/constellation/IdTokenRequest.java | 40 + .../gms/constellation/ImsiRequest.java | 40 + .../gms/constellation/PhoneNumberInfo.java | 49 + .../PhoneNumberVerification.java | 77 + .../VerifyPhoneNumberRequest.java | 81 + .../VerifyPhoneNumberResponse.java | 45 + .../main/proto/phonenumberverification.proto | 1425 +++++++++++++++++ play-services-core/build.gradle | 1 + .../src/main/AndroidManifest.xml | 1 - settings.gradle | 2 + 34 files changed, 3220 insertions(+), 1 deletion(-) create mode 100644 play-services-constellation/build.gradle 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/org/microg/gms/constellation/ConstellationService.kt create mode 100644 play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt create mode 100644 play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/PhoneVerificationClient.kt 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/IdTokenRequest.aidl create mode 100644 play-services-constellation/src/main/aidl/com/google/android/gms/constellation/ImsiRequest.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/PhoneNumberVerification.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/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/IdTokenRequest.java create mode 100644 play-services-constellation/src/main/java/com/google/android/gms/constellation/ImsiRequest.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/PhoneNumberVerification.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 create mode 100644 play-services-core-proto/src/main/proto/phonenumberverification.proto diff --git a/.gitignore b/.gitignore index 8db18dfeff..cc4b1f7afe 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ local.properties .settings .classpath .project +personal-docs/ diff --git a/play-services-constellation/build.gradle b/play-services-constellation/build.gradle new file mode 100644 index 0000000000..8bc1af681e --- /dev/null +++ b/play-services-constellation/build.gradle @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +android { + namespace "com.google.android.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 + } +} + +apply from: '../gradle/publish-android.gradle' + +description = 'microG implementation of play-services-constellation' + +dependencies { + api project(':play-services-base') + api project(':play-services-basement') + api project(':play-services-tasks') + + annotationProcessor 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..e06b39e52a --- /dev/null +++ b/play-services-constellation/core/build.gradle @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +dependencies { + implementation project(':play-services-constellation') + implementation project(':play-services-base-core') + implementation project(':play-services-core-proto') + implementation project(':play-services-droidguard') + implementation project(':play-services-tasks-ktx') + + implementation "com.squareup.wire:wire-runtime:$wireVersion" + implementation "com.squareup.wire:wire-grpc-client:$wireVersion" + implementation "com.squareup.okhttp3:okhttp:$okHttpVersion" + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion" + + implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion" +} + +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 = 1.8 + targetCompatibility = 1.8 + } + + kotlinOptions { + jvmTarget = 1.8 + } +} + +apply from: '../../gradle/publish-android.gradle' + +description = 'microG service implementation for play-services-constellation' 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..accf8dd2f8 --- /dev/null +++ b/play-services-constellation/core/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationService.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationService.kt new file mode 100644 index 0000000000..8ad7bfe46f --- /dev/null +++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationService.kt @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.constellation + +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.Feature +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 org.microg.gms.BaseService +import org.microg.gms.common.GmsService + +private const val TAG = "ConstellationService" + +class ConstellationService : BaseService(TAG, GmsService.CONSTELLATION) { + override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) { + val connectionInfo = ConnectionInfo() + connectionInfo.features = arrayOf( + Feature("constellation", 1), + Feature("constellation_phone_number_verification", 1) + ) + callback.onPostInitCompleteWithConnectionInfo( + ConnectionResult.SUCCESS, + ConstellationServiceImpl(this, request.packageName).asBinder(), + connectionInfo + ) + } +} diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt new file mode 100644 index 0000000000..396a5231e5 --- /dev/null +++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt @@ -0,0 +1,915 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.constellation + +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Bundle +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import android.util.Base64 +import android.util.Log +import com.google.android.gms.common.api.CommonStatusCodes +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.GetPnvCapabilitiesRequest +import com.google.android.gms.constellation.GetPnvCapabilitiesResponse +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.IConstellationApiService +import com.google.android.gms.constellation.internal.IConstellationCallbacks +import com.google.android.gms.droidguard.DroidGuardClient +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import com.google.android.gms.tasks.await +import org.microg.gms.checkin.LastCheckinInfo +import org.microg.gms.phonenumberverification.* +import org.microg.gms.phonenumberverification.Error as ProtoError +import java.security.KeyPairGenerator +import java.security.MessageDigest +import java.security.spec.ECGenParameterSpec +import java.util.Locale +import java.util.UUID + +private const val TAG = "ConstellationImpl" +private const val CONSTELLATION_AUTHORIZED_ENTITY = "496232013492" +private const val GMSCORE_VERSION_NUMBER = 244631038 +private const val GMSCORE_VERSION_STRING = "24.46.31 (190408-693505712)" + +class ConstellationServiceImpl( + private val context: Context, + private val packageName: String +) : IConstellationApiService.Stub() { + + private val scope = CoroutineScope(Dispatchers.IO) + + override fun verifyPhoneNumber(callbacks: IConstellationCallbacks?, request: VerifyPhoneNumberRequest?, apiMetadata: Bundle?) { + Log.d(TAG, "verifyPhoneNumber called by $packageName, policyId=${request?.upiPolicyId}") + if (callbacks == null || request == null) return + + scope.launch { + try { + doVerifyPhoneNumber(callbacks, request) + } catch (e: Exception) { + Log.e(TAG, "verifyPhoneNumber failed", e) + runCatching { + callbacks.onPhoneNumberVerificationsCompleted( + Status(CommonStatusCodes.INTERNAL_ERROR, "Verification failed: ${e.message}"), + null, null + ) + } + } + } + } + + override fun getIidToken(callbacks: IConstellationCallbacks?, request: GetIidTokenRequest?, apiMetadata: Bundle?) { + Log.d(TAG, "getIidToken called by $packageName, appId=${request?.appId}") + if (callbacks == null) return + + scope.launch { + try { + val iidToken = getOrCreateIidToken() + val response = GetIidTokenResponse( + iidToken, null, null, System.currentTimeMillis() + ) + runCatching { + callbacks.onIidTokenGenerated(Status.SUCCESS, response, null) + } + } catch (e: Exception) { + Log.e(TAG, "getIidToken failed", e) + runCatching { + callbacks.onIidTokenGenerated( + Status(CommonStatusCodes.INTERNAL_ERROR, e.message), + null, null + ) + } + } + } + } + + override fun getPnvCapabilities(callbacks: IConstellationCallbacks?, request: GetPnvCapabilitiesRequest?, apiMetadata: Bundle?) { + Log.d(TAG, "getPnvCapabilities called by $packageName, policyId=${request?.policyId}") + if (callbacks == null) return + + runCatching { + callbacks.onGetPnvCapabilitiesCompleted( + Status.SUCCESS, + GetPnvCapabilitiesResponse(listOf("TS43", "MT_SMS", "MO_SMS")), + null + ) + } + } + + // ---- Core verification flow ---- + + private suspend fun doVerifyPhoneNumber( + callbacks: IConstellationCallbacks, + request: VerifyPhoneNumberRequest + ) { + val sessionId = UUID.randomUUID().toString() + Log.d(TAG, "Starting verification session=$sessionId") + + // Step 1: Gather device signals + val iidToken = getOrCreateIidToken() + val checkinInfo = LastCheckinInfo.read(context) + val dgResult = getDroidGuardResult(iidToken) + val spatulaHeader = getSpatulaHeaderSafe() ?: throw IllegalStateException( + "Unable to obtain x-goog-spatula header for $packageName" + ) + val ecKeyPair = generateEcKeyPair() + + Log.d(TAG, "Device signals gathered: iid=${iidToken.take(20)}..., androidId=${checkinInfo.androidId}") + + // Step 2: Build SyncRequest + val syncRequest = buildSyncRequest( + request, sessionId, iidToken, checkinInfo.androidId, dgResult, ecKeyPair + ) + + // Step 3: Call Sync + val client = PhoneVerificationClient(context) { spatulaHeader } + val syncResponse = client.sync(syncRequest) + Log.d(TAG, "SyncResponse received: ${syncResponse.responses.size} responses") + + // Step 4: Process SyncResponse + handleSyncResponse(syncResponse, sessionId, iidToken, checkinInfo.androidId, + dgResult, ecKeyPair, client, callbacks) + } + + private suspend fun handleSyncResponse( + syncResponse: SyncResponse, + sessionId: String, + iidToken: String, + androidId: Long, + dgResult: String, + ecKeyPair: java.security.KeyPair, + client: PhoneVerificationClient, + callbacks: IConstellationCallbacks + ) { + if (syncResponse.responses.isEmpty()) { + Log.w(TAG, "Empty SyncResponse") + runCatching { + callbacks.onPhoneNumberVerificationsCompleted( + Status(CommonStatusCodes.INTERNAL_ERROR, "Empty server response"), + null, null + ) + } + return + } + + val verificationResponse = syncResponse.responses.first() + val verification = verificationResponse.verification ?: run { + Log.w(TAG, "No verification in response, error=${verificationResponse.error}") + runCatching { + callbacks.onPhoneNumberVerificationsCompleted( + Status(CommonStatusCodes.INTERNAL_ERROR, + "Server error: ${verificationResponse.error?.message}"), + null, null + ) + } + return + } + + when (verification.state) { + VerificationState.VERIFICATION_STATE_VERIFIED -> { + handleVerifiedState(verification, callbacks) + } + VerificationState.VERIFICATION_STATE_PENDING -> { + handlePendingState(verification, sessionId, iidToken, androidId, + dgResult, ecKeyPair, client, callbacks) + } + else -> { + Log.w(TAG, "Unexpected verification state: ${verification.state}") + runCatching { + callbacks.onPhoneNumberVerificationsCompleted( + Status(CommonStatusCodes.INTERNAL_ERROR, + "Unexpected state: ${verification.state}"), + null, null + ) + } + } + } + } + + private fun handleVerifiedState( + verification: Verification, + callbacks: IConstellationCallbacks + ) { + val info = verification.verification_info + val phoneNumber = info?.phone_number ?: "" + val timestamp = info?.verification_time?.epochSecond ?: (System.currentTimeMillis() / 1000) + val msisdnToken = extractMsisdnToken(verification) + + Log.d(TAG, "Phone verified: $phoneNumber") + if (msisdnToken.isNullOrBlank()) { + Log.w(TAG, "Verified state missing msisdn token payload") + runCatching { + callbacks.onPhoneNumberVerificationsCompleted( + Status(CommonStatusCodes.INTERNAL_ERROR, "Missing msisdn token in verification"), + null, + null + ) + } + return + } + + val pnv = PhoneNumberVerification( + phoneNumber, // field 1: phoneNumber + timestamp * 1000, // field 2: timestampMillis + 1, // field 3: verificationMethod (1 = server-verified) + 0, // field 4: unknownInt + msisdnToken, // field 5: msisdnToken (opaque server token) + null, // field 6: extras + 1, // field 7: verificationStatus (1 = SUCCESS) + 0 // field 8: retryAfterSeconds + ) + + val response = VerifyPhoneNumberResponse( + arrayOf(pnv), null + ) + + runCatching { + callbacks.onPhoneNumberVerificationsCompleted(Status.SUCCESS, response, null) + } + } + + private suspend fun handlePendingState( + verification: Verification, + sessionId: String, + iidToken: String, + androidId: Long, + dgResult: String, + ecKeyPair: java.security.KeyPair, + client: PhoneVerificationClient, + callbacks: IConstellationCallbacks + ) { + val pendingInfo = verification.pending_verification_info + val challenge = pendingInfo?.challenge + + if (challenge == null) { + Log.w(TAG, "PENDING state but no challenge provided") + runCatching { + callbacks.onPhoneNumberVerificationsCompleted( + Status(CommonStatusCodes.INTERNAL_ERROR, "No challenge in PENDING state"), + null, null + ) + } + return + } + + Log.d(TAG, "Challenge type=${challenge.type}, id=${challenge.challenge_id?.id}") + + when (challenge.type) { + ChallengeType.CHALLENGE_TYPE_TS43 -> { + handleTs43Challenge(verification, challenge, sessionId, iidToken, + androidId, dgResult, ecKeyPair, client, callbacks) + } + ChallengeType.CHALLENGE_TYPE_IMSI_LOOKUP -> { + // IMSI lookup is server-side — just call Proceed immediately + Log.d(TAG, "IMSI_LOOKUP challenge, calling Proceed") + callProceedAndRespond(verification, ChallengeResponse(), + sessionId, iidToken, androidId, dgResult, ecKeyPair, client, callbacks) + } + else -> { + Log.w(TAG, "Unsupported challenge type: ${challenge.type}") + runCatching { + callbacks.onPhoneNumberVerificationsCompleted( + Status(CommonStatusCodes.INTERNAL_ERROR, + "Unsupported challenge: ${challenge.type}"), + null, null + ) + } + } + } + } + + private suspend fun handleTs43Challenge( + verification: Verification, + challenge: Challenge, + sessionId: String, + iidToken: String, + androidId: Long, + dgResult: String, + ecKeyPair: java.security.KeyPair, + client: PhoneVerificationClient, + callbacks: IConstellationCallbacks + ) { + val ts43 = challenge.ts43_challenge ?: run { + Log.w(TAG, "TS43 challenge type but no ts43_challenge data") + runCatching { + callbacks.onPhoneNumberVerificationsCompleted( + Status(CommonStatusCodes.INTERNAL_ERROR, "Missing TS43 challenge data"), + null, null + ) + } + return + } + + Log.d(TAG, "TS43 challenge: url=${ts43.entitlement_url}, appId=${ts43.app_id}, " + + "realm=${ts43.eap_aka_realm}, integrator=${ts43.ts43_type?.integrator}") + + try { + val challengeResponse = performTs43Authentication(ts43) + callProceedAndRespond(verification, challengeResponse, + sessionId, iidToken, androidId, dgResult, ecKeyPair, client, callbacks) + } catch (e: Exception) { + Log.e(TAG, "TS43 authentication failed", e) + // Send error response in Proceed so server knows what happened + val errorResponse = ChallengeResponse( + ts43_challenge_response = Ts43ChallengeResponse( + ts43_type = ts43.ts43_type, + error = ProtoError( + error_type = ErrorType.ERROR_TYPE_RUNTIME_ERROR, + service_entitlement_error = ServiceEntitlementError( + error_code = -1, + http_status = 0, + api = "acquireTemporaryToken" + ) + ) + ) + ) + callProceedAndRespond(verification, errorResponse, + sessionId, iidToken, androidId, dgResult, ecKeyPair, client, callbacks) + } + } + + private fun performTs43Authentication(ts43: Ts43Challenge): ChallengeResponse { + val entitlementUrl = ts43.entitlement_url + val appId = ts43.app_id + + Log.d(TAG, "Performing TS43 auth: url=$entitlementUrl, appId=$appId") + + // Use Android's service entitlement library via reflection (AOSP framework component) + // Available on Android 12+ as com.android.libraries.entitlement + val temporaryToken = try { + performTs43ViaEntitlementLibrary(entitlementUrl, appId, ts43) + } catch (e: Exception) { + Log.w(TAG, "TS43 entitlement library not available, trying direct approach", e) + performTs43Direct(ts43) + } + + Log.d(TAG, "TS43 temporary token obtained: ${temporaryToken.take(20)}...") + + return ChallengeResponse( + ts43_challenge_response = Ts43ChallengeResponse( + ts43_type = ts43.ts43_type, + server_challenge_response = ServerChallengeResponse( + temporary_token = temporaryToken + ) + ) + ) + } + + @Suppress("UNCHECKED_CAST") + private fun performTs43ViaEntitlementLibrary( + url: String, appId: String, ts43: Ts43Challenge + ): String { + // Attempt to use com.android.libraries.entitlement.Ts43Authentication + // This is an AOSP framework library available on Android 12+ with carrier support + val subId = getActiveSubscriptionId() + + val ts43AuthClass = Class.forName("com.android.libraries.entitlement.Ts43Authentication") + val entitlementVersion = ts43.service_entitlement_request?.entitlement_version ?: "2.0" + + // Ts43Authentication(context, url, entitlementVersion) + val constructor = ts43AuthClass.getConstructor( + Context::class.java, String::class.java, String::class.java + ) + val ts43Auth = constructor.newInstance(context, url, entitlementVersion) + + // getAuthToken(slotIndex, appId, packageName, "1.0") + val getAuthTokenMethod = ts43AuthClass.getMethod( + "getAuthToken", Int::class.javaPrimitiveType, + String::class.java, String::class.java, String::class.java + ) + val slotIndex = getSlotIndex(subId) + val authResult = getAuthTokenMethod.invoke(ts43Auth, slotIndex, appId, packageName, "1.0") + + // authResult.token() + val tokenMethod = authResult.javaClass.getMethod("token") + val authToken = tokenMethod.invoke(authResult) as String + + Log.d(TAG, "EAP-AKA auth token obtained") + + // Now acquire temporary token + val ts43OpClass = Class.forName("com.android.libraries.entitlement.Ts43Operation") + val opConstructor = ts43OpClass.getConstructor( + Context::class.java, Int::class.javaPrimitiveType, String::class.java, + String::class.java, String::class.java, Int::class.javaPrimitiveType, + String::class.java + ) + val ts43Op = opConstructor.newInstance( + context, slotIndex, url, entitlementVersion, authToken, 0, packageName + ) + + // Some platform builds expose a no-arg acquireTemporaryToken(). If the signature differs + // we fall back to the direct path instead of passing null-filled placeholders. + val acquireMethod = ts43OpClass.methods.first { it.name == "acquireTemporaryToken" } + if (acquireMethod.parameterCount != 0) { + throw IllegalStateException( + "Unsupported acquireTemporaryToken signature with ${acquireMethod.parameterCount} parameters" + ) + } + val tokenResponse = acquireMethod.invoke(ts43Op) + + val tempTokenMethod = tokenResponse.javaClass.getMethod("temporaryToken") + return tempTokenMethod.invoke(tokenResponse) as String + } + + private fun performTs43Direct( + ts43: Ts43Challenge + ): String { + val entitlementRequest = ts43.service_entitlement_request + val existingTemporaryToken = entitlementRequest?.temporary_token?.takeIf { it.isNotBlank() } + if (existingTemporaryToken != null) { + Log.i(TAG, "Using temporary token from ServiceEntitlementRequest direct fallback") + return existingTemporaryToken + } + throw UnsupportedOperationException( + "TS43 entitlement library unavailable and no temporary token in challenge payload" + ) + } + + private suspend fun callProceedAndRespond( + verification: Verification, + challengeResponse: ChallengeResponse, + sessionId: String, + iidToken: String, + androidId: Long, + dgResult: String, + ecKeyPair: java.security.KeyPair, + client: PhoneVerificationClient, + callbacks: IConstellationCallbacks + ) { + val header = buildRequestHeader(sessionId, iidToken, androidId, dgResult, ecKeyPair) + + val proceedRequest = ProceedRequest( + verification = verification, + challenge_response = challengeResponse, + header_ = header + ) + + Log.d(TAG, "Calling Proceed...") + val proceedResponse = client.proceed(proceedRequest) + + val resultVerification = proceedResponse.verification + if (resultVerification == null) { + Log.w(TAG, "No verification in ProceedResponse") + runCatching { + callbacks.onPhoneNumberVerificationsCompleted( + Status(CommonStatusCodes.INTERNAL_ERROR, "Empty Proceed response"), + null, null + ) + } + return + } + + Log.d(TAG, "Proceed result state=${resultVerification.state}") + + when (resultVerification.state) { + VerificationState.VERIFICATION_STATE_VERIFIED -> { + handleVerifiedState(resultVerification, callbacks) + } + VerificationState.VERIFICATION_STATE_PENDING -> { + // Still pending — could be retryable + val retryChallenge = resultVerification.pending_verification_info?.challenge + val retryAfterSec = retryChallenge?.expiry_time?.timestamp?.epochSecond?.let { + (it - System.currentTimeMillis() / 1000).coerceAtLeast(0) + } ?: 30L + + Log.w(TAG, "Still PENDING after Proceed, retryAfter=${retryAfterSec}s") + + val pnv = PhoneNumberVerification( + null, System.currentTimeMillis(), 0, 0, null, null, + 2, // verificationStatus 2 = FAILED (retryable) + retryAfterSec // retryAfterSeconds + ) + val response = VerifyPhoneNumberResponse(arrayOf(pnv), null) + runCatching { + callbacks.onPhoneNumberVerificationsCompleted(Status.SUCCESS, response, null) + } + } + else -> { + Log.w(TAG, "Verification failed: state=${resultVerification.state}") + val pnv = PhoneNumberVerification( + null, System.currentTimeMillis(), 0, 0, null, null, + 9, 0 // verificationStatus 9 = DENIED + ) + val response = VerifyPhoneNumberResponse(arrayOf(pnv), null) + runCatching { + callbacks.onPhoneNumberVerificationsCompleted(Status.SUCCESS, response, null) + } + } + } + } + + // ---- Request building ---- + + private fun buildSyncRequest( + request: VerifyPhoneNumberRequest, + sessionId: String, + iidToken: String, + androidId: Long, + dgResult: String, + ecKeyPair: java.security.KeyPair + ): SyncRequest { + val header = buildRequestHeader(sessionId, iidToken, androidId, dgResult, ecKeyPair) + val verification = buildVerificationFromRequest(request) + + return SyncRequest( + header_ = header, + verifications = listOf(verification) + ) + } + + private fun buildRequestHeader( + sessionId: String, + iidToken: String, + androidId: Long, + dgResult: String, + ecKeyPair: java.security.KeyPair + ): RequestHeader { + val deviceId = DeviceId( + iid_token = iidToken, + device_android_id = androidId, + user_android_id = androidId + ) + + val publicKeyBytes = ecKeyPair.public.encoded + val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + + val clientInfo = ClientInfo( + device_id = deviceId, + client_public_key = okio.ByteString.of(*publicKeyBytes), + locale = Locale.getDefault().toString(), + gmscore_version_number = GMSCORE_VERSION_NUMBER, + gmscore_version = GMSCORE_VERSION_STRING, + android_sdk_version = Build.VERSION.SDK_INT, + device_signals = DeviceSignals( + droidguard_result = dgResult // Field 2 — REQUIRED (the PR #3269 fix) + ), + country_info = buildCountryInfo(tm), + connectivity_infos = buildConnectivityInfo(), + model = Build.MODEL, + manufacturer = Build.MANUFACTURER, + device_type = DeviceType.DEVICE_TYPE_PHONE, + device_fingerprint = Build.FINGERPRINT, + user_profile_type = UserProfileType.REGULAR_USER, + has_read_privileged_phone_state_permission = false + ) + + return RequestHeader( + client_info = clientInfo, + session_id = sessionId, + trigger = RequestTrigger(type = TriggerType.TRIGGER_TYPE_TRIGGER_API_CALL) + ) + } + + private fun buildVerificationFromRequest( + request: VerifyPhoneNumberRequest + ): Verification { + val extras = request.extras + val policyId = request.upiPolicyId ?: "" + val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + + // Build SIM association + val imsi = extras?.getString("IMSI") ?: getImsiSafe(tm) + val simInfo = SIMInfo( + imsi = if (imsi.isNotEmpty()) listOf(imsi) else emptyList(), + iccid = getIccidSafe() + ) + + val subId = getActiveSubscriptionId() + val simSlot = SIMSlot( + index = getSlotIndex(subId), + sub_id = subId + ) + + val association = VerificationAssociation( + sim = SIMAssociation(sim_info = simInfo, sim_slot = simSlot) + ) + + // Build telephony info + val telephonyInfo = buildTelephonyInfo(tm) + + // Build structured API params from the AIDL request + val idTokenReq = request.idTokenRequest + val structuredParams = StructuredAPIParams( + policy_id = policyId, + calling_package = packageName, + id_token_request = if (idTokenReq != null) { + org.microg.gms.phonenumberverification.IdTokenRequest( + certificate_hash = idTokenReq.hash ?: "", + token_nonce = idTokenReq.appId ?: "" + ) + } else null, + imsi_requests = request.imsis?.map { imsiReq -> + IMSIRequest( + imsi = imsiReq.imsi ?: "", + phone_number_hint = imsiReq.carrierId + ) + } ?: if (imsi.isNotEmpty()) listOf(IMSIRequest(imsi = imsi)) else emptyList() + ) + + // Build API params from the extras bundle + val apiParams = mutableListOf() + extras?.keySet()?.forEach { key -> + extras.getString(key)?.let { value -> + apiParams.add(Param(name = key, value_ = value)) + } + } + + // Challenge preference with random localized message template for silent MT SMS + val mtTemplate = Base64.encodeToString( + ByteArray(8).also { java.security.SecureRandom().nextBytes(it) }, + Base64.NO_WRAP + ) + + return Verification( + association = association, + state = VerificationState.VERIFICATION_STATE_NONE, + telephony_info = telephonyInfo, + structured_api_params = structuredParams, + api_params = apiParams, + challenge_preference = ChallengePreference( + mt_preference = MTChallengePreference( + localized_message_template = mtTemplate + ) + ) + ) + } + + // ---- Device info helpers ---- + + private fun buildTelephonyInfo(tm: TelephonyManager): TelephonyInfo { + val simOperator = tm.simOperator ?: "" + val simCountry = tm.simCountryIso ?: "" + val networkOperator = tm.networkOperator ?: "" + val networkCountry = tm.networkCountryIso ?: "" + + return TelephonyInfo( + sim_state = if (tm.simState == TelephonyManager.SIM_STATE_READY) + SIMState.SIM_READY else SIMState.SIM_NOT_READY, + sim_operator = MobileOperatorInfo( + country_code = simCountry, + mcc_mnc = simOperator, + operator_name = tm.simOperatorName ?: "" + ), + network_operator = MobileOperatorInfo( + country_code = networkCountry, + mcc_mnc = networkOperator, + operator_name = tm.networkOperatorName ?: "" + ), + network_roaming = if (tm.isNetworkRoaming) + RoamingState.ROAMING_STATE_ROAMING else RoamingState.ROAMING_STATE_NOT_ROAMING, + sms_capability = SMSCapability.SMS_CAPABILITY_CAPABLE, + service_state = ServiceState.SERVICE_STATE_IN_SERVICE, + imei = getImeiSafe(tm), + subscription_count = getActiveSubscriptionCount(), + subscription_count_max = getMaxSubscriptionCount() + ) + } + + private fun buildCountryInfo(tm: TelephonyManager): CountryInfo { + val simCountry = tm.simCountryIso ?: "" + val networkCountry = tm.networkCountryIso ?: "" + return CountryInfo( + sim_countries = if (simCountry.isNotEmpty()) listOf(simCountry) else emptyList(), + network_countries = if (networkCountry.isNotEmpty()) listOf(networkCountry) else emptyList() + ) + } + + private fun buildConnectivityInfo(): List { + val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager + ?: return emptyList() + + val infos = mutableListOf() + val activeNetwork = cm.activeNetwork + val caps = if (activeNetwork != null) cm.getNetworkCapabilities(activeNetwork) else null + + if (caps != null) { + if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + infos.add(ConnectivityInfo( + type = ConnectivityType.CONNECTIVITY_TYPE_WIFI, + state = ConnectivityState.CONNECTIVITY_STATE_CONNECTED, + availability = ConnectivityAvailability.CONNECTIVITY_AVAILABLE + )) + } + if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + infos.add(ConnectivityInfo( + type = ConnectivityType.CONNECTIVITY_TYPE_MOBILE, + state = ConnectivityState.CONNECTIVITY_STATE_CONNECTED, + availability = ConnectivityAvailability.CONNECTIVITY_AVAILABLE + )) + } + } + return infos + } + + // ---- Token / Key helpers ---- + + private suspend fun getDroidGuardResult(iidToken: String): String { + val iidHash = sha256Hex(iidToken) + val data = mapOf("iidHash" to iidHash, "rpc" to "sync") + return try { + DroidGuardClient.getResults(context, "constellation_verify", data).await() + } catch (e: Exception) { + Log.w(TAG, "DroidGuard failed, using empty result", e) + "" + } + } + + private suspend fun getOrCreateIidToken(): String { + // Generate a stable IID-like token for Constellation. + // Real GMS uses a backend-issued IID/FCM token. We keep this stable per-install and + // include checkin/android IDs so the value is deterministic across process restarts. + return try { + val prefs = context.getSharedPreferences("constellation_prefs", Context.MODE_PRIVATE) + val cached = prefs.getString("iid_token", null) + if (cached != null) return cached + + val gcmToken = getCachedGcmRegistrationToken() + if (!gcmToken.isNullOrBlank()) { + prefs.edit().putString("iid_token", gcmToken).apply() + Log.i(TAG, "Using cached GCM registration token as IID") + return gcmToken + } + + val checkinInfo = LastCheckinInfo.read(context) + val installationId = prefs.getString("installation_id", null) ?: UUID.randomUUID().toString().replace("-", "") + if (prefs.getString("installation_id", null) == null) { + prefs.edit().putString("installation_id", installationId).apply() + } + val raw = "$CONSTELLATION_AUTHORIZED_ENTITY:$installationId:${checkinInfo.androidId}" + val digest = Base64.encodeToString( + sha256Bytes(raw), + Base64.NO_WRAP or Base64.URL_SAFE or Base64.NO_PADDING + ) + val token = "APA91b$digest" + + prefs.edit().putString("iid_token", token).apply() + token + } catch (e: Exception) { + Log.w(TAG, "IID token generation fallback", e) + val checkinInfo = LastCheckinInfo.read(context) + "APA91b" + Base64.encodeToString( + sha256Bytes("constellation:${checkinInfo.androidId}"), + Base64.NO_WRAP or Base64.URL_SAFE or Base64.NO_PADDING + ) + } + } + + private fun getCachedGcmRegistrationToken(): String? { + // Read existing registration IDs from GCM database if available. + // This is closer to a real IID/FCM identity than a synthetic token. + return queryGcmRegistrationToken(packageName) + ?: queryGcmRegistrationToken("com.google.android.gms") + } + + @Suppress("UNCHECKED_CAST") + private fun queryGcmRegistrationToken(targetPackage: String): String? { + return try { + val dbClass = Class.forName("org.microg.gms.gcm.GcmDatabase") + val constructor = dbClass.getConstructor(Context::class.java) + val database = constructor.newInstance(context) + val getRegistrations = dbClass.getMethod("getRegistrationsByApp", String::class.java) + val registrations = getRegistrations.invoke(database, targetPackage) as? List + val latest = registrations + ?.maxByOrNull { reg -> + runCatching { reg.javaClass.getField("timestamp").getLong(reg) }.getOrDefault(0L) + } + val token = latest + ?.let { reg -> runCatching { reg.javaClass.getField("registerId").get(reg) as? String }.getOrNull() } + ?.takeIf { it.isNotBlank() } + runCatching { dbClass.getMethod("close").invoke(database) } + token + } catch (e: Exception) { + Log.d(TAG, "No cached GCM registration token for $targetPackage", e) + null + } + } + + private fun generateEcKeyPair(): java.security.KeyPair { + val keyGen = KeyPairGenerator.getInstance("EC") + keyGen.initialize(ECGenParameterSpec("secp256r1")) + return keyGen.generateKeyPair() + } + + private fun getSpatulaHeaderSafe(): String? { + return try { + // AppCertServiceImpl exposes a non-suspend getSpatulaHeader(String) entrypoint. + val implClass = Class.forName("org.microg.gms.auth.appcert.AppCertServiceImpl") + val constructor = implClass.getDeclaredConstructor(Context::class.java) + constructor.isAccessible = true + val impl = constructor.newInstance(context) + val method = implClass.getMethod("getSpatulaHeader", String::class.java) + val header = method.invoke(impl, packageName) as? String + header?.takeIf { it.isNotBlank() } + } catch (e: Exception) { + Log.w(TAG, "Spatula header unavailable", e) + null + } + } + + private fun extractMsisdnToken(verification: Verification): String? { + val tokenLikeKeys = setOf( + "rcs_msisdn_token", + "msisdn_token", + "msisdntoken", + "carrier_rcs_msisdn_token" + ) + val fromApiParams = verification.api_params.firstNotNullOfOrNull { param -> + val key = param.name.lowercase(Locale.US) + if (key in tokenLikeKeys || (key.contains("msisdn") && key.contains("token"))) { + param.value_.takeIf { it.isNotBlank() } + } else null + } + if (!fromApiParams.isNullOrBlank()) return fromApiParams + + // Fallback: use the opaque verification token bytes from VERIFIED state. + val verificationToken = verification.verification_info + ?.verification_token + ?.token + ?.takeIf { it.size > 0 } + ?.let { + Base64.encodeToString( + it.toByteArray(), + Base64.NO_WRAP or Base64.URL_SAFE or Base64.NO_PADDING + ) + } + return verificationToken?.takeIf { it.isNotBlank() } + } + + // ---- Telephony helpers ---- + + @Suppress("MissingPermission") + private fun getImsiSafe(tm: TelephonyManager): String { + return try { tm.subscriberId ?: "" } catch (_: Exception) { "" } + } + + @Suppress("MissingPermission") + private fun getImeiSafe(tm: TelephonyManager): String { + return try { + if (Build.VERSION.SDK_INT >= 26) tm.imei ?: "" else "" + } catch (_: Exception) { "" } + } + + @Suppress("MissingPermission") + private fun getIccidSafe(): String { + return try { + if (Build.VERSION.SDK_INT >= 28) { + val sm = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as? SubscriptionManager + sm?.activeSubscriptionInfoList?.firstOrNull()?.iccId ?: "" + } else "" + } catch (_: Exception) { "" } + } + + private fun getActiveSubscriptionId(): Int { + return try { + if (Build.VERSION.SDK_INT >= 24) { + SubscriptionManager.getDefaultSubscriptionId() + } else 0 + } catch (_: Exception) { 0 } + } + + private fun getActiveSubscriptionCount(): Int { + return try { + val sm = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as? SubscriptionManager + sm?.activeSubscriptionInfoCount ?: 1 + } catch (_: Exception) { 1 } + } + + private fun getMaxSubscriptionCount(): Int { + return try { + val sm = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as? SubscriptionManager + sm?.activeSubscriptionInfoCountMax ?: 1 + } catch (_: Exception) { 1 } + } + + private fun getSlotIndex(subId: Int): Int { + return try { + if (Build.VERSION.SDK_INT >= 29) { + SubscriptionManager.getSlotIndex(subId) + } else 0 + } catch (_: Exception) { 0 } + } + + // ---- Crypto helpers ---- + + private fun sha256Hex(input: String): String { + val digest = MessageDigest.getInstance("SHA-256") + return digest.digest(input.toByteArray()).joinToString("") { "%02x".format(it) } + } + + private fun sha256Bytes(input: String): ByteArray { + return MessageDigest.getInstance("SHA-256").digest(input.toByteArray()) + } + +} diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/PhoneVerificationClient.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/PhoneVerificationClient.kt new file mode 100644 index 0000000000..9bbb9dbfe1 --- /dev/null +++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/PhoneVerificationClient.kt @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.constellation + +import android.content.Context +import android.util.Log +import com.squareup.wire.GrpcClient +import com.squareup.wire.Service +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.Response +import org.microg.gms.phonenumberverification.PhoneDeviceVerificationClient +import org.microg.gms.phonenumberverification.ProceedRequest +import org.microg.gms.phonenumberverification.ProceedResponse +import org.microg.gms.phonenumberverification.SyncRequest +import org.microg.gms.phonenumberverification.SyncResponse +import java.util.concurrent.TimeUnit + +private const val TAG = "PhoneVerificationClient" +private const val BASE_URL = "https://phonedeviceverification-pa.googleapis.com" +private const val API_KEY = "AIzaSyAP-gfH3qvi6vgHZbSYwQ_XHqV_mXHhzIk" +private const val GMS_PACKAGE_NAME = "com.google.android.gms" +private const val GMS_PACKAGE_SIGNATURE_SHA1 = "38918a453d07199354f8b19af05ec6562ced5788" + +class PhoneVerificationClient( + private val context: Context, + private val spatulaHeaderProvider: suspend () -> String +) { + private fun buildOkHttpClient(spatulaHeader: String): OkHttpClient { + return OkHttpClient.Builder() + .addInterceptor(ConstellationHeaderInterceptor(spatulaHeader)) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build() + } + + private fun buildGrpcClient(spatulaHeader: String): PhoneDeviceVerificationClient { + val okHttpClient = buildOkHttpClient(spatulaHeader) + val grpcClient = GrpcClient.Builder() + .client(okHttpClient) + .baseUrl(BASE_URL) + .minMessageToCompress(Long.MAX_VALUE) + .build() + return grpcClient.create(PhoneDeviceVerificationClient::class) + } + + suspend fun sync(request: SyncRequest): SyncResponse { + val spatula = spatulaHeaderProvider() + val client = buildGrpcClient(spatula) + Log.d(TAG, "Calling Sync with session=${request.header_?.session_id}") + return client.Sync().execute(request) + } + + suspend fun proceed(request: ProceedRequest): ProceedResponse { + val spatula = spatulaHeaderProvider() + val client = buildGrpcClient(spatula) + Log.d(TAG, "Calling Proceed with session=${request.header_?.session_id}") + return client.Proceed().execute(request) + } +} + +private class ConstellationHeaderInterceptor( + private val spatulaHeader: String +) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request().newBuilder() + .header("x-goog-api-key", API_KEY) + .header("x-android-package", GMS_PACKAGE_NAME) + .header("x-android-cert", GMS_PACKAGE_SIGNATURE_SHA1) + .header("x-goog-spatula", spatulaHeader) + .header("te", "trailers") + .header("user-agent", "grpc-java-okhttp/1.66.0-SNAPSHOT") + .build() + return chain.proceed(request) + } +} diff --git a/play-services-constellation/src/main/AndroidManifest.xml b/play-services-constellation/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..d2f02fd268 --- /dev/null +++ b/play-services-constellation/src/main/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + 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..069e993465 --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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..b242d2192c --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetIidTokenResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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..19ff065a48 --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.constellation; + +parcelable GetPnvCapabilitiesRequest; 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..ae8d0f7950 --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.constellation; + +parcelable GetPnvCapabilitiesResponse; diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/IdTokenRequest.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/IdTokenRequest.aidl new file mode 100644 index 0000000000..3373fba261 --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/IdTokenRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.constellation; + +parcelable IdTokenRequest; diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/ImsiRequest.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/ImsiRequest.aidl new file mode 100644 index 0000000000..021e0e4033 --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/ImsiRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.constellation; + +parcelable ImsiRequest; 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..e3f139397a --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/PhoneNumberInfo.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.constellation; + +parcelable PhoneNumberInfo; diff --git a/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/PhoneNumberVerification.aidl b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/PhoneNumberVerification.aidl new file mode 100644 index 0000000000..565463e13d --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/PhoneNumberVerification.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.constellation; + +parcelable PhoneNumberVerification; 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..abfdb4a45f --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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..7edd93f2e2 --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/VerifyPhoneNumberResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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..c8478f69a6 --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationApiService.aidl @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.constellation.internal; + +import android.os.Bundle; +import com.google.android.gms.constellation.VerifyPhoneNumberRequest; +import com.google.android.gms.constellation.GetIidTokenRequest; +import com.google.android.gms.constellation.GetPnvCapabilitiesRequest; +import com.google.android.gms.constellation.internal.IConstellationCallbacks; + +interface IConstellationApiService { + // Transaction IDs aligned with recent Google Messages Constellation clients. + void verifyPhoneNumber(IConstellationCallbacks callbacks, in VerifyPhoneNumberRequest request, in Bundle apiMetadata) = 2; + void getIidToken(IConstellationCallbacks callbacks, in GetIidTokenRequest request, in Bundle apiMetadata) = 3; + void getPnvCapabilities(IConstellationCallbacks callbacks, in GetPnvCapabilitiesRequest request, in Bundle apiMetadata) = 4; +} 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..69c505c461 --- /dev/null +++ b/play-services-constellation/src/main/aidl/com/google/android/gms/constellation/internal/IConstellationCallbacks.aidl @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.constellation.internal; + +import com.google.android.gms.common.api.Status; +import com.google.android.gms.constellation.PhoneNumberInfo; +import com.google.android.gms.constellation.VerifyPhoneNumberResponse; +import com.google.android.gms.constellation.GetIidTokenResponse; +import com.google.android.gms.constellation.GetPnvCapabilitiesResponse; + +interface IConstellationCallbacks { + oneway void onPhoneNumberVerified(in Status status, in List phoneNumbers, in Bundle apiMetadata) = 1; + oneway void onPhoneNumberVerificationsCompleted(in Status status, in VerifyPhoneNumberResponse response, in Bundle apiMetadata) = 2; + oneway void onIidTokenGenerated(in Status status, in GetIidTokenResponse response, in Bundle apiMetadata) = 3; + oneway void onGetPnvCapabilitiesCompleted(in Status status, in GetPnvCapabilitiesResponse response, in Bundle apiMetadata) = 4; +} 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..736ceecc4e --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenRequest.java @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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; +import org.microg.gms.common.Hide; + +@Hide +@SafeParcelable.Class +public class GetIidTokenRequest extends AbstractSafeParcelable { + + @Field(1) + @Nullable + public String appId; + + @Field(2) + @Nullable + public String scope; + + private GetIidTokenRequest() { + } + + @Constructor + public GetIidTokenRequest(@Param(1) @Nullable String appId, @Param(2) @Nullable String scope) { + this.appId = appId; + this.scope = scope; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GetIidTokenRequest.class); +} 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..43c4469bc5 --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetIidTokenResponse.java @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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; +import org.microg.gms.common.Hide; + +@Hide +@SafeParcelable.Class +public class GetIidTokenResponse extends AbstractSafeParcelable { + + @Field(1) + @Nullable + public String token; + + @Field(2) + @Nullable + public String secondaryToken; + + @Field(3) + @Nullable + public byte[] signature; + + @Field(4) + public long timestamp; + + private GetIidTokenResponse() { + } + + @Constructor + public GetIidTokenResponse( + @Param(1) @Nullable String token, + @Param(2) @Nullable String secondaryToken, + @Param(3) @Nullable byte[] signature, + @Param(4) long timestamp) { + this.token = token; + this.secondaryToken = secondaryToken; + this.signature = signature; + this.timestamp = timestamp; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GetIidTokenResponse.class); +} 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..6bce71ede5 --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesRequest.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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; +import org.microg.gms.common.Hide; + +import java.util.List; + +@Hide +@SafeParcelable.Class +public class GetPnvCapabilitiesRequest extends AbstractSafeParcelable { + + @Field(1) + @Nullable + public String policyId; + + @Field(value = 2, subClass = String.class) + @Nullable + public List list1; + + @Field(value = 3, subClass = String.class) + @Nullable + public List list2; + + private GetPnvCapabilitiesRequest() { + } + + @Constructor + public GetPnvCapabilitiesRequest( + @Param(1) @Nullable String policyId, + @Param(2) @Nullable List list1, + @Param(3) @Nullable List list2) { + this.policyId = policyId; + this.list1 = list1; + this.list2 = list2; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GetPnvCapabilitiesRequest.class); +} 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..e800be932d --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/GetPnvCapabilitiesResponse.java @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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; +import org.microg.gms.common.Hide; + +import java.util.List; + +@Hide +@SafeParcelable.Class +public class GetPnvCapabilitiesResponse extends AbstractSafeParcelable { + + @Field(value = 1, subClass = String.class) + @Nullable + public List capabilities; + + private GetPnvCapabilitiesResponse() { + } + + @Constructor + public GetPnvCapabilitiesResponse(@Param(1) @Nullable List capabilities) { + this.capabilities = capabilities; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GetPnvCapabilitiesResponse.class); +} diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/IdTokenRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/IdTokenRequest.java new file mode 100644 index 0000000000..db3df3acec --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/IdTokenRequest.java @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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 org.microg.gms.common.Hide; + +@Hide +@SafeParcelable.Class +public class IdTokenRequest extends AbstractSafeParcelable { + + @Field(1) + public String appId; + + @Field(2) + public String hash; + + private IdTokenRequest() { + } + + @Constructor + public IdTokenRequest(@Param(1) String appId, @Param(2) String hash) { + this.appId = appId; + this.hash = hash; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(IdTokenRequest.class); +} diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/ImsiRequest.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/ImsiRequest.java new file mode 100644 index 0000000000..f2c68d37a1 --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/ImsiRequest.java @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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 org.microg.gms.common.Hide; + +@Hide +@SafeParcelable.Class +public class ImsiRequest extends AbstractSafeParcelable { + + @Field(1) + public String imsi; + + @Field(2) + public String carrierId; + + private ImsiRequest() { + } + + @Constructor + public ImsiRequest(@Param(1) String imsi, @Param(2) String carrierId) { + this.imsi = imsi; + this.carrierId = carrierId; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ImsiRequest.class); +} 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..08c1b51099 --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberInfo.java @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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; +import org.microg.gms.common.Hide; + +@Hide +@SafeParcelable.Class +public class PhoneNumberInfo extends AbstractSafeParcelable { + + @Field(1) + @Nullable + public String phoneNumber; + + @Field(2) + public long timestamp; + + @Field(3) + public int verificationMethod; + + private PhoneNumberInfo() { + } + + @Constructor + public PhoneNumberInfo( + @Param(1) @Nullable String phoneNumber, + @Param(2) long timestamp, + @Param(3) int verificationMethod) { + this.phoneNumber = phoneNumber; + this.timestamp = timestamp; + this.verificationMethod = verificationMethod; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(PhoneNumberInfo.class); +} diff --git a/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberVerification.java b/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberVerification.java new file mode 100644 index 0000000000..283dae0b1a --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/PhoneNumberVerification.java @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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; +import org.microg.gms.common.Hide; + +@Hide +@SafeParcelable.Class +public class PhoneNumberVerification extends AbstractSafeParcelable { + + @Field(1) + @Nullable + public String phoneNumber; + + @Field(2) + public long timestampMillis; + + @Field(3) + public int verificationMethod; + + @Field(4) + public int unknownInt; + + @Field(5) + @Nullable + public String msisdnToken; + + @Field(6) + @Nullable + public Bundle extras; + + @Field(7) + public int verificationStatus; + + @Field(8) + public long retryAfterSeconds; + + private PhoneNumberVerification() { + } + + @Constructor + public PhoneNumberVerification( + @Param(1) @Nullable String phoneNumber, + @Param(2) long timestampMillis, + @Param(3) int verificationMethod, + @Param(4) int unknownInt, + @Param(5) @Nullable String msisdnToken, + @Param(6) @Nullable Bundle extras, + @Param(7) int verificationStatus, + @Param(8) long retryAfterSeconds) { + this.phoneNumber = phoneNumber; + this.timestampMillis = timestampMillis; + this.verificationMethod = verificationMethod; + this.unknownInt = unknownInt; + this.msisdnToken = msisdnToken; + 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 final SafeParcelableCreatorAndWriter CREATOR = findCreator(PhoneNumberVerification.class); +} 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..5673b471de --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberRequest.java @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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; +import org.microg.gms.common.Hide; + +import java.util.List; + +@Hide +@SafeParcelable.Class +public class VerifyPhoneNumberRequest extends AbstractSafeParcelable { + + @Field(1) + @Nullable + public String upiPolicyId; + + @Field(2) + public long timestamp; + + @Field(3) + @Nullable + public IdTokenRequest idTokenRequest; + + @Field(4) + @Nullable + public Bundle extras; + + @Field(value = 5, subClass = ImsiRequest.class) + @Nullable + public List imsis; + + @Field(6) + public boolean flag; + + @Field(7) + public int withLocalRead; + + @Field(value = 8, subClass = String.class) + @Nullable + public List unknownList; + + private VerifyPhoneNumberRequest() { + } + + @Constructor + public VerifyPhoneNumberRequest( + @Param(1) @Nullable String upiPolicyId, + @Param(2) long timestamp, + @Param(3) @Nullable IdTokenRequest idTokenRequest, + @Param(4) @Nullable Bundle extras, + @Param(5) @Nullable List imsis, + @Param(6) boolean flag, + @Param(7) int withLocalRead, + @Param(8) @Nullable List unknownList) { + this.upiPolicyId = upiPolicyId; + this.timestamp = timestamp; + this.idTokenRequest = idTokenRequest; + this.extras = extras; + this.imsis = imsis; + this.flag = flag; + this.withLocalRead = withLocalRead; + this.unknownList = unknownList; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(VerifyPhoneNumberRequest.class); +} 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..44f96c35a8 --- /dev/null +++ b/play-services-constellation/src/main/java/com/google/android/gms/constellation/VerifyPhoneNumberResponse.java @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +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; +import org.microg.gms.common.Hide; + +@Hide +@SafeParcelable.Class +public class VerifyPhoneNumberResponse extends AbstractSafeParcelable { + + @Field(1) + public PhoneNumberVerification[] verifications; + + @Field(2) + @Nullable + public Bundle extras; + + private VerifyPhoneNumberResponse() { + } + + @Constructor + public VerifyPhoneNumberResponse( + @Param(1) PhoneNumberVerification[] verifications, + @Param(2) @Nullable Bundle extras) { + this.verifications = verifications; + this.extras = extras; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(VerifyPhoneNumberResponse.class); +} diff --git a/play-services-core-proto/src/main/proto/phonenumberverification.proto b/play-services-core-proto/src/main/proto/phonenumberverification.proto new file mode 100644 index 0000000000..f6b392f8f8 --- /dev/null +++ b/play-services-core-proto/src/main/proto/phonenumberverification.proto @@ -0,0 +1,1425 @@ +syntax = "proto3"; + +package google.internal.communications.phonedeviceverification.v1; + +option java_package = "org.microg.gms.phonenumberverification"; + +import "google/protobuf/timestamp.proto"; + +// Project Constellation aims at determining, verifying and maintaining the phone numbers of the Android devices +// so that Google services can use them for different applications, such as near-tier graphs. +// Google Phone Device Verification API is the internal API to the Constellation GMS core module which enables this project. +// (go/constellation-overview) +service PhoneDeviceVerification { + // Client tells the server about its current state. The server can initiate verifications at this time + // or refresh their states. If the new states of some verifications are pending, then client will call + // Proceed for just those verifications. This is called: + // - When the client is a new client coming online for the first time. It has checked the consent using GetConsent. + // - When the client detected an event that would put its last verification state(s) in question or introduces + // new unknown verifications (e.g. IMSI change, or SIM added). + // - Periodically based on server returned next_sync_time in last Sync call. + rpc Sync(SyncRequest) returns (SyncResponse); + + // When client's current verification state is pending, it calls this method to complete the verification. + // The response is a new verification state which can be: Verified (on success), None (on failure), + // or Pending (on retriable errors). The difference between this and Refresh is that current state is + // pending in this case, while it's verified (or expired) in the case of Refresh. + rpc Proceed(ProceedRequest) returns (ProceedResponse); + + // Checks if the constellation client can initiate the verification or not. If device has consent, + // the client can send all information about SIMs and Gaias. If some Gaias have consent, the client + // can only send information about those Gaias, and receive those verifications. + rpc GetConsent(GetConsentRequest) returns (GetConsentResponse); + + // Stores device level consent that allows Google to periodically verify the device's phone number. + rpc SetConsent(SetConsentRequest) returns (SetConsentResponse); +} + +// SyncRequest is the request to sync +message SyncRequest { + // (Required) + RequestHeader header = 4; + + // The current client state of verifications. + // - If this is new client calling the server for the first time, it should have NONE verifications for each SIM and Gaia on device. + // - If this is after a client detected event, it should have all server returned verifications that are *unaffected* by client change and NONE verifications for newly seen IMSI/Gaia (but none of the removed IMSI or Gaias). + // - If this is a periodic sync, it should have all server returned verifications, but updated with any client side changes that didn't warrant an immediate sync (e.g. IMSI should be the currently seen IMSI). + repeated Verification verifications = 3; + + // Verification tokens used in the backup and restore flow for verifying a new device that was phone number verified in the past http://goto.google.com/c11n-br-acquisition + repeated VerificationToken verification_tokens = 5; +} + +// SyncResponse is the response to sync +message SyncResponse { + // The server's state of all verifications in Request. It can be a verification status or an error. (Non empty) + repeated VerificationResponse responses = 1; + + // Time till by when the client needs to sync again. (Required) + ServerTimestamp next_sync_time = 2; + + // (Required) + ResponseHeader header = 3; + + // droidguard response + DroidGuardTokenResponse droidguard_token_response = 4; + + // Verification tokens used in the backup and restore flow for verifying a new device that was phone number verified in the past http://goto.google.com/c11n-br-acquisition + repeated VerificationToken verification_tokens = 5; +} + +// ProceedRequest is the request to proceed +message ProceedRequest { + // Pending Verification to proceed. (Required) + Verification verification = 2; + + // Client's response to the challenge. (Required for MT and CARRIER_ID) + ChallengeResponse challenge_response = 3; + + // (Required) + RequestHeader header = 4; +} + +// ProceedResponse is the response to proceed +message ProceedResponse { + // The new verification for the client. It can be Verified (on success), None (on failure), + // or still Pending (on retriable errors). (Required) + Verification verification = 1; + + // (Required) + ResponseHeader header = 2; + + // Time till by when the client needs to sync again. (Required) + ServerTimestamp next_sync_time = 3; + + // droidguard response + DroidGuardTokenResponse droidguard_token_response = 4; +} + +// GetConsentRequest is the request to get consent +message GetConsentRequest { + // The device id. (DEPRECATED, ignored by service) + DeviceId device_id = 1; + // All the gaia ids on the device. (DEPRECATED, ignored by service) + repeated GaiaId gaia_ids = 2; + // Parameters passed by API callers + repeated Param api_params = 3; + // (Required) + RequestHeader header = 4; + // Specified API params + StructuredAPIParams structured_api_params = 5; + // Whether to include or not the list of asterism consents in the response. Originally a workaround for UPI; no longer respected by the server. + bool include_asterism_consents = 6; + // IMEI of the device. MEID if the phone type is CDMA. + string imei = 7; + // (Required) Added in v26, Required in v >= 26. Assumed CONSTELLATION for v < 26. The client for which the consent is being fetched + AsterismClient asterism_client = 8; + // Whether to include or not the permission mode info in the response. go/device-settings-server-dd + bool include_device_permission_info = 9; +} + +// GetConsentResponse is the response to get consent +message GetConsentResponse { + // Device level consent to do sim associated verification. (Required) + DeviceConsent device_consent = 1; + // All app specific consents for this device Deprecated + repeated AppSpecificConsent app_specific_consents = 2; + // (Required) + ResponseHeader header = 3; + // Gaia level consents requested. The server may not fill this at all if the device level consent is already present. (DEPCRECATED, never populated) + repeated GaiaReachabilityConsent gaia_reachability_consents = 4; + // Server given timestamp after which to sync (Optional) + ServerTimestamp next_sync_time = 5; + // Client behavior for syncs + ClientBehavior client_behavior = 6; + // Assumed CONSTELLATION when not present The client for which the consent is being provided + AsterismClient asterism_client = 7; + // List of asterism consumers with its consent value + repeated AsterismConsent asterism_consents = 8; + // droidguard response + DroidGuardTokenResponse droidguard_token_response = 9; + // permission data for device settings screen + DevicePermissionInfo device_permission_info = 10; +} + +// SetConsentRequest is the request to set consent +message SetConsentRequest { + // (Required) + RequestHeader header = 1; + // One of the following would be set. (Optional) one of the consent messages should be set. See the TODO below (b/294101685) + DeviceConsent device_consent = 2; + // (Required) Added in v23, Required in v >= 23. Assumed CONSTELLATION for v < 23. The client for which the consent is being set + AsterismClient asterism_client = 4; + // (Optional) for device_consent when asterism_client is RCS + RcsConsentVersion consent_version = 5; + // PNV Device Verification Consent. + DeviceVerificationConsent device_verification_consent = 6; + // Parameters passed by API callers + repeated Param api_params = 7; + // (Optional) one of the consent messages should be set Used to set consent via OnDemandConsent flow. + OnDemandConsent on_demand_consent = 8; + // (Optional) Audit token that points to the record of consent moment in ARI + bytes audit_token = 9; +} + +// SetContentResponse is the response to set consent +message SetConsentResponse { + // (Required) + ResponseHeader header = 1; +} + +// VerificationResponse in SyncResponse +message VerificationResponse { + // The server's state of a verification in Request - which can be: + // - Pending (upon challenge), + // - Verified (if still good, or verified without challenge) + // - None (if can't be verified, or error). (Required) + Verification verification = 1; + // Error encountered during completing this specific verification. If there was a more general error for this overall request itself, then we will return an RPC error. But since we are doing batch requests here we allow other independent requests to succeed even when some have errors. + StatusProto error = 2; +} + +// Verification +message Verification { + // To which entity, this verification is associated. (Required) + VerificationAssociation association = 1; + + // Verification state + VerificationState state = 2; + + // Telephony information of the SIM that is being verified. This is transient data, only filled from client->server calls. The server will never fill this field. Client should fill it fresh when it sends this. EXCEPTION: server will send back the exact telephony info given in the request for any verifications returned with state PENDING, until the client starts caching the telephony info. Used by the server to determine verification and reverification policies. (Required for SIM verifications in *client requests*). + TelephonyInfo telephony_info = 5; + + // Verification information of a verified user Populated by the server when state = VERIFIED + VerificationInfo verification_info = 3; + + // Verification information when a challenge is given Populated by the server when state = PENDING + PendingVerificationInfo pending_verification_info = 4; + + // Verification information when the user is unverified Populated by the server when state = NONE + optional bytes unverified_info = 9; + + // Additional API params set in the request and response for each verification. We use free-form key-values than a proto, to make the client agnostic to the actual params. + repeated Param api_params = 6; + + // Specified API params + StructuredAPIParams structured_api_params = 8; + + // Client's preferences of challenge methods. + ChallengePreference challenge_preference = 7; +} + +enum VerificationState { + // UNKNOWN is treated as nil state + VERIFICATION_STATE_UNKNOWN = 0; + // no state. + VERIFICATION_STATE_NONE = 1; + // pending_verification_info should be set. + VERIFICATION_STATE_PENDING = 2; + // verification_info should be set. + VERIFICATION_STATE_VERIFIED = 3; +} + +// To which entity, the verification is associated. +message VerificationAssociation { + // When the verification is associated with SIM. i.e. Constellation performed verification + SIMAssociation sim = 1; + // When the verification is associated with the Gaia. i.e. Focus verification. Note: We decided to not use this for now. Logged in Gaias are part of the client info. + GaiaAssociation gaia = 2; +} + +// For SIM associated verification +message SIMAssociation { + // SIM with which the verification is associated. (Required) + SIMInfo sim_info = 1; + // Zero based number that identifies the SIM slot where the associated SIM card is inserted + SIMSlot sim_slot = 4; + // Gaia ids seen on the device, used for getting hints and bookkeeping. + repeated GaiaId gaia_ids = 2; +} + +// For Gaia associated verification +message GaiaAssociation { + // (Required) Gaia id with which the verification is associated. + GaiaId gaia_id = 1; +} + +// SIM info +message SIMInfo { + // IMSI of the SIM. If the server maps multiple IMSIs to the same phone number on the same device, we could set them to the same SIMInfo proto, making it one verification. (Required at least one) + repeated string imsi = 1; + // Number read from the SIM. + string sim_readable_number = 2; + // Numbers obtained from SubscriptionManager.getPhoneNumber for each of the available sources. + repeated TelephonyPhoneNumber telephony_phone_number = 3; + // the Iccid of the sim + string iccid = 4; +} + +message TelephonyPhoneNumber { + string number = 1; + // LINT.ThenChange( //depot/google3/media/webrtc/server/constellation/analytics/server_enums.proto, //depot/google3/media/webrtc/server/constellation/common/convert.go ) + PhoneNumberSource source = 2; +} + +enum PhoneNumberSource { + PHONE_NUMBER_SOURCE_UNKNOWN = 0; + PHONE_NUMBER_SOURCE_CARRIER = 1; + PHONE_NUMBER_SOURCE_IUCC = 2; + PHONE_NUMBER_SOURCE_IMS = 3; +} + +message SIMSlot { + // Index of the SIM slot. Note this is just the sub_id on pre-Q devices. + optional int32 index = 1; + // Subscription ID corresponding to the SIM slot. + int32 sub_id = 2; +} + +message GaiaId { + // OAuth access token of the Gaia user. (We can move this under oneof later, if we ever use another type of gaia id.) (Required) + string access_token = 1; +} + +// Param for the API. +message Param { + // Name of the API param. + string name = 1; + // Value of the API param. + string value = 2; +} + +// Client Challenge Preference +message ChallengePreference { + // Types of the challenges allowed. Set when client wants to override the default capabilies derived from the Client information, e.g. gmscore version. + repeated string capabilities = 1; + // Preferences for MT challenge. + MTChallengePreference mt_preference = 2; + // Preferences for MO challenge + MOChallengePreference mo_preference = 3; + // Preferences for FLASH_CALL challenge + FlashCallChallengePreference flash_call_preference = 4; +} + +// MT Challenge Preference +message MTChallengePreference { + // Preferred carrier info from the policy. + PreferredCarrierInfo preferred_carrier_info = 1; + // Token that can be used in the MT SMS to keep it silent. Will be passed on Android O+ when silent MT is allowed. + string localized_message_template = 2; + // If it's set to non-zero then binary SMS challenge will be used, and SMS ports defined in the policy will be overwritten. Setting the field to 0 is no-op. + DataSMSInfo data_sms_info = 3; +} + +message PreferredCarrierInfo { + // The carrier to associate this request with to record in streamz/log/db. (The name must exist in googledata/wireless/mobilegw/config/mobile_carrier_config) + string name = 1; + // Get the carrier name by IMSI and telephony_info inside Verify request. If name field is non-empty, this field is ignored. If IMSI's carrier can't be used for MT (the regions don't match, for example), MT SMS challenge is disallowed. + bool lookup_by_imsi = 2; + // By default MGW MTbox queries MDict service to figure out the carrier associated with the target phone number to determine routing. If this field is set, Phone Verifier passes the carrier to MTbox to bypass MDict lookup. The carrier name to be passed to MTbox is determined by: 1. name field if it's not empty 2. carrier name retrieved based on IMSI when lookup_by_imsi is set. + bool enforce_carrier_resolution_override = 3; +} + +// Info required to send data SMS. +message DataSMSInfo { + // The port to use for silent/data SMS. (Required) + int32 port = 1; +} + +// MO Challenge Preference +message MOChallengePreference { + // If set, the data SMS info is applied to MO SMS challenge. + DataSMSInfo data_sms_info = 1; +} + +// Flash Call Challenge Preference +message FlashCallChallengePreference { + // PhoneRange that will be used to call the user's phone number, it will be chosen by constellation server. And the verifier will use a random number in it to trigger a call from. If not provided then the server won't trigger any phone call to the user. + PhoneRange phone_range = 1; +} + +// PhoneRange defines a block of continuous phone numbers to be used on FlashCall verification the phone numbers go from country_code+prefix+lower_bound to country_code+prefix+upper_bound +message PhoneRange { + // Country code of the caller of the flash call Example: 1 + string country_code = 1; + // Prefix of the caller phone number Example: 541301 + string prefix = 2; + // Lower bound of the pool of phone numbers that the client has to expect a call from this lower bound is inclusive and has to be strictly <= upper_bound Example: 0160 + string lower_bound = 3; + // Upper bound of the pool of phone numbers that the client has to expect a call from this upper bound is inclusive and has to be strictly >= lower_bound Example: 0170 + string upper_bound = 4; +} + +// For verifying specific IMSIs +message IMSIRequest { + // IMSIs of the specific SIMs to be verified or IMSIs related to the Phone number hints provided + string imsi = 1; + // Phone number hints received from the calling app to enable sending MT + optional string phone_number_hint = 2; +} + +// Challenge response. +message ChallengeResponse { + // Response to MT Challenge + MTChallengeResponse mt_challenge_response = 1; + // Response to Carrier ID Challenge + CarrierIDChallengeResponse carrier_id_challenge_response = 2; + // Response to MO Challenge + MOChallengeResponse mo_challenge_response = 3; + // Response to Registered SMS Challenge + RegisteredSMSChallengeResponse registered_sms_challenge_response = 4; + // Response to FlashCall Challenge + FlashCallChallengeResponse flash_call_challenge_response = 5; + // Response to Ts43 Challenge + Ts43ChallengeResponse ts43_challenge_response = 9; +} + +// Response to MT challenge. +message MTChallengeResponse { + // The sms that the client received. This can be empty when the client didn't receive anything and still informs us about the failure (basically meaning that our hint phone number was wrong). The server would attempt a new verification method in this case, when available. + // string sms = ?; + // The sender phone number of this SMS. Can be empty when the sms is empty. This info helps MGW team track grey route issues. + // string sender = ?; +} + +// Response to Carrier Id challenge. +message CarrierIDChallengeResponse { + // ISIM Response from the Carrier ID module on the client. http://www.arib.or.jp/IMT-2000/V740Dec09/5_Appendix/Rel5/31/31103-5d1.pdf (Required) + // string isim_response = ?; + // Indicates the requested GTAF verification method for phone number verification + // GtafVerificationMethod gtaf_verification_method = ?; + // Carrier ID challenge preference passed to GTAF + // CarrierIDChallengePreference carrier_id_challenge_preference = ?; +} + +message MOChallengeResponse { + // MO challenge status. + // int32 status = ?; + // Result code of the sms send status. + // int32 sms_result_code = ?; + // Error code if the sms send fails due to a generic error. + // int32 sms_error_code = ?; +} + +// Response to Registered SMS challenge. +message RegisteredSMSChallengeResponse { + // A list of all sms received from verified senders, encoded in MessageID with necessary metadata. + // repeated MessageID message_ids = ?; +} + +// Response to FlashCall challenge. +message FlashCallChallengeResponse { + // The phone number of the caller of the flash call Example: +15413010167 + // string caller = ?; +} + +// Pending verification information +message PendingVerificationInfo { + // Hint phone number for MT verification + string mt_hint_number = 1; + // Challenge given to the client, that can help with proceeding this verification towards completion. (Required) + Challenge challenge = 2; + // Asterism client for this pending verification + AsterismClient asterism_client = 3; + // Billing client for this pending verification passed along to proceed. + BillingClient billing_client = 4; +} + +enum AsterismClient { + ASTERISM_CLIENT_UNKNOWN = 0; + ASTERISM_CLIENT_CONSTELLATION = 1; + ASTERISM_CLIENT_RCS = 2; + // It is not being used to hold a consent value + ASTERISM_CLIENT_ONE_TIME_VERIFICATION = 3; +} + +enum BillingClient { + BILLING_CLIENT_UNKNOWN = 0; + BILLING_CLIENT_CONSTELLATION = 1; + BILLING_CLIENT_CONSTELLATION_ACQUISITION = 2; + BILLING_CLIENT_CONSTELLATION_REVERIFICATION = 3; + BILLING_CLIENT_CONSTELLATION_INTERNATIONAL_MO = 4; + BILLING_CLIENT_RCS = 5; + BILLING_CLIENT_RCS_MO = 6; + BILLING_CLIENT_RCS_HB_MO = 7; + BILLING_CLIENT_RCS_JIBE = 8; + BILLING_CLIENT_RCS_OTP_PROBER = 9; + BILLING_CLIENT_ONE_TIME_VERIFICATION_VERIFIER_SIGNUP_RECOVERY = 10; + BILLING_CLIENT_ONE_TIME_VERIFICATION_ABRA_USERNAME_RECOVERY = 11; + BILLING_CLIENT_ONE_TIME_VERIFICATION_INTERNATIONAL_MO = 12; + // Used for API calls from UPI used for RCS provisioning, set in UPI policy. go/rcs-upi-mvp + BILLING_CLIENT_RCS_PROVISIONING_UPI = 13; + // Gaia username recovery with pre-registered phone. + BILLING_CLIENT_GAIA_USERNAME_RECOVERY = 14; + // Gaia username recovery with pre-registered phone using International MO when eligible. + BILLING_CLIENT_GAIA_USERNAME_RECOVERY_INT_MO = 15; + BILLING_CLIENT_MEET = 16; + BILLING_CLIENT_UPI_FREE_SMS = 17; + // Gaia Device verification flows - IPP, IAP etc. + BILLING_CLIENT_GAIA_DEVICE_VERIFICATION = 18; + // Gaia Device verification flows with INT MO when eligible. + BILLING_CLIENT_GAIA_DEVICE_VERIFICATION_INT_MO = 19; + // UPI Carrier TOS + BILLING_CLIENT_UPI_CARRIER_TOS = 20; + // UPI International MO + BILLING_CLIENT_UPI_INTL_MO = 21; + // Firebase EPNV + BILLING_CLIENT_FIREBASE_PNV = 22; +} + +// Verification information +message VerificationInfo { + // Phone number that was verified. (Required) + string phone_number = 1; + // Time when verification was done. (Required) + google.protobuf.Timestamp verification_time = 2; + // Asterism client for this verification + AsterismClient asterism_client = 3; + // Verification token used in the backup and restore flow for verifying a new device that was phone number verified in the past http://goto.google.com/c11n-br-acquisition + VerificationToken verification_token = 4; + // Challenge method used to complete the verification. + ChallengeType challenge_method = 5; +} + +message Challenge { + // ID of the pending verification (or challenge) given by the server. This is used to refer to the pending verification in process. (Required) + ChallengeID challenge_id = 1; + // Type of the challenge. (Required) + ChallengeType type = 2; + // Time till when this challenge can be responded. After this time, the client can't proceed to complete the verification, or it needs to start over. (Required) + ServerTimestamp expiry_time = 3; + // Group Id will be used by parallel execution to group challenges, one of the challenges in group need to be successful for the entire group to be considered successful. + int32 group_id = 4; + // Challenge by MT SMS + MTChallenge mt_challenge = 5; + // Challenge by MO SMS + MOChallenge mo_challenge = 6; + // Challenge by carrier id + CarrierIDChallenge carrier_id_challenge = 7; + // Challenge by registered SMS. + RegisteredSMSChallenge registered_sms_challenge = 8; + // Challenge by flash call + FlashCallChallenge flash_call_challenge = 9; + // Challenge by TS.43 + Ts43Challenge ts43_challenge = 12; +} + +// Challenge ID +message ChallengeID { + // (Required) + string id = 1; +} + +// MT Challenge +message MTChallenge { + // If set, client will only read SMS that has this string as substring, and send only those SMS as challenge response. + string message_substring = 1; +} + +// MO Challenge +message MOChallenge { + // The proxy phone number where the client will send an SMS. (Required) + string proxy_number = 1; + // The message that the client is expected to send to the proxy number. (Required) + string sms = 2; + // Info required to send data SMS. If not provided the client will send visible/text SMS. + DataSMSInfo data_sms_info = 3; + // The sleep intervals for the MO challenge set by the server. + repeated int32 polling_intervals = 4; +} + +// Carrier ID Challenge used for SS7 traffic. +message CarrierIDChallenge { + // ISIM Request for the Carrier ID module on the client. http://www.arib.or.jp/IMT-2000/V740Dec09/5_Appendix/Rel5/31/31103-5d1.pdf (Required) + string isim_request = 1; + // Determines whether 2G or 3G authentication should be used. + int32 auth_type = 2; + // The app_type to be used for the ICC authentication. + int32 app_type = 3; +} + +// Registered SMS Challenge. Challenges client to provide message id of SMS from registered google senders (go/c11n-a2p-design). +message RegisteredSMSChallenge { + // List of verified SMS senders from which client needs to lookup messages. +// repeated PhoneNumberID verified_senders = 1; +} + +// FlashCall Challenge +message FlashCallChallenge { + // PhoneRanges to be used for the flash call verification + repeated PhoneRange phone_ranges = 1; + // ID of the previous pending verifications (or challenges) given by the server, this is used to refer to the pending verification in process. + repeated string previous_challenge_ids = 2; + // Responses of each challenge made during the flash call flow, used in combination with the previous_challenge_ids to verify the user's device has received and verified all the required phone calls. + repeated FlashCallChallengeResponse previous_challenge_responses = 3; + // Milliseconds to wait before requesting next phone call interception. Must always be at least 10 seconds, because that is the delay from the platform API to intercept phone calls. To check how the delay is managed on platform's side go/platform-api-hangup-delay. + int64 millis_between_interceptions = 4; +} + +enum ChallengeType { + // UNKNOWN is treated as nil method. + CHALLENGE_TYPE_UNKNOWN = 0; + // Challenge by MO SMS. (go/mobile-originated) + CHALLENGE_TYPE_MO_SMS = 1; + // Challenge by MT SMS. (go/mobile-terminated) + CHALLENGE_TYPE_MT_SMS = 2; + // Challenge by carrier id (GTAF). + CHALLENGE_TYPE_CARRIER_ID = 3; + // Imsi lookup performed through GTAF + CHALLENGE_TYPE_IMSI_LOOKUP = 5; + // Challenge issued to do a RegisteredSMS Verification. + CHALLENGE_TYPE_REGISTERED_SMS = 7; + // Challenge by FlashCall (go/c11n-design-flash-call-auth). + CHALLENGE_TYPE_FLASH_CALL = 8; + // Challenge by TS.43 (go/c11n-ts43-design) + CHALLENGE_TYPE_TS43 = 11; +} + +// TS43 Challenge. LINT.IfChange +message Ts43Challenge { + // Ts43 route information. + Ts43Type ts43_type = 1; + // Server URL, android telephony entitlment library connects to perform TS.43 phone number verification. + string entitlement_url = 2; + // ServiceEntitlementRequest to the entitlement library. + ServiceEntitlementRequest service_entitlement_request = 3; + // Client based Ts43 challenge to be performed by client. + ClientChallenge client_challenge = 5; + // Application Id to perform Ts43 verification. + string app_id = 6; + // EAP-AKA Realm of the TS43 verification. + string eap_aka_realm = 7; + // Server based Ts43 challenge to be performed by client. + ServerChallenge server_challenge = 8; +} + +message Ts43Type { + Ts43Integrator integrator = 1; + RcsRouteInfo rcs_route_info = 2; +} + +enum Ts43Integrator { + TS43_INTEGRATOR_UNKNOWN = 0; + TS43_INTEGRATOR_UNSPECIFIED = 1; + TS43_INTEGRATOR_JIO = 2; + TS43_INTEGRATOR_TELUS = 3; + TS43_INTEGRATOR_ERICSSON = 4; + TS43_INTEGRATOR_HPE = 5; + TS43_INTEGRATOR_TMO = 6; + TS43_INTEGRATOR_TMO_SERVER = 7; + TS43_INTEGRATOR_TELENOR = 8; + TS43_INTEGRATOR_RCS_CIS_PROXY = 9; + TS43_INTEGRATOR_MOBI_US = 10; + TS43_INTEGRATOR_SFR = 11; + TS43_INTEGRATOR_SASKTEL_CANADA = 12; + TS43_INTEGRATOR_MOTIVE = 13; + TS43_INTEGRATOR_DT = 14; + TS43_INTEGRATOR_DT_SERVER = 15; + TS43_INTEGRATOR_GLIDE = 16; + TS43_INTEGRATOR_GLIDE_GETPHONENUMBER = 17; + TS43_INTEGRATOR_NETLYNC = 18; + TS43_INTEGRATOR_ORANGE_FRANCE = 19; + TS43_INTEGRATOR_AMDOCS = 20; + TS43_INTEGRATOR_IPIFICATION = 21; +} + +message RcsRouteInfo { + // RCS Carrier ID e.g. rcs5.googleprod.com/carrier/tmo-us. + string rcs_carrier_id = 1; + OverrideTagSet rcs_override_tag_set = 2; +} + +// Set of tags used for carrier configuration overrides (go/rcs-carrier-config#overrides). Must not contain PII. LINT.IfChange +message OverrideTagSet { + // Zero or more tags. Note: Each tag must be a nonempty string that matches regex pattern "A-Za-z*". + repeated string tags = 1; +} + +// Challenge to be performed based on client TS.43 specification. +message ClientChallenge { + // GetPhoneNumber operation to be performed by client. + OdsaOperation get_phone_number_operation = 1; +} + +// Challenge to be performed based on server TS.43 specification. +message ServerChallenge { + // AcquireTemporary token operation to be performed by client. + OdsaOperation acquire_temporary_token_operation = 1; +} + +// Client response Ts43 Challenge. +message Ts43ChallengeResponse { + // Ts43 route information. + Ts43Type ts43_type = 1; + // Client based Ts43 challenge response. + ClientChallengeResponse client_challenge_response = 2; + // Server based Ts43 challenge response. + ServerChallengeResponse server_challenge_response = 3; + // Error observed while performing Ts43 Verification. + Error error = 4; + // HTTP history of the TS43 challenge. This will only be set for devices during test or initial ramp phase for debugging. cs/symbol:com.android.libraries.entitlement.http.HttpRequest + repeated string http_history = 5; +} + +// Challenge response to client based Ts43. +message ClientChallengeResponse { + // Payload containing phone number. + string payload = 1; + // Response received from GetPhoneNumber ODSA. + string get_phone_number_response = 2; +} + +// Challenge response to server based Ts43. +message ServerChallengeResponse { + string temporary_token = 1; + // Response received from AcquireTemporaryToken response. + string acquire_temporary_token_response = 2; +} + +// Error observed while performing Ts43 Verification. +message Error { + // Error observed while performing Ts43 Verification. + ErrorType error_type = 1; + // Error observed at ServiceEntitlement library. + ServiceEntitlementError service_entitlement_error = 2; +} + +enum ErrorType { + ERROR_TYPE_VERIFICATION_ERROR_TYPE_UNSPECIFIED = 0; + ERROR_TYPE_NOT_SUPPORTED = 1; + ERROR_TYPE_CHALLENGE_NOT_SET = 2; + ERROR_TYPE_INTERNAL_ERROR = 3; + ERROR_TYPE_RUNTIME_ERROR = 4; + ERROR_TYPE_JSON_PARSE_ERROR = 5; +} + +// Error received from service entitlement library. +message ServiceEntitlementError { + // cs/symbol:com.android.libraries.entitlement.ServiceEntitlementException.mErrorCode + int32 error_code = 1; + // cs/symbol:com.android.libraries.entitlement.ServiceEntitlementException.mHttpStatus + int32 http_status = 2; + // Api failed on the client. + string api = 3; +} + +// Passed by android client to entitlement library for performing EAP-AKA verification and GetPhoneNumber. This is sent to C11n android client module by server as part of Sync response cs/third_party/java_src/service_entitlement/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java +message ServiceEntitlementRequest { + // Configuration version stored on client, passed in "vers" parameter to carrier. + int32 configuration_version = 1; + // Entitlement specification version, passed in "entitlement_version" parameter to carrier. + string entitlement_version = 2; + // Token received after performing EAP-AKA, passed in "token" parameter to carrier for Odsa operations. + string authentication_token = 3; + // Used to perform Odsa operations, passed in "temporary_token" parameter to carrier. + string temporary_token = 4; + // Terminal Id contains unique identifiers of device like terminal Id, passed in "terminal_id" parameter to carrier. + string terminal_id = 5; + // OEM of the device, passed in "terminal_vendor" parameter to carrier. + string terminal_vendor = 6; + // Model of the device, passed in "terminal_model" parameter to carrier. + string terminal_model = 7; + // Software version of the device, passed in "terminal_sw_version" parameter to carrier. + string terminal_software_version = 8; + // FCM token used to register for entitlement configuration request, passed in "notification_token" parameter to carrier. + string notification_token = 9; + // Action associated with FCM registration token, passed in "notif_token" parameter to carrier. + int32 notification_action = 10; + // Content type C11n client module can accept to process the response. + string accept_content_type = 13; + // Boost type for premium network slice entitlement. + string boost_type = 14; + // GID1 of the SIM, passed in "gid1" parameter to carrier. + string gid1 = 15; +} + +// Passed by android client to entitlement library for performing GetPhoneNumber. This is sent to C11n android client module by server as part of Sync response cs/third_party/java_src/service_entitlement/java/com/android/libraries/entitlement/EsimOdsaOperation.java +message OdsaOperation { + // Odsa operation to be performed, passed in "operation" parameter to carrier. + string operation = 1; + // Operation targets to be performed after obtaining the temporary token, passed in "operation_targets" parameter to carrier. + repeated string operation_targets = 3; + // Operation type to performed, passed in "operation_type" parameter to carrier. + int32 operation_type = 4; + // ICCID of the device, passed in "target_terminal_iccid" parameter to carrier. + string target_terminal_iccid = 12; +} + +// Group of named API params, each of which is used in a subset of the existing APIs +message StructuredAPIParams { + // Used to identify the policy related with a sync, proceed or getConsent request + string policy_id = 1; + // TODO: Missing field maxVerificationAgeHours (field number unknown) + // Used in verifyPhoneNumber API. A number indicating how old a verification could be, to be considered valid. This number is one of the aspects to consider in the server to decide whether to issue a challenge or not. + // Used in verifyPhoneNumber API. Optional, only required for IdToken generation. + IdTokenRequest id_token_request = 3; + // Name of the app package that corresponds to the API caller. This value is not an input parameter, is internally set. + string calling_package = 4; + // IMSIs and phone number hints for numbers to be verified. + repeated IMSIRequest imsi_requests = 5; +} + +message IdTokenRequest { + // A first-party or third-party Android app needs to be first registered in cloud console and then use the certificate SHA-1 hash. This field is expected to be a Base64 encoded string, e.g. "IUW99pi4cVA5vQ6D8gab7UNawhw=" + string certificate_hash = 1; + // This allows security against replay attacks, as the server generated nonce would be different for different clients + string token_nonce = 2; +} + +// RequestHeader is the header for each client request. +message RequestHeader { + // Client info (Required) + ClientInfo client_info = 1; + // Client auth contains client signature signed by the client's private key. This should be left blank + // until the client has been told via ClientInfoUpdate that their public key has been added to storage. + // If the client wants to change their stored public key, this should be signed with their previous private key. + ClientAuth client_auth = 2; + // UUID used for a logging trace for a client session. (Required) + string session_id = 3; + // Used for better quota control on the server, based information like is this a user visible call, + // or is this call gives us critical change to the server to know about, etc. Mostly ignored. + // Only used to know which requests to prioritize when the systems are overloaded. (Required) + RequestTrigger trigger = 4; +} + +// Client's authentication +message ClientAuth { + // Device identity of the client. (Required) + DeviceId device_id = 1; + // ECDSA signature of a SHA256 hash of "device_id.iid_token:sign_timestamp.seconds:sign_timestamp.nanos" made by the private key associated with the public key that was sent to the server in last successful update. (Required) + bytes client_sign = 2; + // Client Timestamp used in the sign, to prevent against reply attacks. [In future: If this client time is older than 30 minutes or already used in other request, we will set new server time retry_with_timestamp in error details which can be used in a followup request.] (Required) + google.protobuf.Timestamp sign_timestamp = 3; +} + +// Device ID +message DeviceId { + // GCM IID token for Android devices. (We can move this under oneof later, if we ever use another type of device id.) (Required) + string iid_token = 1; + // The Android ID of the primary user profile of the device. + int64 device_android_id = 2; + // Device serial number for the multiple user feature. Also known as Multiuser-serial. device_user_id is used to identify different user profiles on a single device. (Required when device_id is set) + optional int64 device_user_id = 3; + // Android ID of the user profile, if the user profile had a checkin. + int64 user_android_id = 4; +} + +// Client info Next ID: 22 +message ClientInfo { + // Device id of the client. (Required) + DeviceId device_id = 1; + // Client public key. Used for client authentication in future. (Required) + bytes client_public_key = 2; + // User's locale. Used for translating message templates, etc. (Required) + string locale = 3; + // User's GMScore version number. (Required) + int32 gmscore_version_number = 4; + // User's human readable (and more granular) GMScore version. (Required) + string gmscore_version = 5; + // User's Android SDK version. (Required) + int32 android_sdk_version = 6; + // Client's preferences of challenge methods. (Moved into Verification) + ChallengePreference challenge_preference = 7; + // Device signals for this client. (Required for Sync and Proceed calls before client starts signing.) + DeviceSignals device_signals = 8; + // If the GMS core has the READ_PRIVILEGED_PHONE_STATE permission. This helps us determine Carrier ID. + bool has_read_privileged_phone_state_permission = 11; + // Gaia ids seen on the device, used for the client state. (Required when Gaias are signed in on the device.) + repeated GaiaId gaia_ids = 12; + // Fields to derive client's country. + CountryInfo country_info = 13; + repeated ConnectivityInfo connectivity_infos = 14; + // The model of the device making this request + string model = 15; + // The manufacturer of the device making this request + string manufacturer = 16; + // The type of user profile the client runs on (regular user or work profile). + optional UserProfileType user_profile_type = 17; + DeviceType device_type = 18; + optional bool is_standalone_device = 19; + // The fingerprint of the device making this request + // Obtained from https://developer.android.com/reference/android/os/Build#FINGERPRINT + string device_fingerprint = 21; + // TODO: Add missing fields from constellation.json: + // The experiments that client is running. + // repeated Experiment experiments = ?; + // Gaia info collected from the device. + // GaiaInfo gaia_info = ?; + // Partial sim information for all the sims on the device + // repeated PartialSimInfo partial_sim_info = ?; +} + +// DeviceSignals in ClientInfo +message DeviceSignals { + // DroidGuard token from the client. + optional string droidguard_token = 1; + // DroidGuard result from the client. (Required) + string droidguard_result = 2; +} + +// CountryInfo of the SIM. +message CountryInfo { + // SIM countries for all SIMs. Got from TelephonyManager.getSimCountryIso(). + repeated string sim_countries = 1; + // Network countries for all SIMs. Got from TelephonyManager.getNetworkCountryIso() + repeated string network_countries = 2; +} + +// Connectivity information for the device +message ConnectivityInfo { + // Connection type. + ConnectivityType type = 1; + ConnectivityState state = 2; + ConnectivityAvailability availability = 3; +} + +enum ConnectivityType { + // Default type + CONNECTIVITY_TYPE_UNKNOWN = 0; + // Wifi connection + CONNECTIVITY_TYPE_WIFI = 1; + // Cellular data connection + CONNECTIVITY_TYPE_MOBILE = 2; +} + +enum ConnectivityState { + // Default state + CONNECTIVITY_STATE_UNKNOWN = 0; + // Connecting + CONNECTIVITY_STATE_CONNECTING = 1; + // Connected + CONNECTIVITY_STATE_CONNECTED = 2; + // Disconnecting + CONNECTIVITY_STATE_DISCONNECTING = 3; + // Disconnected + CONNECTIVITY_STATE_DISCONNECTED = 4; + // Suspended + CONNECTIVITY_STATE_SUSPENDED = 5; +} + +enum ConnectivityAvailability { + // Default availability + CONNECTIVITY_AVAILABILITY_UNKNOWN = 0; + // Connection available + CONNECTIVITY_AVAILABLE = 1; + // Connection not available + CONNECTIVITY_NOT_AVAILABLE = 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; +} + +enum UserProfileType { + // Default type. + UNKNOWN_PROFILE_TYPE = 0; + // Set if it is a regular user profile on the device. + REGULAR_USER = 1; + // Set if it is a managed (work) profile on the device. + MANAGED_PROFILE = 2; +} + +// server encrypted verification record +message VerificationToken { + // encrypted token containing + bytes token = 1; + // time till which the token is valid + google.protobuf.Timestamp expiration_time = 2; +} + +// ResponseHeader is the header for response to client. +message ResponseHeader { + // Indicates whether the given client info was acknowledged by the server. + ClientInfoUpdate client_info_update = 1; + // UUID used for logging given by the client returned back in the response. (Required) + string session_id = 2; + // server_build_label describes the version of the server binary. + string server_build_label = 3; +} + +// ClientInfoUpdate updates the ClientInfo +message ClientInfoUpdate { + // Indicates whether the public key in the request was acknowledged by the server. + PublicKeyStatus public_key_status = 1; +} + +enum PublicKeyStatus { + // No updates were written + PUBLIC_KEY_STATUS_NO_STATUS = 0; + // The public key in the request was written to storage + CLIENT_KEY_UPDATED = 1; +} + +// LINT.IfChange ServerTimestamp has server time with when it was written. +// This helps clients take the difference in their clocks into account, before using it. +// LINT.IfChange ServerTimestamp has server time with when it was written. This helps clients take the difference in their clocks into account, before using it. +message ServerTimestamp { + // Timestamp for an event, the server is referring (e.g. expiry time). + google.protobuf.Timestamp timestamp = 1; + // Timestamp when the server writes this proto. + google.protobuf.Timestamp now = 2; +} + +// server encrypted droidguard response +message DroidGuardTokenResponse { + // Droidguard token + string droidguard_token = 1; + // Droidguard token ttl + google.protobuf.Timestamp droidguard_token_ttl = 2; +} + +// Wire-format for a Status object +message StatusProto { + // Numeric code drawn from the space specified below. Often, this is the canonical error space, and code is drawn from google3/util/task/codes.proto copybara:strip_begin(b/383363683) copybara:strip_end_and_replace optional int32 code = 1; + int32 code = 1; + // copybara:strip_begin(b/383363683) Space to which this status belongs copybara:strip_end_and_replace optional string space = 2; // Space to which this status belongs + string space = 2; + // Detail message copybara:strip_begin(b/383363683) copybara:strip_end_and_replace optional string message = 3; + string message = 3; + // message_set associates an arbitrary proto message with the status. copybara:strip_begin(b/383363683) copybara:strip_end_and_replace optional proto2.bridge.MessageSet message_set = 5; + bytes message_set = 5; + // copybara:strip_begin(b/383363683) copybara:strip_end_and_replace optional int32 canonical_code = 6; + int32 canonical_code = 6; +} + +// The telephony information of the client which is used to choose a right challenge for this client based on policies. Next ID: 27 +message TelephonyInfo { + // Sim state of the device. + SIMState sim_state = 1; + // type of the phone. + // optional PhoneType phone_type = ?; + // Group Identifier Level1 for a GSM phone. TelephonyManager.getGroupIdLevel1() + string group_id_level1 = 2; + // SIM Operator information. (Required) + MobileOperatorInfo sim_operator = 3; + // Current Network Operator information. + MobileOperatorInfo network_operator = 4; + // Is user on Network Roaming? TelephonyManager.isNetworkRoaming() + RoamingState network_roaming = 5; + // Is user on Data Roaming? NetworkInfo.isRoaming() This could be false, even when network_roaming is true, e.g. Fi + RoamingState data_roaming = 6; + // SMS capability of the device/sim + SMSCapability sms_capability = 7; + // CarrierID capability of the device/sim + CarrierIdCapability carrier_id_capability = 8; + PremiumSmsPermission premium_sms_permission = 9; + // Number of active sims on the device. SubscriptionManager.getActiveSubscriptionInfoCount() Client will set 0, when unknown (due to API unavailability). + int32 subscription_count = 11; + // Number of sim slots on the device. SubscriptionManager.getActiveSubscriptionInfoCountMax() Client will set 0, when unknown (due to API unavailability). + int32 subscription_count_max = 12; + // The index of the active sim for the associated verification. Client does not need to set this. CFE sets this before passing this message along to Verifier as part of the VerifyRequest Deprecated: this is for a very old bug: b/64725733. + uint32 sim_index = 13; + // True if the SIM is an eSIM. Client will set to false when API unavailable. + optional bool is_embedded_sim = 14; + // IMEI of the device. MEID if the phone type is CDMA. + string imei = 15; + // Service state of the sim + ServiceState service_state = 16; + // Verification method used for the request This field is used for clients signaling requested verification method to server + optional GtafVerificationMethod gtaf_verification_method = 17; + // Carrier ID Challenge preference passed to GTAF through phone verifier. + optional CarrierIdChallengePreference carrier_id_challenge_preference = 18; + // Cellular network events. + repeated CellularNetworkEvent cellular_network_events = 22; + // Events for incoming/outgoing calls + repeated CallEvent call_events = 19; + // Events for incoming outgoing smses + repeated SMSEvent sms_events = 21; + // Service registration state events. + repeated ServiceStateEvent service_state_events = 20; + // Android Telephony's Carrier ID: https://developer.android.com/reference/android/telephony/TelephonyManager#getSimCarrierId() + int32 sim_carrier_id = 25; + // Skip Device roaming checks b/406852685. + optional bool skip_device_roaming_checks = 23; +} + +// Information about a mobile operator. +message MobileOperatorInfo { + // Country of the network. ISO country code. TelephonyManager.getNetworkCountryIso() for network operator and TelephonyManager.getSimCountryIso() for sim operator. + string country_code = 1; + // Numeric name (MCC+MNC) of the network operator. TelephonyManager.getNetworkOperator() for network operator and TelephonyManager.getSimOperator() for sim operator. + string mcc_mnc = 2; + // Alphabetic name of the network operator. TelephonyManager.getNetworkOperatorName() for network operator and TelephonyManager.getSimOperatorName() for sim operator. + string operator_name = 3; + // Time in micros since when these values have been nil. (Deprecated after v18) + optional uint32 nil_since_micros = 4; + // Time in micros since when these values have been nil. + optional uint64 nil_since_usec = 5; +} + +message CarrierIdChallengePreference { + // Integrator used for Carrier ID request + Integrator integrator = 1; + // Verification method used for the request This field is used for clients signaling requested verification method to server + GtafVerificationMethod gtaf_verification_method = 2; +} + +// Event when a cellular data network was detected. +message CellularNetworkEvent { + // When the event was recorded. + google.protobuf.Timestamp event_timestamp = 1; + // Whether "mobile data" is enabled in device's settings. + bool mobile_data_enabled = 2; + // Whether "airplane mode" is enabled in device's settings. + bool airplane_mode_enabled = 3; + // Whether "mobile data always on" is enabled in device's settings. + bool mobile_data_always_on_enabled = 4; + // Whether the device was connected to WiFi when this event happened. + bool connected_to_wifi = 5; + // Cellular networks available. + repeated CellularNetwork data_networks = 6; +} + +// Information collected from device about a cellular network. +message CellularNetwork { + // All network capabilities. https://developer.android.com/reference/android/net/NetworkCapabilities.html#hasCapability(int) + repeated int32 network_capabilities = 1; + // If the network has internet capability, whether we can successfully reach Google servers through it. + bool can_reach_google = 2; +} + +message CallEvent { + // When the event was recorded rounded down to the multiple of n hours before the call is made n is defined by a flag + google.protobuf.Timestamp event_timestamp = 1; + // The direction of the Call + CommunicationDirection event_direction = 2; + // Short code or Long number + PhoneNumberType number_type = 3; +} + +message SMSEvent { + // When the event was recorded rounded down to the multiple of n hours before the call is made n is defined by a flag + google.protobuf.Timestamp event_timestamp = 1; + // The direction of the SMS + CommunicationDirection event_direction = 2; + // Short code or Long number + PhoneNumberType number_type = 3; +} + +// The SIM's service registration state. +message ServiceStateEvent { + // When the event was recorded. + google.protobuf.Timestamp event_timestamp = 1; + // Whether mobile data is enabled in device's settings. + optional bool mobile_data_enabled = 2; + // Whether airplane mode is enabled in device's settings. + optional bool airplane_mode_enabled = 3; + // Voice registration state. + int32 voice_registration_state = 4; + // Data registration state. + int32 data_registration_state = 5; + // Voice network type. + optional int32 voice_network_type = 6; + // Data network type. + optional int32 data_network_type = 7; + // If registration state is OUT_OF_SERVICE, the signal strength level detected at the time of the event. + optional int32 signal_strength = 8; +} + +enum SIMState { + SIM_STATE_UNKNOWN = 0; + SIM_NOT_READY = 1; + SIM_READY = 2; +} + +enum PhoneType { + // UNKNOWN_PHONE_TYPE is treated as nil phone type. + PHONE_TYPE_UNKNOWN = 0; + // GSM phone. + PHONE_TYPE_GSM = 1; + // CDMA phone. + PHONE_TYPE_CDMA = 2; + // SIP phone. + PHONE_TYPE_SIP = 3; +} + +enum RoamingState { + // UNKNOWN_ROAMING is treated as nil roaming. + ROAMING_STATE_UNKNOWN = 0; + // No roaming. + ROAMING_STATE_NOT_ROAMING = 1; + // Has roaming. + ROAMING_STATE_ROAMING = 2; +} + +enum SMSCapability { + // UNKNOWN_SMS_CAPABILITY is treated as nil sms capability. + SMS_CAPABILITY_UNKNOWN = 0; + // SMS incapable device. + SMS_CAPABILITY_INCAPABLE = 1; + // App lacks permissions to send/receive SMS. + SMS_CAPABILITY_APP_RESTRICTED = 2; + // User Profile lacks permissions to send/receive SMS. + SMS_CAPABILITY_USER_RESTRICTED = 3; + // SMS capable client. + SMS_CAPABILITY_CAPABLE = 4; +} + +enum CarrierIdCapability { + // UNKNOWN_CARRIER_ID_CAPABILITY is treated as no capability. + CARRIER_ID_CAPABILITY_UNKNOWN = 0; + // Carrier ID incapable device/sim. + CARRIER_ID_INCAPABLE = 1; + // Carrier ID capable device/sim. + CARRIER_ID_CAPABLE = 2; +} + +enum PremiumSmsPermission { + // Unknown. + PREMIUM_SMS_PERMISSION_UNKNOWN = 0; + // SMS to a shortcode, will show Premium SMS prompt for this client. + PREMIUM_SMS_PROMPT_REQUIRED = 1; + // GMSCore has permissions to send "Premium SMS". No prompt will be shown. + PREMIUM_SMS_PERMISSION_GRANTED = 2; +} + +enum ServiceState { + // State unknown + SERVICE_STATE_UNKNOWN = 0; + // Phone has full function either in home or roaming network + SERVICE_STATE_IN_SERVICE = 1; + // Phone is not registered on any operator + SERVICE_STATE_OUT_OF_SERVICE = 2; + // Phone is registered but locked for emergency use + SERVICE_STATE_EMERGENCY_ONLY = 3; + // Radio telephony is powered off + SERVICE_STATE_POWER_OFF = 4; +} + +enum GtafVerificationMethod { + // Method not set by client + METHOD_UNKNOWN = 0; + // Using carrier id ts43 eap-aka verification + METHOD_CARRIER_ID_TS43 = 1; + // Legacy server to server carrier id flow + METHOD_CARRIER_ID_LEGACY = 2; + // Using carrier ID TS.43 for UPI + METHOD_CARRIER_ID_TS43_UPI = 3; +} + +enum Integrator { + INTEGRATOR_UNSPECIFIED = 0; + INTEGRATOR_TATA_GT1 = 1; + INTEGRATOR_TATA_GT2 = 2; +} + +enum CommunicationDirection { + COMMUNICATION_DIRECTION_UNKNOWN = 0; + COMMUNICATION_DIRECTION_INCOMING = 1; + COMMUNICATION_DIRECTION_OUTGOING = 2; + COMMUNICATION_DIRECTION_MISSED = 3; +} + +enum PhoneNumberType { + PHONE_NUMBER_TYPE_UNKNOWN = 0; + PHONE_NUMBER_TYPE_LONG_NUMBER = 1; + PHONE_NUMBER_TYPE_SHORT_CODE = 2; +} + +// RequestTrigger triggers which requests to prioritize when the systems are overloaded. +message RequestTrigger { + // Indicates which trigger type in the request. + TriggerType type = 1; +} + +enum TriggerType { + // Default type. + TRIGGER_TYPE_UNKNOWN = 0; + // Triggered by periodic consent checker. + TRIGGER_TYPE_PERIODIC_CONSENT_CHECK = 1; + // Triggered by periodic refresh/sync. + TRIGGER_TYPE_PERIODIC_REFRESH = 2; + // Triggered due to sim change mismatch. + TRIGGER_TYPE_SIM_STATE_CHANGED = 3; + // Triggered due to Gaia change on the device. + TRIGGER_TYPE_GAIA_CHANGE_EVENT = 4; + // Triggered from the User visited settings (c11n consent only). + TRIGGER_TYPE_USER_SETTINGS = 5; + // Triggered from the internal debug settings. + TRIGGER_TYPE_DEBUG_SETTINGS = 6; + // Client side Trigger API called (c11n consent only). + TRIGGER_TYPE_TRIGGER_API_CALL = 7; + // Either sim or gaia change detected on reboot. + TRIGGER_TYPE_REBOOT_CHECKER = 8; + // Triggered by the server using a GCM push. + TRIGGER_TYPE_SERVER_TRIGGER = 9; + // Retry due to a previous failure. + TRIGGER_TYPE_FAILURE_RETRY = 10; + // Triggered due to client side consent API call. + TRIGGER_TYPE_CONSENT_API_TRIGGER = 11; + // Triggered from the PNVR settings. + TRIGGER_TYPE_PNVR_DEVICE_SETTINGS = 12; +} + +// Device consent +message DeviceConsent { + // A device level consent for this device. (Required) + ConsentValue consent = 2; + // Cost setting for the Consent. (Required) + ConsentCostSetting cost_setting = 3; +} + +message AppSpecificConsent { + // An app specific consent for this device. (Required) + ConsentValue consent = 1; + // The app name for this consent This should stay unused on the client since adding new values will require client updates + AppIdentifier app = 2; +} + +// Gaia reachability consent +message GaiaReachabilityConsent { + // Gaia id for this consent. (Required) + GaiaId gaia_id = 1; + // True if this Gaia user gave reachability consent to Google. (Required) + ConsentValue reachability_consent = 2; +} + +// On-demand consent, used by ODCv2 to set consent +message OnDemandConsent { + // Value of the consent + ConsentValue consent = 1; + // Gaia used to set the consent + GaiaId gaia_id = 2; + // Variant id of the consent + string consent_variant = 3; + // Trigger for the consent flow + string trigger = 4; +} + +// Device-level phone number verification consent +message DeviceVerificationConsent { + // A device level phone number verification consent for this device + ConsentValue consent_value = 1; + // Source for the consent request (i.e. where the request is made from) + DeviceVerificationConsentSource consent_source = 2; + // Version of the PNV consent. + DeviceVerificationConsentVersion consent_version = 3; +} + +message ClientBehavior { + // Consent for the current call + DeviceConsent current_consent = 1; + // Whether the client should keep the checkers active or not + CheckerState checkers_state = 2; +} + +message AsterismConsent { + // The consumer which owns the consent, such as "RCS", "Constellation" "One-time verification" + AsterismClient consumer = 1; + // The value of the consent for the consumer + ConsentValue consent = 2; + // The consent version. Only available for RCS + RcsConsentVersion consent_version = 3; + // If all imsis on the device are unmonitored. Only relevant for RCS + bool are_all_rcs_users_unmonitored = 4; +} + +// Device permission info for device settings screen +message DevicePermissionInfo { + // Permission state for the device + DevicePermissionState permission_state = 1; + // Permission mode for the device + DevicePermissionMode permission_mode = 2; +} + +enum ConsentValue { + // Default value. + CONSENT_VALUE_UNKNOWN = 0; + // Consent was given. + CONSENT_VALUE_CONSENTED = 1; + // Consent was not given or revoked. + CONSENT_VALUE_NO_CONSENT = 2; + // The user's decision has expired. + CONSENT_VALUE_EXPIRED = 3; +} + +enum ConsentCostSetting { + // Set to NONE when consent is NO_CONSENT or EXPIRED. + CONSENT_COST_SETTING_NONE = 0; + // Used for CONSENTED. Gives Google an ability to reverify the phone without checking with Google. + CONSENT_COST_SETTING_AUTOMATIC = 1; + // Used for CONSENTED. Google should check with the user before reverifying the phone. + CONSENT_COST_SETTING_MANUAL = 2; +} + +enum CheckerState { + CHECKERS_UNKNOWN_STATE = 0; + // Disable all checkers. + CHECKERS_INACTIVE = 1; + // Enable all checkers. + CHECKERS_ACTIVE = 2; +} + +enum DeviceVerificationConsentSource { + DEVICE_VERIFICATION_CONSENT_SOURCE_UNSPECIFIED = 0; + DEVICE_VERIFICATION_CONSENT_SOURCE_ANDROID_DEVICE_SETTINGS = 1; + DEVICE_VERIFICATION_CONSENT_SOURCE_GAIA_USERNAME_RECOVERY = 2; + DEVICE_VERIFICATION_CONSENT_SOURCE_AOB_SETUP_WIZARD = 3; + DEVICE_VERIFICATION_CONSENT_SOURCE_MINUTEMAID_JS_BRIDGE = 4; + // PVaaS Web + DEVICE_VERIFICATION_CONSENT_SOURCE_GAIA_WEB_JS_BRIDGE = 5; + // Messages Profiles + DEVICE_VERIFICATION_CONSENT_SOURCE_AM_PROFILES = 6; +} + +enum DeviceVerificationConsentVersion { + DEVICE_VERIFICATION_CONSENT_VERSION_UNKNOWN = 0; + // Basic PNV consent + DEVICE_VERIFICATION_CONSENT_VERSION_PHONE_VERIFICATION_DEFAULT = 1; + // PNV consent with both messages and calls + DEVICE_VERIFICATION_CONSENT_VERSION_PHONE_VERIFICATION_MESSAGES_CALLS_V1 = 2; + // PNV consent with international SMS and calls + DEVICE_VERIFICATION_CONSENT_VERSION_PHONE_VERIFICATION_INTL_SMS_CALLS = 3; + // PNV consent with reachability and international SMS and calls + DEVICE_VERIFICATION_CONSENT_VERSION_PHONE_VERIFICATION_REACHABILITY_INTL_SMS_CALLS = 4; +} + +enum RcsConsentVersion { + // Not specified. + RCS_CONSENT_VERSION_UNSPECIFIED = 0; + // RCS consent + RCS_CONSENT_VERSION_RCS_CONSENT = 1; + // RCS Default-On on app open. + RCS_CONSENT_VERSION_RCS_DEFAULT_ON_LEGAL_FYI = 2; + // RCS Default-On out of box. + RCS_CONSENT_VERSION_RCS_DEFAULT_ON_OUT_OF_BOX = 3; + // Manual RCS Unfreeze consent in SM. + RCS_CONSENT_VERSION_RCS_SAMSUNG_UNFREEZE = 4; + // RCS Default-On with legal FYI in settings. + RCS_CONSENT_VERSION_RCS_DEFAULT_ON_LEGAL_FYI_IN_SETTINGS = 5; +} + +enum AppIdentifier { + // Default value + APP_IDENTIFIER_UNKNOWN_APP = 0; + // RCS + APP_IDENTIFIER_RCS = 1; +} + +enum DevicePermissionState { + // Default value. + DEVICE_PERMISSION_STATE_UNSPECIFIED = 0; + // Permission granted. + DEVICE_PERMISSION_STATE_GRANTED = 1; + // Permission denied. + DEVICE_PERMISSION_STATE_DENIED = 2; +} + +enum DevicePermissionMode { + // Default value. + DEVICE_PERMISSION_MODE_UNSPECIFIED = 0; + // The device doesn't have PNVR permission record, with or without legacy PNV permission record + DEVICE_PERMISSION_MODE_LEGACY_DPNV = 1; + // The device has PNVR permission record, either enabled or disabled + DEVICE_PERMISSION_MODE_PNVR = 2; + // The device is disallowed for DPNV and PNVR permissions + DEVICE_PERMISSION_MODE_NOT_ALLOWED = 3; +} \ No newline at end of file diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle index 8fc2896bbc..5571eafae6 100644 --- a/play-services-core/build.gradle +++ b/play-services-core/build.gradle @@ -31,6 +31,7 @@ dependencies { implementation project(':play-services-base-core') implementation project(':play-services-cast-core') implementation project(':play-services-cast-framework-core') + implementation project(':play-services-constellation-core') implementation project(':play-services-conscrypt-provider-core') implementation project(':play-services-cronet-core') implementation project(':play-services-droidguard-core') diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 66038da7e2..348c7abcbf 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -1218,7 +1218,6 @@ - diff --git a/settings.gradle b/settings.gradle index 1ab99b9000..347a542135 100644 --- a/settings.gradle +++ b/settings.gradle @@ -35,6 +35,7 @@ include ':play-services-base' include ':play-services-basement' include ':play-services-cast' include ':play-services-cast-framework' +include ':play-services-constellation' include ':play-services-clearcut' include ':play-services-drive' include ':play-services-droidguard' @@ -90,6 +91,7 @@ sublude ':play-services-base:core' sublude ':play-services-base:core:package' sublude ':play-services-cast:core' sublude ':play-services-cast-framework:core' +sublude ':play-services-constellation:core' include ':play-services-chimera-core' include ':play-services-conscrypt-provider-core' sublude ':play-services-cronet:core' From da25dfe3f5b693f047dd4d59aca526f6d91bf5f5 Mon Sep 17 00:00:00 2001 From: vikramships Date: Tue, 10 Feb 2026 19:13:13 +0530 Subject: [PATCH 2/4] Constellation: apply lint-safe API guards in service implementation. Add explicit exported service metadata and API-level guards/suppressions for network and telephony paths so the module builds and lints cleanly without changing verification flow behavior. --- .../core/src/main/AndroidManifest.xml | 3 +- .../constellation/ConstellationServiceImpl.kt | 66 ++++++++++++++----- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/play-services-constellation/core/src/main/AndroidManifest.xml b/play-services-constellation/core/src/main/AndroidManifest.xml index accf8dd2f8..7887f35956 100644 --- a/play-services-constellation/core/src/main/AndroidManifest.xml +++ b/play-services-constellation/core/src/main/AndroidManifest.xml @@ -7,7 +7,8 @@ - + diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt index 396a5231e5..199d758522 100644 --- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt +++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt @@ -5,6 +5,7 @@ package org.microg.gms.constellation +import android.annotation.SuppressLint import android.content.Context import android.net.ConnectivityManager import android.net.NetworkCapabilities @@ -198,6 +199,7 @@ class ConstellationServiceImpl( } } + @SuppressLint("NewApi") // Wire Instant.epochSecond — RCS requires API 24+ in practice private fun handleVerifiedState( verification: Verification, callbacks: IConstellationCallbacks @@ -367,6 +369,7 @@ class ConstellationServiceImpl( ) } + @SuppressLint("PrivateApi") @Suppress("UNCHECKED_CAST") private fun performTs43ViaEntitlementLibrary( url: String, appId: String, ts43: Ts43Challenge @@ -437,6 +440,7 @@ class ConstellationServiceImpl( ) } + @SuppressLint("NewApi") // Wire Instant.epochSecond — RCS requires API 24+ in practice private suspend fun callProceedAndRespond( verification: Verification, challengeResponse: ChallengeResponse, @@ -686,28 +690,50 @@ class ConstellationServiceImpl( ) } + @SuppressLint("MissingPermission") private fun buildConnectivityInfo(): List { val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager ?: return emptyList() + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return emptyList() + val infos = mutableListOf() - val activeNetwork = cm.activeNetwork - val caps = if (activeNetwork != null) cm.getNetworkCapabilities(activeNetwork) else null - - if (caps != null) { - if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { - infos.add(ConnectivityInfo( - type = ConnectivityType.CONNECTIVITY_TYPE_WIFI, - state = ConnectivityState.CONNECTIVITY_STATE_CONNECTED, - availability = ConnectivityAvailability.CONNECTIVITY_AVAILABLE - )) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val activeNetwork = cm.activeNetwork + val caps = if (activeNetwork != null) cm.getNetworkCapabilities(activeNetwork) else null + if (caps != null) { + if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + infos.add(ConnectivityInfo( + type = ConnectivityType.CONNECTIVITY_TYPE_WIFI, + state = ConnectivityState.CONNECTIVITY_STATE_CONNECTED, + availability = ConnectivityAvailability.CONNECTIVITY_AVAILABLE + )) + } + if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + infos.add(ConnectivityInfo( + type = ConnectivityType.CONNECTIVITY_TYPE_MOBILE, + state = ConnectivityState.CONNECTIVITY_STATE_CONNECTED, + availability = ConnectivityAvailability.CONNECTIVITY_AVAILABLE + )) + } } - if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { - infos.add(ConnectivityInfo( - type = ConnectivityType.CONNECTIVITY_TYPE_MOBILE, - state = ConnectivityState.CONNECTIVITY_STATE_CONNECTED, - availability = ConnectivityAvailability.CONNECTIVITY_AVAILABLE - )) + } else { + @Suppress("DEPRECATION") + val ni = cm.activeNetworkInfo + if (ni != null && ni.isConnected) { + @Suppress("DEPRECATION") + val type = when (ni.type) { + ConnectivityManager.TYPE_WIFI -> ConnectivityType.CONNECTIVITY_TYPE_WIFI + ConnectivityManager.TYPE_MOBILE -> ConnectivityType.CONNECTIVITY_TYPE_MOBILE + else -> null + } + if (type != null) { + infos.add(ConnectivityInfo( + type = type, + state = ConnectivityState.CONNECTIVITY_STATE_CONNECTED, + availability = ConnectivityAvailability.CONNECTIVITY_AVAILABLE + )) + } } } return infos @@ -849,12 +875,12 @@ class ConstellationServiceImpl( // ---- Telephony helpers ---- - @Suppress("MissingPermission") + @SuppressLint("MissingPermission", "HardwareIds") private fun getImsiSafe(tm: TelephonyManager): String { return try { tm.subscriberId ?: "" } catch (_: Exception) { "" } } - @Suppress("MissingPermission") + @SuppressLint("MissingPermission", "HardwareIds") private fun getImeiSafe(tm: TelephonyManager): String { return try { if (Build.VERSION.SDK_INT >= 26) tm.imei ?: "" else "" @@ -879,14 +905,18 @@ class ConstellationServiceImpl( } catch (_: Exception) { 0 } } + @SuppressLint("MissingPermission") private fun getActiveSubscriptionCount(): Int { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) return 1 return try { val sm = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as? SubscriptionManager sm?.activeSubscriptionInfoCount ?: 1 } catch (_: Exception) { 1 } } + @SuppressLint("MissingPermission") private fun getMaxSubscriptionCount(): Int { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) return 1 return try { val sm = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as? SubscriptionManager sm?.activeSubscriptionInfoCountMax ?: 1 From 235c7d26ff6898f77771e8f7fc5d9d5df0d5da1c Mon Sep 17 00:00:00 2001 From: vikramships Date: Wed, 11 Feb 2026 12:34:07 +0530 Subject: [PATCH 3/4] Proto: drop internal-only comment references. Remove Google-internal shortlinks and lint metadata comments from the reconstructed schema so the proto stays source-agnostic and reviewable. --- .../main/proto/phonenumberverification.proto | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/play-services-core-proto/src/main/proto/phonenumberverification.proto b/play-services-core-proto/src/main/proto/phonenumberverification.proto index f6b392f8f8..c77b620a3f 100644 --- a/play-services-core-proto/src/main/proto/phonenumberverification.proto +++ b/play-services-core-proto/src/main/proto/phonenumberverification.proto @@ -9,7 +9,6 @@ import "google/protobuf/timestamp.proto"; // Project Constellation aims at determining, verifying and maintaining the phone numbers of the Android devices // so that Google services can use them for different applications, such as near-tier graphs. // Google Phone Device Verification API is the internal API to the Constellation GMS core module which enables this project. -// (go/constellation-overview) service PhoneDeviceVerification { // Client tells the server about its current state. The server can initiate verifications at this time // or refresh their states. If the new states of some verifications are pending, then client will call @@ -46,7 +45,7 @@ message SyncRequest { // - If this is a periodic sync, it should have all server returned verifications, but updated with any client side changes that didn't warrant an immediate sync (e.g. IMSI should be the currently seen IMSI). repeated Verification verifications = 3; - // Verification tokens used in the backup and restore flow for verifying a new device that was phone number verified in the past http://goto.google.com/c11n-br-acquisition + // Verification tokens used in the backup and restore flow for verifying a new device that was phone number verified in the past. repeated VerificationToken verification_tokens = 5; } @@ -64,7 +63,7 @@ message SyncResponse { // droidguard response DroidGuardTokenResponse droidguard_token_response = 4; - // Verification tokens used in the backup and restore flow for verifying a new device that was phone number verified in the past http://goto.google.com/c11n-br-acquisition + // Verification tokens used in the backup and restore flow for verifying a new device that was phone number verified in the past. repeated VerificationToken verification_tokens = 5; } @@ -114,7 +113,7 @@ message GetConsentRequest { string imei = 7; // (Required) Added in v26, Required in v >= 26. Assumed CONSTELLATION for v < 26. The client for which the consent is being fetched AsterismClient asterism_client = 8; - // Whether to include or not the permission mode info in the response. go/device-settings-server-dd + // Whether to include or not the permission mode info in the response. bool include_device_permission_info = 9; } @@ -258,7 +257,6 @@ message SIMInfo { message TelephonyPhoneNumber { string number = 1; - // LINT.ThenChange( //depot/google3/media/webrtc/server/constellation/analytics/server_enums.proto, //depot/google3/media/webrtc/server/constellation/common/convert.go ) PhoneNumberSource source = 2; } @@ -447,7 +445,7 @@ enum BillingClient { BILLING_CLIENT_ONE_TIME_VERIFICATION_VERIFIER_SIGNUP_RECOVERY = 10; BILLING_CLIENT_ONE_TIME_VERIFICATION_ABRA_USERNAME_RECOVERY = 11; BILLING_CLIENT_ONE_TIME_VERIFICATION_INTERNATIONAL_MO = 12; - // Used for API calls from UPI used for RCS provisioning, set in UPI policy. go/rcs-upi-mvp + // Used for API calls from UPI used for RCS provisioning, set in UPI policy. BILLING_CLIENT_RCS_PROVISIONING_UPI = 13; // Gaia username recovery with pre-registered phone. BILLING_CLIENT_GAIA_USERNAME_RECOVERY = 14; @@ -475,7 +473,7 @@ message VerificationInfo { google.protobuf.Timestamp verification_time = 2; // Asterism client for this verification AsterismClient asterism_client = 3; - // Verification token used in the backup and restore flow for verifying a new device that was phone number verified in the past http://goto.google.com/c11n-br-acquisition + // Verification token used in the backup and restore flow for verifying a new device that was phone number verified in the past. VerificationToken verification_token = 4; // Challenge method used to complete the verification. ChallengeType challenge_method = 5; @@ -538,7 +536,7 @@ message CarrierIDChallenge { int32 app_type = 3; } -// Registered SMS Challenge. Challenges client to provide message id of SMS from registered google senders (go/c11n-a2p-design). +// Registered SMS Challenge. Challenges client to provide message id of SMS from registered google senders. message RegisteredSMSChallenge { // List of verified SMS senders from which client needs to lookup messages. // repeated PhoneNumberID verified_senders = 1; @@ -552,16 +550,16 @@ message FlashCallChallenge { repeated string previous_challenge_ids = 2; // Responses of each challenge made during the flash call flow, used in combination with the previous_challenge_ids to verify the user's device has received and verified all the required phone calls. repeated FlashCallChallengeResponse previous_challenge_responses = 3; - // Milliseconds to wait before requesting next phone call interception. Must always be at least 10 seconds, because that is the delay from the platform API to intercept phone calls. To check how the delay is managed on platform's side go/platform-api-hangup-delay. + // Milliseconds to wait before requesting next phone call interception. Must always be at least 10 seconds, because that is the delay from the platform API to intercept phone calls. int64 millis_between_interceptions = 4; } enum ChallengeType { // UNKNOWN is treated as nil method. CHALLENGE_TYPE_UNKNOWN = 0; - // Challenge by MO SMS. (go/mobile-originated) + // Challenge by MO SMS. CHALLENGE_TYPE_MO_SMS = 1; - // Challenge by MT SMS. (go/mobile-terminated) + // Challenge by MT SMS. CHALLENGE_TYPE_MT_SMS = 2; // Challenge by carrier id (GTAF). CHALLENGE_TYPE_CARRIER_ID = 3; @@ -569,13 +567,13 @@ enum ChallengeType { CHALLENGE_TYPE_IMSI_LOOKUP = 5; // Challenge issued to do a RegisteredSMS Verification. CHALLENGE_TYPE_REGISTERED_SMS = 7; - // Challenge by FlashCall (go/c11n-design-flash-call-auth). + // Challenge by FlashCall. CHALLENGE_TYPE_FLASH_CALL = 8; - // Challenge by TS.43 (go/c11n-ts43-design) + // Challenge by TS.43. CHALLENGE_TYPE_TS43 = 11; } -// TS43 Challenge. LINT.IfChange +// TS43 Challenge. message Ts43Challenge { // Ts43 route information. Ts43Type ts43_type = 1; @@ -629,7 +627,7 @@ message RcsRouteInfo { OverrideTagSet rcs_override_tag_set = 2; } -// Set of tags used for carrier configuration overrides (go/rcs-carrier-config#overrides). Must not contain PII. LINT.IfChange +// Set of tags used for carrier configuration overrides. Must not contain PII. message OverrideTagSet { // Zero or more tags. Note: Each tag must be a nonempty string that matches regex pattern "A-Za-z*". repeated string tags = 1; @@ -958,9 +956,8 @@ enum PublicKeyStatus { CLIENT_KEY_UPDATED = 1; } -// LINT.IfChange ServerTimestamp has server time with when it was written. +// ServerTimestamp has server time with when it was written. // This helps clients take the difference in their clocks into account, before using it. -// LINT.IfChange ServerTimestamp has server time with when it was written. This helps clients take the difference in their clocks into account, before using it. message ServerTimestamp { // Timestamp for an event, the server is referring (e.g. expiry time). google.protobuf.Timestamp timestamp = 1; From bec42a5253bd563f46525f6c5dac98d64a23ae6a Mon Sep 17 00:00:00 2001 From: vikramships Date: Wed, 11 Feb 2026 17:36:44 +0530 Subject: [PATCH 4/4] Constellation: align GMS version constants with public APK artifact. Use the verifiable 24.46.31 (190400-699197446) build variant so request metadata matches a publicly downloadable APK. --- .../org/microg/gms/constellation/ConstellationServiceImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt index 199d758522..f76b605dc5 100644 --- a/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt +++ b/play-services-constellation/core/src/main/kotlin/org/microg/gms/constellation/ConstellationServiceImpl.kt @@ -42,8 +42,8 @@ import java.util.UUID private const val TAG = "ConstellationImpl" private const val CONSTELLATION_AUTHORIZED_ENTITY = "496232013492" -private const val GMSCORE_VERSION_NUMBER = 244631038 -private const val GMSCORE_VERSION_STRING = "24.46.31 (190408-693505712)" +private const val GMSCORE_VERSION_NUMBER = 244631029 +private const val GMSCORE_VERSION_STRING = "24.46.31 (190400-699197446)" class ConstellationServiceImpl( private val context: Context,