Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
738832a
Add Asterism module build configuration [skip ci]
woahwhattheheck Oct 3, 2026
37a9b13
Integrate Asterism service and protocol baseline (stage 1/13) [skip ci]
woahwhattheheck Oct 3, 2026
a862882
Add Constellation settings and service module wiring [skip ci]
woahwhattheheck Oct 3, 2026
dc21cdd
Integrate Constellation protocol and service core [skip ci]
woahwhattheheck Oct 3, 2026
eec9ed8
Integrate Constellation request verification services [skip ci]
woahwhattheheck Oct 3, 2026
bd42630
Integrate Constellation carrier and messaging verification [skip ci]
woahwhattheheck Oct 3, 2026
8004bf4
Add remaining Constellation verification flows [skip ci]
woahwhattheheck Oct 3, 2026
455451e
Bind Asterism requests to caller lifetime [skip ci]
woahwhattheheck Oct 3, 2026
f9ba245
Add TS.43 EAP-AKA support helpers [skip ci]
woahwhattheheck Oct 3, 2026
1825e41
Add Constellation protobuf schemas [skip ci]
woahwhattheheck Oct 3, 2026
6b4fcdb
Add Constellation UI resources and callback tests [skip ci]
woahwhattheheck Oct 3, 2026
508e442
Add remaining Constellation API wrappers and existing tests [skip ci]
woahwhattheheck Oct 3, 2026
3d31170
Add RCS reply APIs and phenotype wiring [skip ci]
woahwhattheheck Oct 3, 2026
ae5d278
Complete RCS module wiring and consent fallback [skip ci]
woahwhattheheck Oct 4, 2026
eca2522
fix(constellation): serialize signing key initialization
woahwhattheheck Oct 4, 2026
8d6858c
Preserve all 64 bits when parsing the platform Android ID
woahwhattheheck Oct 4, 2026
02e25b7
constellation: stop IID callbacks after cancelled blocking work
woahwhattheheck Oct 4, 2026
ad51327
Integrate RCS authentication and DroidGuard source packets [skip ci]
woahwhattheheck Oct 4, 2026
352b0f0
Preserve IID coroutine cancellation before credential delivery [skip ci]
woahwhattheheck Oct 4, 2026
1d63e94
Capture APK signing information before certificate checks [skip ci]
woahwhattheheck Oct 4, 2026
47e3953
Preserve PNV request cancellation before capability delivery [skip ci]
woahwhattheheck Oct 4, 2026
71c28a2
Integrate PNV cancellation and repair DroidGuard Kotlin types [skip ci]
woahwhattheheck Oct 4, 2026
7cf292b
Close TS.43 HTTP responses on every completion path [skip ci]
woahwhattheheck Oct 4, 2026
96466cb
Fix partial MT SMS inbox registration cleanup [skip ci]
woahwhattheheck Oct 4, 2026
703fd5e
Guard Carrier ID platform manager resolution [skip ci]
woahwhattheheck Oct 4, 2026
38c60b8
Merge MT SMS setup cleanup into RCS integration [skip ci]
woahwhattheheck Oct 4, 2026
2b3dda4
Merge TS.43 response cleanup into RCS integration [skip ci]
woahwhattheheck Oct 4, 2026
a95a9fd
Use exact EAP identity and refresh missing DroidGuard VMs [skip ci]
woahwhattheheck Oct 4, 2026
639051d
Integrate carrier verification platform error handling [skip ci]
woahwhattheheck Oct 4, 2026
66dd1dc
Handle absent Gaia IDs and optional SIM phone hints [skip ci]
woahwhattheheck Oct 4, 2026
76f09dc
Reuse SIM number lookups during registered SMS verification [skip ci]
woahwhattheheck Oct 4, 2026
c19f9ea
Merge registered-SMS lookup reuse into RCS integration [skip ci]
woahwhattheheck Oct 4, 2026
1a21c2b
fix(constellation): retain associated SIM slot without IMSI mapping […
woahwhattheheck Oct 4, 2026
a1c2940
Honor cancellation before phone verification side effects [skip ci]
woahwhattheheck Oct 4, 2026
0881c49
Merge phone verification cancellation boundary [skip ci]
woahwhattheheck Oct 4, 2026
7ce283e
Integrate associated SIM slot fallback [skip ci]
woahwhattheheck Oct 4, 2026
d155413
Use Android-compatible TS43 response test server [skip ci]
woahwhattheheck Oct 4, 2026
e6a18dd
Compose supplied RCS SMS and verification handlers [skip ci]
woahwhattheheck Oct 4, 2026
61587ce
Run TS43 lifecycle fixture with Android JSON implementation [skip ci]
woahwhattheheck Oct 4, 2026
8a10bae
Recover rejected credentials during automatic consent [skip ci]
woahwhattheheck Oct 4, 2026
8b9a7bc
Fix local phone verification state and per-subscription identity hand…
woahwhattheheck Oct 4, 2026
c9dadf1
test(droidguard): cover optional rb initialization matrix
woahwhattheheck Oct 4, 2026
ec451d0
RCS: expose modern APP_CERT service action
woahwhattheheck Oct 4, 2026
50799a8
fix(constellation): normalize nullable verification requests
woahwhattheheck Oct 4, 2026
294f45f
test(constellation): cover nullable verification request fields
woahwhattheheck Oct 4, 2026
09dffdd
Guard E.164 flash-call normalization on API 21+
woahwhattheheck Oct 4, 2026
64b9a16
Add API-O guard and tests for next-sync timestamps
woahwhattheheck Oct 4, 2026
79beb9e
Port DroidGuard account presence compatibility from #3784
woahwhattheheck Oct 4, 2026
0cf5025
GCM: send gcm_ver in register requests
woahwhattheheck Oct 4, 2026
270e6df
AppCert: return Android-ID Spatula fallback
woahwhattheheck Oct 4, 2026
d1c8640
GCM: preserve delete semantics on unregister
woahwhattheheck Oct 4, 2026
ff0cd7c
DroidGuard: accept first build-map variant
woahwhattheheck Oct 4, 2026
3b145e8
Auth: redact sensitive AppCert debug logging
woahwhattheheck Oct 4, 2026
08bb476
DroidGuard: redact sensitive debug logging
woahwhattheheck Oct 4, 2026
f308e0b
DroidGuard: redact sensitive debug logging
woahwhattheheck Oct 4, 2026
1e8a2d7
DroidGuard: redact sensitive debug logging
woahwhattheheck Oct 4, 2026
0a0671f
DroidGuard: redact serial values from debug logging
woahwhattheheck Oct 4, 2026
c88c539
DroidGuard: redact remote snapshot debug logging
woahwhattheheck Oct 4, 2026
21c0946
fix(constellation): evict refreshed verification cache entries
woahwhattheheck Oct 4, 2026
4e82adf
test(constellation): evict stale refreshed SIM cache
woahwhattheheck Oct 4, 2026
4772a2b
fix(constellation): track refreshed SIMs during sync
woahwhattheheck Oct 4, 2026
56c5b68
fix(rcs): align Constellation RPC timeouts
woahwhattheheck Oct 4, 2026
2012451
Phenotype: add storage info compatibility
woahwhattheheck Oct 4, 2026
2087790
Asterism: close RCS consent-version invariant gap
woahwhattheheck Oct 4, 2026
41f4339
fix(rcs): return callbacks on unsupported asterism APIs
woahwhattheheck Oct 5, 2026
3edd136
fix(rcs): complete unsupported constellation requests
woahwhattheheck Oct 5, 2026
f1e6c0f
fix(droidguard): declare account lookup permission
woahwhattheheck Oct 5, 2026
b9f741b
fix(droidguard): avoid reflective keystore SPI access
woahwhattheheck Oct 5, 2026
59a3114
fix(droidguard): support inherited VM methods
woahwhattheheck Oct 5, 2026
ab1508d
test(droidguard): escape keyword property access in RbTest
woahwhattheheck Oct 5, 2026
d980baf
test(droidguard): make RbTest VM fakes public for reflective dispatch
woahwhattheheck Oct 5, 2026
bf6a893
fix(droidguard): avoid logging payloads and match VM signatures
woahwhattheheck Oct 6, 2026
44b97d1
test(droidguard): cover overloaded snapshot dispatch
woahwhattheheck Oct 6, 2026
93764be
fix(asterism): avoid logging consent request payloads
woahwhattheheck Oct 6, 2026
93872c9
fix(constellation): avoid logging subscriber identifiers
woahwhattheheck Oct 6, 2026
74685db
privacy: avoid logging MO SMS destination
woahwhattheheck Oct 6, 2026
ca14a3b
fix(constellation): serialize verified-number merges
woahwhattheheck Oct 6, 2026
8fbe68a
privacy: avoid logging MT SMS sender
woahwhattheheck Oct 6, 2026
d1496d0
fix: avoid empty carrier allowlist deny-all
woahwhattheheck Oct 6, 2026
13d93cf
test: cover FPNV carrier gate fallback
woahwhattheheck Oct 6, 2026
974223a
fix(droidguard): resolve inherited VM methods
woahwhattheheck Oct 6, 2026
eee037a
test(droidguard): cover inherited VM method lookup
woahwhattheheck Oct 6, 2026
3236fe7
fix(rcs): verify mandatory EAP-AKA server challenge MAC before respon…
woahwhattheheck Oct 8, 2026
11d6ef4
test(rcs): reject unverified EAP-AKA challenges and mismatched lengths
woahwhattheheck Oct 8, 2026
733ffef
Reject mixed-sender multipart SMS and bound per-request unmatched inb…
woahwhattheheck Oct 8, 2026
7be0706
Regress mismatched SMS origins and incomplete multipart challenge data
woahwhattheheck Oct 8, 2026
caefb2a
Cover bounded MT-SMS backlog with matching of recent challenges
woahwhattheheck Oct 8, 2026
bc1ab3f
Make MT-SMS sender normalization explicit in Kotlin
woahwhattheheck Oct 8, 2026
8b8159c
fix(constellation): contain registered SMS telephony failures
woahwhattheheck Oct 8, 2026
931e2e2
test(constellation): cover registered SMS telephony failure
woahwhattheheck Oct 8, 2026
66cb761
fix(constellation): keep registered SMS evidence on its SIM
woahwhattheheck Oct 8, 2026
7a7d130
test(constellation): reject cross-SIM registered SMS fallback
woahwhattheheck Oct 8, 2026
f6b9ac6
fix(constellation): prepare catch-all MT SMS inbox
woahwhattheheck Oct 8, 2026
204bb14
fix(constellation): prevent cross-inbox MT SMS replay
woahwhattheheck Oct 8, 2026
dbcd5a9
test(constellation): share MT SMS inbox across subscription keys
woahwhattheheck Oct 8, 2026
712f5a3
test(constellation): fence MT SMS replay across subscription matching
woahwhattheheck Oct 8, 2026
a757d9c
Fix MT SMS inbox test handle signature
woahwhattheheck Oct 8, 2026
55db74a
Fix incomplete targeted SIM local-read fallback
woahwhattheheck Oct 8, 2026
e31a421
test(rcs): fall back on incomplete targeted SIM descriptor
woahwhattheheck Oct 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2026 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/

package com.google.android.gms.phenotype.internal;

import com.google.android.gms.common.api.Status;

interface IGetStorageInfoCallbacks {
oneway void onResult(in Status status, in byte[] storageInfo) = 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.google.android.gms.phenotype.internal;

import com.google.android.gms.common.api.internal.IStatusCallback;
import com.google.android.gms.phenotype.internal.IPhenotypeCallbacks;
import com.google.android.gms.phenotype.internal.IGetStorageInfoCallbacks;
import com.google.android.gms.phenotype.Flag;
import com.google.android.gms.phenotype.RegistrationInfo;

Expand Down Expand Up @@ -32,4 +33,5 @@ interface IPhenotypeService {
oneway void syncAllAfterOperation(IPhenotypeCallbacks callbacks, long p1) = 23; // returns via callbacks.onSyncFinished()
oneway void setRuntimeProperties(IStatusCallback callbacks, String p1, in byte[] p2) = 24;
// oneway void setExternalExperiments(IStatusCallback callbacks, String p1, in List<byte[]> p2) = 25;
oneway void getStorageInfo(IGetStorageInfoCallbacks callbacks) = 26; // Binder transaction 27; returns via callbacks.onResult()
}
40 changes: 40 additions & 0 deletions play-services-asterism/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2026, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/

apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply plugin: 'signing'

android {
namespace "org.microg.gms.asterism"

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-asterism'

dependencies {
implementation project(':play-services-basement')

annotationProcessor project(":safe-parcel-processor")
}
55 changes: 55 additions & 0 deletions play-services-asterism/core/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2026, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/

apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply plugin: 'kotlin-android'
apply plugin: 'signing'

android {
namespace "org.microg.gms.asterism.core"

compileSdkVersion androidCompileSdk
buildToolsVersion "$androidBuildVersionTools"

defaultConfig {
versionName version
minSdkVersion androidMinSdk
targetSdkVersion androidTargetSdk
}

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
}

testOptions {
unitTests.returnDefaultValues = true
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = 1.8
}
}

apply from: '../../gradle/publish-android.gradle'

description = 'microG service implementation for play-services-asterism'

dependencies {
api project(':play-services-asterism')

implementation project(':play-services-base-core')
implementation project(':play-services-constellation-core')

testImplementation 'junit:junit:4.13.2'
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutineVersion"
testImplementation 'org.mockito:mockito-core:5.11.0'
}
19 changes: 19 additions & 0 deletions play-services-asterism/core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ SPDX-FileCopyrightText: 2026, microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application>
<service
android:name=".AsterismApiService"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="com.google.android.gms.asterism.service.START" />
</intent-filter>
</service>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.google.android.gms.asterism

import org.microg.gms.constellation.core.proto.AsterismClient
import org.microg.gms.constellation.core.proto.Consent
import org.microg.gms.constellation.core.proto.ConsentVersion

val GetAsterismConsentRequest.asterismClient: AsterismClient
get() = AsterismClient.fromValue(asterismClientValue) ?: AsterismClient.UNKNOWN_CLIENT

fun getAsterismConsentResponse(
requestCode: Int,
consentState: Consent,
gmscoreIidToken: String?,
fid: String?,
consentVersion: ConsentVersion
): GetAsterismConsentResponse {
val consentStateValue =
if (consentState == Consent.CONSENTED || consentState == Consent.CONSENT_UNKNOWN) {
consentState.value
} else {
Consent.NO_CONSENT.value
}

return GetAsterismConsentResponse(
requestCode,
consentStateValue,
gmscoreIidToken,
fid,
consentVersion.value
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.google.android.gms.asterism

import org.microg.gms.constellation.core.proto.AsterismClient
import org.microg.gms.constellation.core.proto.AsterismConsent.DeviceConsentVersion
import org.microg.gms.constellation.core.proto.Consent
import org.microg.gms.constellation.core.proto.ConsentSource
import org.microg.gms.constellation.core.proto.ConsentVersion

enum class SetAsterismConsentRequestStatus(val value: Int) {
RESOURCE_TOS(0),
STATIC_STRING(1),
RCS(2),
ON_DEMAND(3),
RESOURCE_TOS_FALLBACK(4);

companion object {
fun fromValue(value: Int): SetAsterismConsentRequestStatus =
entries.find { it.value == value } ?: RESOURCE_TOS_FALLBACK
}
}

val SetAsterismConsentRequest.asterismClient: AsterismClient
get() = AsterismClient.fromValue(asterismClientValue) ?: AsterismClient.UNKNOWN_CLIENT

val SetAsterismConsentRequest.consent: Consent
get() = Consent.fromValue(consentValue) ?: Consent.CONSENT_UNKNOWN

val SetAsterismConsentRequest.consentVersion: ConsentVersion?
get() = ConsentVersion.fromValue(consentVersionValue)

val SetAsterismConsentRequest.deviceConsentSource: ConsentSource
get() = ConsentSource.fromValue(deviceConsentSourceValue) ?: ConsentSource.SOURCE_UNSPECIFIED

val SetAsterismConsentRequest.deviceConsentVersion: DeviceConsentVersion
get() = DeviceConsentVersion.fromValue(deviceConsentVersionValue).let {
if (it == null || it == DeviceConsentVersion.UNKNOWN) {
DeviceConsentVersion.PHONE_VERIFICATION_DEFAULT
} else {
it
}
}

val SetAsterismConsentRequest.status: SetAsterismConsentRequestStatus
get() = SetAsterismConsentRequestStatus.fromValue(statusValue)

fun SetAsterismConsentRequest.isDevicePnvrFlow(): Boolean {
return asterismClient == AsterismClient.CONSTELLATION &&
deviceConsentSourceValue > 0 &&
deviceConsentVersionValue > 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package org.microg.gms.asterism.core

import android.content.Context
import android.os.Build
import android.util.Log
import com.google.android.gms.asterism.GetAsterismConsentRequest
import com.google.android.gms.asterism.SetAsterismConsentRequest
import com.google.android.gms.asterism.internal.IAsterismApiService
import com.google.android.gms.asterism.internal.IAsterismCallbacks
import com.google.android.gms.common.Feature
import com.google.android.gms.common.api.Status
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import org.microg.gms.BaseService
import org.microg.gms.common.GmsService
import org.microg.gms.common.PackageUtils

private const val TAG = "AsterismApiService"

class AsterismApiService : BaseService(TAG, GmsService.ASTERISM) {
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

override fun handleServiceRequest(
callback: IGmsCallbacks?,
request: GetServiceRequest?,
service: GmsService?
) {
val packageName = PackageUtils.getAndCheckCallingPackage(this, request?.packageName)
if (!PackageUtils.isGooglePackage(this, packageName)) {
throw SecurityException("$packageName is not a Google package")
}
callback!!.onPostInitCompleteWithConnectionInfo(
0,
AsterismApiServiceImpl(this, serviceScope).asBinder(),
ConnectionInfo().apply {
features = arrayOf(
Feature("asterism_consent", 3),
Feature("one_time_verification", 1),
Feature("carrier_auth", 1),
Feature("verify_phone_number", 2),
Feature("get_iid_token", 1),
Feature("get_pnv_capabilities", 1),
Feature("ts43", 1),
Feature("verify_phone_number_local_read", 1)
)
}
)
}

override fun onDestroy() {
super.onDestroy()
serviceScope.cancel("AsterismApiService destroyed")
}
}

class AsterismApiServiceImpl(
private val context: Context,
serviceScope: CoroutineScope
) : IAsterismApiService.Stub() {
private val requestDispatcher = AsterismRequestDispatcher(serviceScope)
override fun getAsterismConsent(
cb: IAsterismCallbacks?,
request: GetAsterismConsentRequest?,
) {
Log.i(TAG, "getAsterismConsent()")
if (cb == null || request == null) return
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
cb.onConsentFetched(Status.INTERNAL_ERROR, null)
return
}
requestDispatcher.dispatch(cb, "getAsterismConsent") { callbacks ->
handleGetAsterismConsent(context, callbacks, request)
}
}

override fun setAsterismConsent(cb: IAsterismCallbacks?, request: SetAsterismConsentRequest?) {
Log.i(TAG, "setAsterismConsent()")
if (cb == null || request == null) return
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
cb.onConsentRegistered(Status.INTERNAL_ERROR, null)
return
}
requestDispatcher.dispatch(cb, "setAsterismConsent") { callbacks ->
handleSetAsterismConsent(context, callbacks, request)
}
}

override fun getIsPnvrConstellationDevice(cb: IAsterismCallbacks?) {
Log.i(TAG, "getIsPnvrConstellationDevice()")
if (cb == null) return
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
cb.onIsPnvrConstellationDevice(Status.INTERNAL_ERROR, false)
return
}
requestDispatcher.dispatch(cb, "getIsPnvrConstellationDevice") { callbacks ->
handleGetIsPnvrConstellationDevice(context, callbacks)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2026 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/

package org.microg.gms.asterism.core

import android.os.IBinder
import android.os.RemoteException
import android.util.Log
import com.google.android.gms.asterism.GetAsterismConsentResponse
import com.google.android.gms.asterism.SetAsterismConsentResponse
import com.google.android.gms.asterism.internal.IAsterismCallbacks
import com.google.android.gms.common.api.Status
import kotlinx.coroutines.CancellationException

private const val TAG = "AsterismCallbacks"

internal class AsterismCallbacksWrapper(private val delegate: IAsterismCallbacks) : IAsterismCallbacks {
override fun onConsentFetched(status: Status?, response: GetAsterismConsentResponse?) =
runRemote("onConsentFetched") { delegate.onConsentFetched(status, response) }

override fun onConsentRegistered(status: Status?, response: SetAsterismConsentResponse?) =
runRemote("onConsentRegistered") { delegate.onConsentRegistered(status, response) }

override fun onIsPnvrConstellationDevice(status: Status?, isPnvrDevice: Boolean) =
runRemote("onIsPnvrConstellationDevice") { delegate.onIsPnvrConstellationDevice(status, isPnvrDevice) }

override fun asBinder(): IBinder = delegate.asBinder()

private inline fun runRemote(method: String, block: () -> Unit) {
try {
block()
} catch (e: CancellationException) {
throw e
} catch (e: RemoteException) {
Log.w(TAG, "RemoteException in $method", e)
} catch (e: RuntimeException) {
Log.w(TAG, "RuntimeException delivering $method", e)
}
}
}
Loading