From 4e8200a1eaae9bfcb4d1720f34c5240a32f6ccfd Mon Sep 17 00:00:00 2001 From: DaVinci9196 <150454414+DaVinci9196@users.noreply.github.com> Date: Tue, 30 Jun 2026 05:42:31 +0800 Subject: [PATCH] Identity: Add IdentityCredentialApiService dummy (#3484) Co-authored-by: Marvin W --- build.gradle | 2 +- .../org/microg/gms/common/GmsService.java | 2 +- play-services-core/build.gradle | 1 + .../src/main/AndroidManifest.xml | 7 + .../gms/auth/credentials/CredentialIntents.kt | 50 +++ .../IdentityCredentialChooserActivity.kt | 295 ++++++++++++++++++ .../credentials/provider/GoogleIdService.kt | 1 + .../provider/PublicKeyProxyActivity.kt | 25 +- .../provider/SignInProxyActivity.kt | 44 +-- .../screenlock/ScreenLockTransportHandler.kt | 33 +- .../build.gradle | 37 +++ .../core/build.gradle | 52 +++ .../core/src/main/AndroidManifest.xml | 15 + .../IdentityCredentialApiService.kt | 215 +++++++++++++ .../src/main/AndroidManifest.xml | 4 + .../ClearCreationOptionsRequest.aidl | 8 + .../ClearCreationOptionsResponse.aidl | 8 + .../ClearCredentialStateRequest.aidl | 8 + .../ClearCredentialStateResponse.aidl | 8 + .../ClearExportRequest.aidl | 8 + .../ClearExportResponse.aidl | 8 + .../ClearRegistryRequest.aidl | 8 + .../ClearRegistryResponse.aidl | 8 + .../CreateCredentialHandle.aidl | 8 + .../CreateCredentialRequest.aidl | 8 + .../CreateCredentialResponse.aidl | 8 + .../CredentialInformationRequest.aidl | 8 + .../CredentialInformationResponse.aidl | 8 + .../identitycredentials/CredentialOption.aidl | 8 + .../CredentialTransferCapabilities.aidl | 8 + ...ExportCredentialsToDeviceSetupRequest.aidl | 8 + ...xportCredentialsToDeviceSetupResponse.aidl | 8 + .../GetCredentialRequest.aidl | 8 + ...CredentialTransferCapabilitiesRequest.aidl | 8 + ...mportCredentialsForDeviceSetupRequest.aidl | 8 + ...portCredentialsForDeviceSetupResponse.aidl | 8 + .../ImportCredentialsRequest.aidl | 8 + .../PendingGetCredentialHandle.aidl | 8 + .../PendingImportCredentialsHandle.aidl | 8 + .../RegisterCreationOptionsRequest.aidl | 8 + .../RegisterCreationOptionsResponse.aidl | 8 + .../RegisterExportRequest.aidl | 8 + .../RegisterExportResponse.aidl | 8 + .../RegistrationRequest.aidl | 8 + .../RegistrationResponse.aidl | 8 + .../SignalCredentialStateRequest.aidl | 8 + .../SignalCredentialStateResponse.aidl | 8 + .../IIdentityCredentialCallbacks.aidl | 44 +++ .../internal/IIdentityCredentialService.aidl | 42 +++ .../CallingAppInfoParcelable.java | 103 ++++++ .../ClearCreationOptionsRequest.java | 138 ++++++++ .../ClearCreationOptionsResponse.java | 49 +++ .../ClearCredentialStateRequest.java | 31 ++ .../ClearCredentialStateResponse.java | 31 ++ .../ClearExportRequest.java | 74 +++++ .../ClearExportResponse.java | 53 ++++ .../ClearRegistryRequest.java | 155 +++++++++ .../ClearRegistryResponse.java | 50 +++ .../CreateCredentialHandle.java | 71 +++++ .../CreateCredentialRequest.java | 112 +++++++ .../CreateCredentialResponse.java | 63 ++++ .../gms/identitycredentials/Credential.java | 65 ++++ .../CredentialInformation.java | 70 +++++ .../CredentialInformationRequest.java | 39 +++ .../CredentialInformationResponse.java | 44 +++ .../identitycredentials/CredentialOption.java | 115 +++++++ .../CredentialTransferCapabilities.java | 107 +++++++ ...ExportCredentialsToDeviceSetupRequest.java | 65 ++++ ...xportCredentialsToDeviceSetupResponse.java | 105 +++++++ .../GetCredentialRequest.java | 98 ++++++ ...CredentialTransferCapabilitiesRequest.java | 49 +++ .../IdentityCredentialClient.java | 119 +++++++ .../IdentityCredentialManager.java | 42 +++ ...mportCredentialsForDeviceSetupRequest.java | 78 +++++ ...portCredentialsForDeviceSetupResponse.java | 60 ++++ .../ImportCredentialsRequest.java | 66 ++++ .../PendingGetCredentialHandle.java | 54 ++++ .../PendingImportCredentialsHandle.java | 52 +++ .../RegisterCreationOptionsRequest.java | 109 +++++++ .../RegisterCreationOptionsResponse.java | 31 ++ .../RegisterExportRequest.java | 79 +++++ .../RegisterExportResponse.java | 31 ++ .../RegistrationRequest.java | 142 +++++++++ .../RegistrationResponse.java | 31 ++ .../SignalCredentialStateRequest.java | 82 +++++ .../SignalCredentialStateResponse.java | 31 ++ .../IdentityCredentialApiClient.java | 145 +++++++++ .../IdentityCredentialClientImpl.java | 212 +++++++++++++ settings.gradle | 2 + 89 files changed, 4117 insertions(+), 61 deletions(-) create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/CredentialIntents.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/identity/IdentityCredentialChooserActivity.kt create mode 100644 play-services-identity-credentials/build.gradle create mode 100644 play-services-identity-credentials/core/build.gradle create mode 100644 play-services-identity-credentials/core/src/main/AndroidManifest.xml create mode 100644 play-services-identity-credentials/core/src/main/kotlin/org/microg/gms/identitycredentials/IdentityCredentialApiService.kt create mode 100644 play-services-identity-credentials/src/main/AndroidManifest.xml create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCreationOptionsRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCreationOptionsResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCredentialStateRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCredentialStateResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearExportRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearExportResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearRegistryRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearRegistryResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialHandle.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialInformationRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialInformationResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialOption.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialTransferCapabilities.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/GetCredentialRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/GetCredentialTransferCapabilitiesRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/PendingGetCredentialHandle.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/PendingImportCredentialsHandle.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterCreationOptionsRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterCreationOptionsResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterExportRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterExportResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegistrationRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegistrationResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/SignalCredentialStateRequest.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/SignalCredentialStateResponse.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/internal/IIdentityCredentialCallbacks.aidl create mode 100644 play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/internal/IIdentityCredentialService.aidl create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CallingAppInfoParcelable.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCreationOptionsRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCreationOptionsResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCredentialStateRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCredentialStateResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearExportRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearExportResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearRegistryRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearRegistryResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialHandle.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/Credential.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformation.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformationRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformationResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialOption.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialTransferCapabilities.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/GetCredentialRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/GetCredentialTransferCapabilitiesRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/IdentityCredentialClient.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/IdentityCredentialManager.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/PendingGetCredentialHandle.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/PendingImportCredentialsHandle.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterCreationOptionsRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterCreationOptionsResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterExportRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterExportResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegistrationRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegistrationResponse.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/SignalCredentialStateRequest.java create mode 100644 play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/SignalCredentialStateResponse.java create mode 100644 play-services-identity-credentials/src/main/java/org/microg/gms/identitycredentials/IdentityCredentialApiClient.java create mode 100644 play-services-identity-credentials/src/main/java/org/microg/gms/identitycredentials/IdentityCredentialClientImpl.java diff --git a/build.gradle b/build.gradle index 296bd8709..d64e7e78a 100644 --- a/build.gradle +++ b/build.gradle @@ -73,7 +73,7 @@ def execResult(... args) { } def ignoreGit = providers.environmentVariable('GRADLE_MICROG_VERSION_WITHOUT_GIT').getOrElse('0') == '1' -def gmsVersion = "25.09.32" +def gmsVersion = "25.24.32" def gmsVersionCode = Integer.parseInt(gmsVersion.replaceAll('\\.', '')) def vendingVersion = "40.2.26" def vendingVersionCode = Integer.parseInt(vendingVersion.replaceAll('\\.', '')) diff --git a/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java b/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java index 97b136426..0026c7278 100644 --- a/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java +++ b/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java @@ -370,7 +370,7 @@ public enum GmsService { GOOGLESETTINGS(349), HTTPFLAGS(350), SETUP_SERVICES_REMOTE_SETUP(351), - IDENTITY_CREDENTIALS(352), + IDENTITY_CREDENTIALS(352, "com.google.android.gms.identitycredentials.service.START"), AMBIENT_CONTEXT(353), SAFE_BROWSING(354), MULTIDEVICE_API_FEATURE_SETTINGS(355), diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle index 967b2d442..d7134cab3 100644 --- a/play-services-core/build.gradle +++ b/play-services-core/build.gradle @@ -37,6 +37,7 @@ dependencies { implementation project(':play-services-fido-core') implementation project(':play-services-fitness-core') implementation project(':play-services-gmscompliance-core') + implementation project(':play-services-identity-credentials-core') implementation project(':play-services-location-core') implementation project(':play-services-location-core-base') implementation project(':play-services-oss-licenses-core') diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index a17d7f03e..ec46dd59d 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -649,6 +649,13 @@ android:theme="@style/Theme.App.Translucent" android:excludeFromRecents="true"/> + + { + isCreatePath = false + Log.d(TAG, "onCreate get pkg=$callingPackage options=${getReq.credentialOptions.size}") + routeGet(getReq) + } + createReq != null -> { + isCreatePath = true + Log.d(TAG, "onCreate create pkg=$callingPackage type=${createReq.type} origin=${createReq.origin}") + routeCreate(createReq) + } + else -> finishWithGetException(GetCredentialUnknownException("Missing request payload")) + } + }.onFailure { e -> + Log.e(TAG, "onCreate parse failed", e) + if (isCreatePath) finishWithCreateException(CreateCredentialUnknownException(e.message ?: "Internal error")) + else finishWithGetException(GetCredentialUnknownException(e.message ?: "Internal error")) + } + } + + private fun routeGet(req: GetCredentialRequest) { + val option = req.credentialOptions.firstOrNull() + ?: return finishWithGetException(NoCredentialException("No credential options requested")) + when (option.type) { + PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> startPasskeyGet(option) + TYPE_GOOGLE_ID_TOKEN_CREDENTIAL -> startGoogleSignIn(option) + PasswordCredential.TYPE_PASSWORD_CREDENTIAL -> + finishWithGetException(NoCredentialException("Password credentials are not stored")) + else -> finishWithGetException(NoCredentialException("Unsupported type: ${option.type}")) + } + } + + private fun routeCreate(req: CreateCredentialRequest) { + Log.d(TAG, "routeCreate type=${req.type} pkg=$callingPackage") + when (req.type) { + PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> startPasskeyCreate(req) + else -> finishWithCreateException(CreateCredentialNoCreateOptionException("Unsupported create type: ${req.type}")) + } + } + + private fun startPasskeyGet(option: CredentialOption) { + val data = option.credentialRetrievalData + val requestJson = data?.getString(PUBKEY_REQ_JSON_KEY) + ?: return finishWithGetException(NoCredentialException("Passkey request missing requestJson")) + val pkOptions = runCatching { + JSONObject(requestJson).parsePublicKeyCredentialRequestOptions() + }.getOrElse { + Log.e(TAG, "Passkey JSON parse failed", it) + return finishWithGetException(GetCredentialUnknownException("Invalid passkey requestJson")) + } + val fidoIntent = buildFidoAuthenticateIntent(SOURCE_APP, pkOptions.serializeToBytes(), callingPackage, TYPE_SIGN) + startActivityForResult(fidoIntent, REQ_CODE_FIDO) + } + + private fun startPasskeyCreate(req: CreateCredentialRequest) { + val requestJson = req.requestJson ?: req.credentialData.getString(PUBKEY_REQ_JSON_KEY) + if (requestJson.isNullOrBlank()) { + return finishWithCreateException(CreateCredentialUnknownException("Passkey create missing requestJson")) + } + val pkOptions = runCatching { + JSONObject(requestJson).parsePublicKeyCredentialCreationOptions() + }.getOrElse { + Log.e(TAG, "Passkey create JSON parse failed", it) + return finishWithCreateException(CreateCredentialUnknownException("Invalid passkey requestJson")) + } + val fidoIntent = buildFidoAuthenticateIntent(SOURCE_APP, pkOptions.serializeToBytes(), callingPackage, TYPE_REGISTER) + startActivityForResult(fidoIntent, REQ_CODE_FIDO) + } + + private fun startGoogleSignIn(option: CredentialOption) { + val data = option.credentialRetrievalData ?: Bundle() + val serverClientId = (data.getString(GOOGLE_ID_SERVER_CLIENT_ID) ?: data.getString(GOOGLE_ID_SIWG_SERVER_CLIENT_ID)).orEmpty() + if (serverClientId.isBlank()) { + return finishWithGetException(NoCredentialException("GoogleIdToken option missing serverClientId")) + } + val nonce = data.getString(GOOGLE_ID_NONCE) ?: data.getString(GOOGLE_ID_SIWG_NONCE) + val filterByAuthorized = data.getBoolean(GOOGLE_ID_FILTER_BY_AUTHORIZED_ACCOUNTS, false) + val autoSelect = data.getBoolean(GOOGLE_ID_ANDROIDX_AUTO_SELECT, false) + + val signInRequest = BeginSignInRequest.Builder() + .setGoogleIdTokenRequestOptions( + BeginSignInRequest.GoogleIdTokenRequestOptions.builder() + .setSupported(true) + .setServerClientId(serverClientId) + .setFilterByAuthorizedAccounts(filterByAuthorized) + .apply { nonce?.let { setNonce(it) } } + .build() + ) + .setAutoSelectEnabled(autoSelect) + .build() + + val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestEmail() + .requestIdToken(serverClientId) + .build() + + val signInIntent = buildAssistedSignInIntent( + requestExtraKey = BEGIN_SIGN_IN_REQUEST, + serializedRequest = SafeParcelableSerializer.serializeToBytes(signInRequest), + googleSignInOptions = gso, + callingPackage = callingPackage + ) + startActivityForResult(signInIntent, REQ_CODE_SIGN_IN) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + Log.d(TAG, "onActivityResult: requestCode: $requestCode resultCode: $resultCode") + when (requestCode) { + REQ_CODE_FIDO -> handleFidoResult(resultCode, data) + REQ_CODE_SIGN_IN -> handleSignInResult(resultCode, data) + else -> finishWithGetException(GetCredentialUnknownException("Unexpected requestCode=$requestCode")) + } + } + + private fun handleFidoResult(resultCode: Int, data: Intent?) { + Log.d(TAG, "handleFidoResult: data: $data") + if (resultCode != RESULT_OK || data == null) { + return if (isCreatePath) finishWithCreateException(CreateCredentialUnknownException("Passkey flow canceled")) + else finishWithGetException(GetCredentialCancellationException("Passkey flow canceled")) + } + runCatching { + val credentialBytes = data.getByteArrayExtra(FIDO2_KEY_CREDENTIAL_EXTRA) + ?: throw IllegalStateException("FIDO returned no credential") + val publicKeyCredential = com.google.android.gms.fido.fido2.api.common.PublicKeyCredential + .deserializeFromBytes(credentialBytes) + (publicKeyCredential.response as? AuthenticatorErrorResponse)?.let { err -> + throw IllegalStateException(err.errorMessage ?: err.errorCode.toString()) + } + val json = publicKeyCredential.toJson() + val credData = Bundle().apply { + putString(if (isCreatePath) PUBKEY_RES_REG_JSON_KEY else PUBKEY_RES_AUTH_JSON_KEY, json) + } + Log.d(TAG, "handleFidoResult: $credData") + finishWithCredential(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, credData) + }.onFailure { e -> + Log.e(TAG, "handleFidoResult failed", e) + val msg = e.message ?: "FIDO result error" + if (isCreatePath) finishWithCreateException(CreateCredentialUnknownException(msg)) + else finishWithGetException(GetCredentialUnknownException(msg)) + } + } + + private fun handleSignInResult(resultCode: Int, data: Intent?) { + if (resultCode != RESULT_OK || data == null) { + return finishWithGetException(GetCredentialCancellationException("Sign-in canceled")) + } + runCatching { + val bytes = data.getByteArrayExtra(AuthConstants.SIGN_IN_CREDENTIAL) + ?: throw IllegalStateException("Sign-in result missing credential") + val credential = SafeParcelableSerializer.deserializeFromBytes(bytes, SignInCredential.CREATOR) + val credData = Bundle().apply { + putString(GOOGLE_ID_BUNDLE_KEY_ID, credential.id) + credential.googleIdToken?.let { putString(GOOGLE_ID_BUNDLE_KEY_ID_TOKEN, it) } + credential.displayName?.let { putString(GOOGLE_ID_BUNDLE_KEY_DISPLAY_NAME, it) } + credential.givenName?.let { putString(GOOGLE_ID_BUNDLE_KEY_GIVEN_NAME, it) } + credential.familyName?.let { putString(GOOGLE_ID_BUNDLE_KEY_FAMILY_NAME, it) } + credential.profilePictureUri?.let { putString(GOOGLE_ID_BUNDLE_KEY_PROFILE_PICTURE_URI, it.toString()) } + } + finishWithCredential(TYPE_GOOGLE_ID_TOKEN_CREDENTIAL, credData) + }.onFailure { e -> + Log.e(TAG, "handleSignInResult failed", e) + finishWithGetException(GetCredentialUnknownException(e.message ?: "Sign-in result error")) + } + } + + private fun finishWithCredential(type: String, credentialData: Bundle) { + val responseBundle = Bundle().apply { + if (isCreatePath) { + putString(PROVIDER_EXTRA_CREATE_RESPONSE_TYPE, type) + putBundle(PROVIDER_EXTRA_CREATE_REQUEST_DATA, credentialData) + } else { + putString(PROVIDER_EXTRA_CREDENTIAL_TYPE, type) + putBundle(PROVIDER_EXTRA_CREDENTIAL_DATA, credentialData) + } + } + val extraKey = if (isCreatePath) EXTRA_CREATE_RESPONSE_BUNDLE else EXTRA_GET_RESPONSE_BUNDLE + Log.d(TAG, "finishWithCredential: extraKey: $extraKey responseBundle: $responseBundle") + setResult(RESULT_OK, Intent().putExtra(extraKey, responseBundle)) + finish() + } + + private fun finishWithGetException(e: androidx.credentials.exceptions.GetCredentialException) { + finishWithExceptionBundle(EXTRA_GET_EXCEPTION_BUNDLE, e.type, e.message ?: e.javaClass.simpleName) + } + + private fun finishWithCreateException(e: androidx.credentials.exceptions.CreateCredentialException) { + finishWithExceptionBundle(EXTRA_CREATE_EXCEPTION_BUNDLE, e.type, e.message ?: e.javaClass.simpleName) + } + + private fun finishWithExceptionBundle(extraKey: String, type: String, message: String) { + val bundle = Bundle().apply { + putString(PROVIDER_EXTRA_EXCEPTION_TYPE, type) + putString(PROVIDER_EXTRA_EXCEPTION_MESSAGE, message) + } + setResult(RESULT_OK, Intent().putExtra(extraKey, bundle)) + finish() + } + + private fun readNestedParcelable(name: String, clazz: Class): T? = + intent.getBundleExtra(name)?.let { bundle -> + bundle.classLoader = clazz.classLoader + BundleCompat.getParcelable(bundle, name, clazz) + } + + companion object { + private const val REQ_CODE_FIDO = 0x1001 + private const val REQ_CODE_SIGN_IN = 0x1002 + + private const val EXTRA_GET_RESPONSE_BUNDLE = "android.service.credentials.extra.GET_CREDENTIAL_RESPONSE" + private const val EXTRA_GET_EXCEPTION_BUNDLE = "android.service.credentials.extra.GET_CREDENTIAL_EXCEPTION" + private const val EXTRA_CREATE_RESPONSE_BUNDLE = "android.service.credentials.extra.CREATE_CREDENTIAL_RESPONSE" + private const val EXTRA_CREATE_EXCEPTION_BUNDLE = "android.service.credentials.extra.CREATE_CREDENTIAL_EXCEPTION" + private const val PROVIDER_EXTRA_CREDENTIAL_TYPE = "androidx.credentials.provider.extra.EXTRA_CREDENTIAL_TYPE" + private const val PROVIDER_EXTRA_CREDENTIAL_DATA = "androidx.credentials.provider.extra.EXTRA_CREDENTIAL_DATA" + private const val PROVIDER_EXTRA_CREATE_RESPONSE_TYPE = "androidx.credentials.provider.extra.CREATE_CREDENTIAL_RESPONSE_TYPE" + private const val PROVIDER_EXTRA_CREATE_REQUEST_DATA = "androidx.credentials.provider.extra.CREATE_CREDENTIAL_REQUEST_DATA" + private const val PROVIDER_EXTRA_EXCEPTION_TYPE = "androidx.credentials.provider.extra.CREATE_CREDENTIAL_EXCEPTION_TYPE" + private const val PROVIDER_EXTRA_EXCEPTION_MESSAGE = "androidx.credentials.provider.extra.CREATE_CREDENTIAL_EXCEPTION_MESSAGE" + + private const val PUBKEY_REQ_JSON_KEY = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON" + private const val PUBKEY_RES_AUTH_JSON_KEY = "androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON" + private const val PUBKEY_RES_REG_JSON_KEY = "androidx.credentials.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON" + } +} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/GoogleIdService.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/GoogleIdService.kt index ed24e4cb0..489b9b580 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/GoogleIdService.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/GoogleIdService.kt @@ -172,6 +172,7 @@ const val GOOGLE_ID_BUNDLE_KEY_GIVEN_NAME = "com.google.android.libraries.identi const val GOOGLE_ID_BUNDLE_KEY_FAMILY_NAME = "com.google.android.libraries.identity.googleid.BUNDLE_KEY_FAMILY_NAME" const val GOOGLE_ID_BUNDLE_KEY_PHONE_NUMBER = "com.google.android.libraries.identity.googleid.BUNDLE_KEY_PHONE_NUMBER" const val GOOGLE_ID_BUNDLE_KEY_PROFILE_PICTURE_URI = "com.google.android.libraries.identity.googleid.BUNDLE_KEY_PROFILE_PICTURE_URI" +const val GOOGLE_ID_FILTER_BY_AUTHORIZED_ACCOUNTS = "com.google.android.libraries.identity.googleid.BUNDLE_KEY_FILTER_BY_AUTHORIZED_ACCOUNTS" // Credential types const val TYPE_GOOGLE_ID_TOKEN_CREDENTIAL = "com.google.android.libraries.identity.googleid.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL" diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/PublicKeyProxyActivity.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/PublicKeyProxyActivity.kt index c2d407699..72c35dc18 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/PublicKeyProxyActivity.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/PublicKeyProxyActivity.kt @@ -18,14 +18,8 @@ import androidx.credentials.provider.ProviderGetCredentialRequest import com.google.android.gms.fido.Fido.FIDO2_KEY_CREDENTIAL_EXTRA import com.google.android.gms.fido.fido2.api.common.* import org.json.JSONObject -import org.microg.gms.common.GmsService -import org.microg.gms.fido.core.ui.ACTION_FIDO_AUTHENTICATE -import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_CALLER +import org.microg.gms.auth.credentials.buildFidoAuthenticateIntent import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_CREDENTIAL_ID -import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_OPTIONS -import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_SERVICE -import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_SOURCE -import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_TYPE import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.SOURCE_APP import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.SOURCE_BROWSER import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.TYPE_REGISTER @@ -50,7 +44,7 @@ class PublicKeyProxyActivity : CredentialProviderActivity() { val credentialIdString = intent.getStringExtra(KEY_CREDENTIAL_ID) val (optionsBytes, source) = buildRequestOptions(options, isBrowserRequest, request.callingAppInfo.origin, option.clientDataHash) - val fidoIntent = createFidoIntent(source, optionsBytes, request.callingAppInfo.packageName, TYPE_SIGN, credentialIdString) + val fidoIntent = buildFidoAuthenticateIntent(source, optionsBytes, request.callingAppInfo.packageName, TYPE_SIGN, credentialIdString) startActivityForResult(fidoIntent, REQUEST_CODE_FIDO) } @@ -76,7 +70,7 @@ class PublicKeyProxyActivity : CredentialProviderActivity() { Log.d(TAG, "handlePasskeyCreate: options: $options") val (optionsBytes, source) = buildCreationOptions(options, isBrowserRequest, origin, publicKeyRequest.clientDataHash) - val fidoIntent = createFidoIntent(source, optionsBytes, callingPackage, TYPE_REGISTER) + val fidoIntent = buildFidoAuthenticateIntent(source, optionsBytes, callingPackage, TYPE_REGISTER) startActivityForResult(fidoIntent, REQUEST_CODE_FIDO) Log.d(TAG, "Launched FIDO authenticator by PasskeyCreate") @@ -118,19 +112,6 @@ class PublicKeyProxyActivity : CredentialProviderActivity() { } } - fun createFidoIntent( - source: String, optionsBytes: ByteArray, callingPackage: String, type: String, credentialIdString: String? = null - ): Intent = Intent(ACTION_FIDO_AUTHENTICATE).apply { - `package` = packageName - putExtra(KEY_SERVICE, GmsService.FIDO2_API.SERVICE_ID) - putExtra(KEY_SOURCE, source) - putExtra(KEY_TYPE, type) - putExtra(KEY_OPTIONS, optionsBytes) - putExtra(KEY_CALLER, callingPackage) - credentialIdString?.let { putExtra(KEY_CREDENTIAL_ID, it) } - } - - private fun handleFidoSuccess(publicKeyCredential: PublicKeyCredential) = runCatching { when (val response = publicKeyCredential.response) { is AuthenticatorAttestationResponse -> { diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/SignInProxyActivity.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/SignInProxyActivity.kt index e598964e8..49e90df78 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/SignInProxyActivity.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/provider/SignInProxyActivity.kt @@ -20,10 +20,8 @@ import com.google.android.gms.auth.api.identity.SignInCredential import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.android.gms.common.internal.safeparcel.SafeParcelableSerializer import org.microg.gms.auth.AuthConstants -import org.microg.gms.auth.signin.ACTION_ASSISTED_SIGN_IN -import org.microg.gms.auth.signin.CLIENT_PACKAGE_NAME +import org.microg.gms.auth.credentials.buildAssistedSignInIntent import org.microg.gms.auth.signin.GET_SIGN_IN_INTENT_REQUEST -import org.microg.gms.auth.signin.GOOGLE_SIGN_IN_OPTIONS private const val TAG = "SignInProxyActivity" private const val REQUEST_CODE_SIGN_IN = 100 @@ -32,31 +30,23 @@ private const val REQUEST_CODE_SIGN_IN = 100 class SignInProxyActivity : CredentialProviderActivity() { override fun onProviderGetCredentialRequest(request: ProviderGetCredentialRequest) { - val bundle = Bundle().apply { - val signInRequest = GetSignInIntentRequest.builder() - .setServerClientId(intent.getStringExtra(GOOGLE_ID_SIWG_SERVER_CLIENT_ID) ?: "") - .apply { - intent.getStringExtra(GOOGLE_ID_SIWG_NONCE)?.let { setNonce(it) } - } - .build() - - val googleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .requestEmail() - .requestIdToken(intent.getStringExtra(GOOGLE_ID_SIWG_SERVER_CLIENT_ID) ?: "") - .apply { intent.getStringExtra(GOOGLE_ID_SIWG_ACCOUNT_NAME)?.let { setAccountName(it) } } - .build() - - putByteArray(GET_SIGN_IN_INTENT_REQUEST, SafeParcelableSerializer.serializeToBytes(signInRequest)) - putByteArray(GOOGLE_SIGN_IN_OPTIONS, SafeParcelableSerializer.serializeToBytes(googleSignInOptions)) - putString(CLIENT_PACKAGE_NAME, intent.getStringExtra(GOOGLE_ID_SIWG_CALLER_PACKAGE)) - } - startActivityForResult( - Intent(ACTION_ASSISTED_SIGN_IN).apply { - `package` = packageName - putExtras(bundle) - }, - REQUEST_CODE_SIGN_IN + val serverClientId = intent.getStringExtra(GOOGLE_ID_SIWG_SERVER_CLIENT_ID).orEmpty() + val signInRequest = GetSignInIntentRequest.builder() + .setServerClientId(serverClientId) + .apply { intent.getStringExtra(GOOGLE_ID_SIWG_NONCE)?.let { setNonce(it) } } + .build() + val googleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestEmail() + .requestIdToken(serverClientId) + .apply { intent.getStringExtra(GOOGLE_ID_SIWG_ACCOUNT_NAME)?.let { setAccountName(it) } } + .build() + val signInIntent = buildAssistedSignInIntent( + requestExtraKey = GET_SIGN_IN_INTENT_REQUEST, + serializedRequest = SafeParcelableSerializer.serializeToBytes(signInRequest), + googleSignInOptions = googleSignInOptions, + callingPackage = intent.getStringExtra(GOOGLE_ID_SIWG_CALLER_PACKAGE) ) + startActivityForResult(signInIntent, REQUEST_CODE_SIGN_IN) } override fun onProviderCreateCredentialRequest(request: ProviderCreateCredentialRequest) { diff --git a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/screenlock/ScreenLockTransportHandler.kt b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/screenlock/ScreenLockTransportHandler.kt index ec4bcf8b5..64300b707 100644 --- a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/screenlock/ScreenLockTransportHandler.kt +++ b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/screenlock/ScreenLockTransportHandler.kt @@ -26,6 +26,8 @@ import org.microg.gms.fido.core.transport.TransportHandler import org.microg.gms.fido.core.transport.TransportHandlerCallback import org.microg.gms.utils.toBase64 import java.security.Signature +import java.security.cert.Certificate +import java.security.cert.X509Certificate import java.security.interfaces.ECPublicKey import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException @@ -122,7 +124,6 @@ class ScreenLockTransportHandler(private val activity: FragmentActivity, callbac } } val (clientData, clientDataHash) = getClientDataAndHash(activity, options, callerPackage) - val aaguid = if (options.registerOptions.skipAttestation) ByteArray(16) else AAGUID val keyId = store.createKey(options.rpId, clientDataHash) val publicKey = store.getPublicKey(options.rpId, keyId) ?: throw RequestHandlingException(ErrorCode.INVALID_STATE_ERR) @@ -130,6 +131,12 @@ class ScreenLockTransportHandler(private val activity: FragmentActivity, callbac // We're ignoring the signature object as we don't need it for registration val signature = getActiveSignature(options, callerPackage, keyId) + val skipAttestation = options.registerOptions.skipAttestation + val useAndroidKey = !skipAttestation && SDK_INT >= 24 && + runCatching { store.getCertificateChain(options.rpId, keyId).hasValidLeafCertificate() }.getOrDefault(false) + val useSafetyNet = !skipAttestation && SDK_INT < 24 + val aaguid = if (useAndroidKey || useSafetyNet) AAGUID else ByteArray(16) + val (x, y) = (publicKey as ECPublicKey).w.let { it.affineX to it.affineY } val coseKey = CoseKey(EC2Algorithm.ES256, x, y, 1, 32) val credentialId = CredentialId(1, keyId, options.rpId, publicKey) @@ -137,19 +144,20 @@ class ScreenLockTransportHandler(private val activity: FragmentActivity, callbac val credentialData = getCredentialData(aaguid, credentialId, coseKey) val authenticatorData = getAuthenticatorData(options.rpId, credentialData) - val attestationObject = if (options.registerOptions.skipAttestation) { - NoneAttestationObject(authenticatorData) - } else { - try { - if (SDK_INT >= 24) { - createAndroidKeyAttestation(signature, authenticatorData, clientDataHash, options.rpId, keyId) - } else { - createSafetyNetAttestation(authenticatorData, clientDataHash) - } + val attestationObject = when { + useAndroidKey -> try { + createAndroidKeyAttestation(signature, authenticatorData, clientDataHash, options.rpId, keyId) } catch (e: Exception) { Log.w("FidoScreenLockTransport", e) NoneAttestationObject(authenticatorData) } + useSafetyNet -> try { + createSafetyNetAttestation(authenticatorData, clientDataHash) + } catch (e: Exception) { + Log.w("FidoScreenLockTransport", e) + NoneAttestationObject(authenticatorData) + } + else -> NoneAttestationObject(authenticatorData) } return AuthenticatorResponseWithUser( @@ -180,6 +188,11 @@ class ScreenLockTransportHandler(private val activity: FragmentActivity, callbac store.getCertificateChain(rpId, keyId).map { it.encoded }) } + private fun Array.hasValidLeafCertificate(): Boolean { + val leaf = firstOrNull() as? X509Certificate ?: return false + return runCatching { leaf.checkValidity() }.isSuccess + } + private suspend fun createSafetyNetAttestation( authenticatorData: AuthenticatorData, clientDataHash: ByteArray diff --git a/play-services-identity-credentials/build.gradle b/play-services-identity-credentials/build.gradle new file mode 100644 index 000000000..6f374dbff --- /dev/null +++ b/play-services-identity-credentials/build.gradle @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' + +android { + namespace "com.google.android.gms.identitycredentials" + + compileSdkVersion androidCompileSdk + buildToolsVersion "$androidBuildVersionTools" + + buildFeatures { + aidl = true + } + + defaultConfig { + versionName version + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } +} + +description = 'microG implementation of play-services-identity-credentials' + +dependencies { + api project(':play-services-base') + api project(':play-services-basement') + + annotationProcessor project(':safe-parcel-processor') +} diff --git a/play-services-identity-credentials/core/build.gradle b/play-services-identity-credentials/core/build.gradle new file mode 100644 index 000000000..30ef5927c --- /dev/null +++ b/play-services-identity-credentials/core/build.gradle @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +dependencies { + api project(':play-services-identity-credentials') + + implementation project(':play-services-base-core') + implementation project(':play-services-fido-core') + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion" +} + +android { + namespace "org.microg.gms.identitycredentials.core" + + compileSdkVersion androidCompileSdk + buildToolsVersion "$androidBuildVersionTools" + + defaultConfig { + versionName version + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + sourceSets { + main { + java.srcDirs = ['src/main/kotlin'] + } + } + + lintOptions { + disable 'MissingTranslation' + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } + + kotlinOptions { + jvmTarget = 1.8 + } +} + +description = 'microG service implementation for play-services-identity-credentials' diff --git a/play-services-identity-credentials/core/src/main/AndroidManifest.xml b/play-services-identity-credentials/core/src/main/AndroidManifest.xml new file mode 100644 index 000000000..f474e84df --- /dev/null +++ b/play-services-identity-credentials/core/src/main/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/play-services-identity-credentials/core/src/main/kotlin/org/microg/gms/identitycredentials/IdentityCredentialApiService.kt b/play-services-identity-credentials/core/src/main/kotlin/org/microg/gms/identitycredentials/IdentityCredentialApiService.kt new file mode 100644 index 000000000..15efd6b0c --- /dev/null +++ b/play-services-identity-credentials/core/src/main/kotlin/org/microg/gms/identitycredentials/IdentityCredentialApiService.kt @@ -0,0 +1,215 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.identitycredentials + +import android.app.PendingIntent +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.Log +import androidx.core.app.PendingIntentCompat +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.Feature +import com.google.android.gms.common.api.ApiMetadata +import com.google.android.gms.common.api.CommonStatusCodes +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 com.google.android.gms.identitycredentials.ClearCreationOptionsRequest +import com.google.android.gms.identitycredentials.ClearCredentialStateRequest +import com.google.android.gms.identitycredentials.ClearCredentialStateResponse +import com.google.android.gms.identitycredentials.ClearExportRequest +import com.google.android.gms.identitycredentials.ClearRegistryRequest +import com.google.android.gms.identitycredentials.CreateCredentialHandle +import com.google.android.gms.identitycredentials.CreateCredentialRequest +import com.google.android.gms.identitycredentials.CredentialInformation +import com.google.android.gms.identitycredentials.CredentialInformationRequest +import com.google.android.gms.identitycredentials.CredentialInformationResponse +import com.google.android.gms.identitycredentials.CredentialTransferCapabilities +import com.google.android.gms.identitycredentials.ExportCredentialsToDeviceSetupRequest +import com.google.android.gms.identitycredentials.GetCredentialRequest +import com.google.android.gms.identitycredentials.GetCredentialTransferCapabilitiesRequest +import com.google.android.gms.identitycredentials.ImportCredentialsForDeviceSetupRequest +import com.google.android.gms.identitycredentials.ImportCredentialsRequest +import com.google.android.gms.identitycredentials.PendingGetCredentialHandle +import com.google.android.gms.identitycredentials.RegisterCreationOptionsRequest +import com.google.android.gms.identitycredentials.RegisterExportRequest +import com.google.android.gms.identitycredentials.RegistrationRequest +import com.google.android.gms.identitycredentials.SignalCredentialStateRequest +import com.google.android.gms.identitycredentials.internal.IIdentityCredentialCallbacks +import com.google.android.gms.identitycredentials.internal.IIdentityCredentialService +import kotlinx.coroutines.launch +import org.microg.gms.BaseService +import org.microg.gms.common.AccountUtils +import org.microg.gms.common.GmsService +import org.microg.gms.fido.core.Database +import org.microg.gms.profile.Build +import org.microg.gms.profile.ProfileManager + +private const val TAG = "IdentityCredentialApi" + +private const val CHOOSER_ACTIVITY_CLASS = "org.microg.gms.auth.credentials.identity.IdentityCredentialChooserActivity" + +const val EXTRA_GET_REQUEST = "org.microg.gms.identitycredentials.EXTRA_GET_REQUEST" +const val EXTRA_CREATE_REQUEST = "org.microg.gms.identitycredentials.EXTRA_CREATE_REQUEST" +const val EXTRA_CALLING_PACKAGE = "org.microg.gms.identitycredentials.EXTRA_CALLING_PACKAGE" + +private val FEATURES = arrayOf( + Feature("GET_CREDENTIAL", 1), + Feature("CREDENTIAL_REGISTRY", 1), + Feature("CLEAR_REGISTRY", 2), + Feature("CLEAR_CREATION_OPTIONS", 1), + Feature("GET_CREDENTIAL_INFORMATION", 1), + Feature("CLEAR_CREDENTIAL_STATE", 1), + Feature("CREATE_CREDENTIAL", 3), + Feature("REGISTER_CREATION_OPTIONS", 1), + Feature("REGISTER_EXPORT", 1), + Feature("IMPORT_CREDENTIALS", 1), + Feature("SIGNAL_CREDENTIAL_STATE", 1), + Feature("CLEAR_EXPORT", 1), + Feature("IMPORT_CREDENTIALS_FOR_DEVICE_SETUP", 3), + Feature("EXPORT_CREDENTIALS_TO_DEVICE_SETUP", 3), + Feature("GET_CREDENTIAL_TRANSFER_CAPABILITIES", 3), +) + +class IdentityCredentialApiService : BaseService(TAG, GmsService.IDENTITY_CREDENTIALS) { + + override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) { + Log.d(TAG, "handleServiceRequest pkg=${request.packageName}") + val connectionInfo = ConnectionInfo() + connectionInfo.features = FEATURES + ProfileManager.ensureInitialized(this) + callback.onPostInitCompleteWithConnectionInfo( + ConnectionResult.SUCCESS, + IdentityCredentialApiServiceImpl(this, request.packageName, lifecycle).asBinder(), + connectionInfo + ) + } +} + +class IdentityCredentialApiServiceImpl( + private val context: Context, + private val clientPackageName: String, + override val lifecycle: Lifecycle, +) : IIdentityCredentialService.Stub(), LifecycleOwner { + + override fun getCredential(callback: IIdentityCredentialCallbacks, request: GetCredentialRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "getCredential pkg=$clientPackageName options=${request.credentialOptions.size} origin=${request.origin}") + callback.onGetCredential(Status.SUCCESS, PendingGetCredentialHandle(buildChooserPendingIntent(request)), ApiMetadata.SKIP) + } + + override fun createCredential(callback: IIdentityCredentialCallbacks, request: CreateCredentialRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "createCredential pkg=$clientPackageName type=${request.type} origin=${request.origin}") + callback.onCreateCredential(Status.SUCCESS, CreateCredentialHandle(buildCreateChooserPendingIntent(request), null), ApiMetadata.SKIP) + } + + override fun clearCredentialState(callback: IIdentityCredentialCallbacks, request: ClearCredentialStateRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "clearCredentialState pkg=$clientPackageName") + callback.onClearCredentialState(Status.SUCCESS, ClearCredentialStateResponse(), ApiMetadata.SKIP) + } + + override fun getCredentialInformation(callback: IIdentityCredentialCallbacks, request: CredentialInformationRequest, apiMetadata: ApiMetadata) { + val packageNames = request.packageNames.orEmpty() + Log.d(TAG, "getCredentialInformation pkg=$clientPackageName count=${packageNames.size}") + if (Build.VERSION.SDK_INT < 34) { + callback.onGetCredentialInformation(Status.SUCCESS, CredentialInformationResponse(emptyList())) + return + } + lifecycleScope.launch { + fun resolveCredentialInformation(packageName: String): CredentialInformation { + val installed = runCatching { context.packageManager.getPackageInfo(packageName, 0) }.isSuccess + if (!installed) return CredentialInformation(packageName, 0, 0, 0, 0) + val hasPasskey = runCatching { Database(context).getKnownRegistrationInfo(packageName).isNotEmpty() }.getOrDefault(false) + val hasGoogleAccount = AccountUtils.get(context).getSelectedAccount(packageName) != null + return CredentialInformation(packageName, 0, if (hasPasskey) 1 else 0, if (hasGoogleAccount) 1 else 0, 0) + } + val infos = packageNames.filterNotNull().map { resolveCredentialInformation(it) } + runCatching { callback.onGetCredentialInformation(Status.SUCCESS, CredentialInformationResponse(infos)) } + .onFailure { Log.w(TAG, "getCredentialInformation callback failed", it) } + } + } + + override fun register(callback: IIdentityCredentialCallbacks, request: RegistrationRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "register: not implemented") + callback.onRegister(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + override fun clearRegistry(callback: IIdentityCredentialCallbacks, request: ClearRegistryRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "clearRegistry: not implemented") + callback.onClearRegistry(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + override fun importCredentials(callback: IIdentityCredentialCallbacks, request: ImportCredentialsRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "importCredentials: not implemented") + callback.onImportCredentials(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + override fun registerExport(callback: IIdentityCredentialCallbacks, request: RegisterExportRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "registerExport: not implemented") + callback.onRegisterExport(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + override fun registerCreationOptions(callback: IIdentityCredentialCallbacks, request: RegisterCreationOptionsRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "registerCreationOptions: not implemented") + callback.onRegisterCreationOptions(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + override fun signalCredentialState(callback: IIdentityCredentialCallbacks, request: SignalCredentialStateRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "signalCredentialState: not implemented") + callback.onSignalCredentialState(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + override fun clearExport(callback: IIdentityCredentialCallbacks, request: ClearExportRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "clearExport: not implemented") + callback.onClearExport(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + override fun importCredentialsForDeviceSetup(callback: IIdentityCredentialCallbacks, request: ImportCredentialsForDeviceSetupRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "importCredentialsForDeviceSetup: not implemented") + callback.onImportCredentialsForDeviceSetup(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + override fun exportCredentialsToDeviceSetup(callback: IIdentityCredentialCallbacks, request: ExportCredentialsToDeviceSetupRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "exportCredentialsToDeviceSetup: not implemented") + callback.onExportCredentialsToDeviceSetup(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + override fun getCredentialTransferCapabilities(callback: IIdentityCredentialCallbacks, request: GetCredentialTransferCapabilitiesRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "getCredentialTransferCapabilities pkg=$clientPackageName") + callback.onGetCredentialTransferCapabilities(Status.SUCCESS, CredentialTransferCapabilities(Bundle.EMPTY), ApiMetadata.SKIP) + } + + override fun clearCreationOptions(callback: IIdentityCredentialCallbacks, request: ClearCreationOptionsRequest, apiMetadata: ApiMetadata) { + Log.d(TAG, "clearCreationOptions: not implemented") + callback.onClearCreationOptions(Status(CommonStatusCodes.API_NOT_CONNECTED), null, ApiMetadata.SKIP) + } + + private fun buildChooserPendingIntent(request: GetCredentialRequest): PendingIntent = + buildChooserPendingIntent(request.hashCode()) { + putExtra(EXTRA_GET_REQUEST, Bundle().apply { putParcelable(EXTRA_GET_REQUEST, request) }) + } + + private fun buildCreateChooserPendingIntent(request: CreateCredentialRequest): PendingIntent = + buildChooserPendingIntent(request.hashCode()) { + putExtra(EXTRA_CREATE_REQUEST, Bundle().apply { putParcelable(EXTRA_CREATE_REQUEST, request) }) + } + + private inline fun buildChooserPendingIntent(requestCode: Int, configure: Intent.() -> Unit): PendingIntent { + val intent = Intent().apply { + component = ComponentName(context.packageName, CHOOSER_ACTIVITY_CLASS) + putExtra(EXTRA_CALLING_PACKAGE, clientPackageName) + configure() + } + val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_ONE_SHOT + return PendingIntentCompat.getActivity(context, requestCode, intent, flags, true)!! + } + +} diff --git a/play-services-identity-credentials/src/main/AndroidManifest.xml b/play-services-identity-credentials/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/play-services-identity-credentials/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCreationOptionsRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCreationOptionsRequest.aidl new file mode 100644 index 000000000..a9069fdf4 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCreationOptionsRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ClearCreationOptionsRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCreationOptionsResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCreationOptionsResponse.aidl new file mode 100644 index 000000000..f877c7b61 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCreationOptionsResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ClearCreationOptionsResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCredentialStateRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCredentialStateRequest.aidl new file mode 100644 index 000000000..73923bbcc --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCredentialStateRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ClearCredentialStateRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCredentialStateResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCredentialStateResponse.aidl new file mode 100644 index 000000000..18cb0c495 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearCredentialStateResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ClearCredentialStateResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearExportRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearExportRequest.aidl new file mode 100644 index 000000000..b483e6fac --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearExportRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ClearExportRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearExportResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearExportResponse.aidl new file mode 100644 index 000000000..a64474f1b --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearExportResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ClearExportResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearRegistryRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearRegistryRequest.aidl new file mode 100644 index 000000000..586d1c435 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearRegistryRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ClearRegistryRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearRegistryResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearRegistryResponse.aidl new file mode 100644 index 000000000..f6ea9b8c7 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ClearRegistryResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ClearRegistryResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialHandle.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialHandle.aidl new file mode 100644 index 000000000..100d74aa1 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialHandle.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable CreateCredentialHandle; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialRequest.aidl new file mode 100644 index 000000000..3fa9e1966 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable CreateCredentialRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialResponse.aidl new file mode 100644 index 000000000..a78de7c61 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CreateCredentialResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable CreateCredentialResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialInformationRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialInformationRequest.aidl new file mode 100644 index 000000000..02180d175 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialInformationRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable CredentialInformationRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialInformationResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialInformationResponse.aidl new file mode 100644 index 000000000..7704b5cce --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialInformationResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable CredentialInformationResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialOption.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialOption.aidl new file mode 100644 index 000000000..d8efeec23 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialOption.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable CredentialOption; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialTransferCapabilities.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialTransferCapabilities.aidl new file mode 100644 index 000000000..f6ef69abb --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/CredentialTransferCapabilities.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable CredentialTransferCapabilities; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupRequest.aidl new file mode 100644 index 000000000..aa24fa68f --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ExportCredentialsToDeviceSetupRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupResponse.aidl new file mode 100644 index 000000000..7bf033ae9 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ExportCredentialsToDeviceSetupResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/GetCredentialRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/GetCredentialRequest.aidl new file mode 100644 index 000000000..595d594c1 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/GetCredentialRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable GetCredentialRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/GetCredentialTransferCapabilitiesRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/GetCredentialTransferCapabilitiesRequest.aidl new file mode 100644 index 000000000..86bb3fbfc --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/GetCredentialTransferCapabilitiesRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable GetCredentialTransferCapabilitiesRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupRequest.aidl new file mode 100644 index 000000000..8ce822b3d --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ImportCredentialsForDeviceSetupRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupResponse.aidl new file mode 100644 index 000000000..23a7832ea --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ImportCredentialsForDeviceSetupResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsRequest.aidl new file mode 100644 index 000000000..03d4a4cda --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/ImportCredentialsRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable ImportCredentialsRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/PendingGetCredentialHandle.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/PendingGetCredentialHandle.aidl new file mode 100644 index 000000000..f39b7be3a --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/PendingGetCredentialHandle.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable PendingGetCredentialHandle; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/PendingImportCredentialsHandle.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/PendingImportCredentialsHandle.aidl new file mode 100644 index 000000000..ba6c01353 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/PendingImportCredentialsHandle.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable PendingImportCredentialsHandle; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterCreationOptionsRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterCreationOptionsRequest.aidl new file mode 100644 index 000000000..c98462fe3 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterCreationOptionsRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable RegisterCreationOptionsRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterCreationOptionsResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterCreationOptionsResponse.aidl new file mode 100644 index 000000000..78a4981f5 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterCreationOptionsResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable RegisterCreationOptionsResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterExportRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterExportRequest.aidl new file mode 100644 index 000000000..404814b38 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterExportRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable RegisterExportRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterExportResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterExportResponse.aidl new file mode 100644 index 000000000..0c43c73cd --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegisterExportResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable RegisterExportResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegistrationRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegistrationRequest.aidl new file mode 100644 index 000000000..af6a88ff3 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegistrationRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable RegistrationRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegistrationResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegistrationResponse.aidl new file mode 100644 index 000000000..8af24f09e --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/RegistrationResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable RegistrationResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/SignalCredentialStateRequest.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/SignalCredentialStateRequest.aidl new file mode 100644 index 000000000..8b7fbde20 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/SignalCredentialStateRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable SignalCredentialStateRequest; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/SignalCredentialStateResponse.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/SignalCredentialStateResponse.aidl new file mode 100644 index 000000000..3195d46c7 --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/SignalCredentialStateResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +parcelable SignalCredentialStateResponse; diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/internal/IIdentityCredentialCallbacks.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/internal/IIdentityCredentialCallbacks.aidl new file mode 100644 index 000000000..69b38a5ae --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/internal/IIdentityCredentialCallbacks.aidl @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials.internal; + +import com.google.android.gms.common.api.ApiMetadata; +import com.google.android.gms.common.api.Status; +import com.google.android.gms.identitycredentials.ClearCreationOptionsResponse; +import com.google.android.gms.identitycredentials.ClearCredentialStateResponse; +import com.google.android.gms.identitycredentials.ClearExportResponse; +import com.google.android.gms.identitycredentials.ClearRegistryResponse; +import com.google.android.gms.identitycredentials.CreateCredentialHandle; +import com.google.android.gms.identitycredentials.CreateCredentialResponse; +import com.google.android.gms.identitycredentials.CredentialInformationResponse; +import com.google.android.gms.identitycredentials.CredentialTransferCapabilities; +import com.google.android.gms.identitycredentials.ExportCredentialsToDeviceSetupResponse; +import com.google.android.gms.identitycredentials.ImportCredentialsForDeviceSetupResponse; +import com.google.android.gms.identitycredentials.PendingGetCredentialHandle; +import com.google.android.gms.identitycredentials.PendingImportCredentialsHandle; +import com.google.android.gms.identitycredentials.RegisterCreationOptionsResponse; +import com.google.android.gms.identitycredentials.RegisterExportResponse; +import com.google.android.gms.identitycredentials.RegistrationResponse; +import com.google.android.gms.identitycredentials.SignalCredentialStateResponse; + +interface IIdentityCredentialCallbacks { + void onGetCredential(in Status status, in PendingGetCredentialHandle handle, in ApiMetadata apiMetadata) = 0; + void onRegister(in Status status, in RegistrationResponse response, in ApiMetadata apiMetadata) = 1; + void onClearRegistry(in Status status, in ClearRegistryResponse response, in ApiMetadata apiMetadata) = 2; + void onImportCredentials(in Status status, in PendingImportCredentialsHandle handle, in ApiMetadata apiMetadata) = 3; + void onRegisterExport(in Status status, in RegisterExportResponse response, in ApiMetadata apiMetadata) = 4; + void onCreateCredentialLegacy(in Status status, in CreateCredentialResponse response, in ApiMetadata apiMetadata) = 5; + void onCreateCredential(in Status status, in CreateCredentialHandle handle, in ApiMetadata apiMetadata) = 6; + void onRegisterCreationOptions(in Status status, in RegisterCreationOptionsResponse response, in ApiMetadata apiMetadata) = 7; + void onClearCredentialState(in Status status, in ClearCredentialStateResponse response, in ApiMetadata apiMetadata) = 8; + void onSignalCredentialState(in Status status, in SignalCredentialStateResponse response, in ApiMetadata apiMetadata) = 9; + void onClearExport(in Status status, in ClearExportResponse response, in ApiMetadata apiMetadata) = 10; + void onImportCredentialsForDeviceSetup(in Status status, in ImportCredentialsForDeviceSetupResponse response, in ApiMetadata apiMetadata) = 11; + void onExportCredentialsToDeviceSetup(in Status status, in ExportCredentialsToDeviceSetupResponse response, in ApiMetadata apiMetadata) = 12; + void onGetCredentialTransferCapabilities(in Status status, in CredentialTransferCapabilities capabilities, in ApiMetadata apiMetadata) = 13; + void onClearCreationOptions(in Status status, in ClearCreationOptionsResponse response, in ApiMetadata apiMetadata) = 14; + void onGetCredentialInformation(in Status status, in CredentialInformationResponse response) = 15; +} diff --git a/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/internal/IIdentityCredentialService.aidl b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/internal/IIdentityCredentialService.aidl new file mode 100644 index 000000000..80749deee --- /dev/null +++ b/play-services-identity-credentials/src/main/aidl/com/google/android/gms/identitycredentials/internal/IIdentityCredentialService.aidl @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials.internal; + +import com.google.android.gms.common.api.ApiMetadata; +import com.google.android.gms.identitycredentials.ClearCreationOptionsRequest; +import com.google.android.gms.identitycredentials.ClearCredentialStateRequest; +import com.google.android.gms.identitycredentials.ClearExportRequest; +import com.google.android.gms.identitycredentials.ClearRegistryRequest; +import com.google.android.gms.identitycredentials.CreateCredentialRequest; +import com.google.android.gms.identitycredentials.CredentialInformationRequest; +import com.google.android.gms.identitycredentials.ExportCredentialsToDeviceSetupRequest; +import com.google.android.gms.identitycredentials.GetCredentialRequest; +import com.google.android.gms.identitycredentials.GetCredentialTransferCapabilitiesRequest; +import com.google.android.gms.identitycredentials.ImportCredentialsForDeviceSetupRequest; +import com.google.android.gms.identitycredentials.ImportCredentialsRequest; +import com.google.android.gms.identitycredentials.RegisterCreationOptionsRequest; +import com.google.android.gms.identitycredentials.RegisterExportRequest; +import com.google.android.gms.identitycredentials.RegistrationRequest; +import com.google.android.gms.identitycredentials.SignalCredentialStateRequest; +import com.google.android.gms.identitycredentials.internal.IIdentityCredentialCallbacks; + +interface IIdentityCredentialService { + void getCredential(IIdentityCredentialCallbacks callbacks, in GetCredentialRequest request, in ApiMetadata apiMetadata) = 0; + void register(IIdentityCredentialCallbacks callbacks, in RegistrationRequest request, in ApiMetadata apiMetadata) = 1; + void clearRegistry(IIdentityCredentialCallbacks callbacks, in ClearRegistryRequest request, in ApiMetadata apiMetadata) = 2; + void importCredentials(IIdentityCredentialCallbacks callbacks, in ImportCredentialsRequest request, in ApiMetadata apiMetadata) = 3; + void registerExport(IIdentityCredentialCallbacks callbacks, in RegisterExportRequest request, in ApiMetadata apiMetadata) = 4; + void createCredential(IIdentityCredentialCallbacks callbacks, in CreateCredentialRequest request, in ApiMetadata apiMetadata) = 5; + void registerCreationOptions(IIdentityCredentialCallbacks callbacks, in RegisterCreationOptionsRequest request, in ApiMetadata apiMetadata) = 7; + void clearCredentialState(IIdentityCredentialCallbacks callbacks, in ClearCredentialStateRequest request, in ApiMetadata apiMetadata) = 8; + void signalCredentialState(IIdentityCredentialCallbacks callbacks, in SignalCredentialStateRequest request, in ApiMetadata apiMetadata) = 9; + void clearExport(IIdentityCredentialCallbacks callbacks, in ClearExportRequest request, in ApiMetadata apiMetadata) = 10; + void importCredentialsForDeviceSetup(IIdentityCredentialCallbacks callbacks, in ImportCredentialsForDeviceSetupRequest request, in ApiMetadata apiMetadata) = 11; + void exportCredentialsToDeviceSetup(IIdentityCredentialCallbacks callbacks, in ExportCredentialsToDeviceSetupRequest request, in ApiMetadata apiMetadata) = 12; + void getCredentialTransferCapabilities(IIdentityCredentialCallbacks callbacks, in GetCredentialTransferCapabilitiesRequest request, in ApiMetadata apiMetadata) = 13; + void clearCreationOptions(IIdentityCredentialCallbacks callbacks, in ClearCreationOptionsRequest request, in ApiMetadata apiMetadata) = 14; + void getCredentialInformation(IIdentityCredentialCallbacks callbacks, in CredentialInformationRequest request, in ApiMetadata apiMetadata) = 15; +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CallingAppInfoParcelable.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CallingAppInfoParcelable.java new file mode 100644 index 000000000..ba20b3a11 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CallingAppInfoParcelable.java @@ -0,0 +1,103 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * Information pertaining to the calling application. + *

+ * The {@code packageCertificates} will either return a single byte-array corresponding to the oldest available signature for pre-P devices; for P+ + * devices, it will return the full rotation history (including the signature used to sign the package) or an empty list. The empty list means the + * package was not found or the package does not have any trust-worthy signatures. + */ +public class CallingAppInfoParcelable implements Parcelable { + @NonNull + private final String packageName; + @NonNull + private final List packageCertificates; + @NonNull + private final String origin; + + public CallingAppInfoParcelable(@NonNull String packageName, @NonNull List packageCertificates, @NonNull String origin) { + this.packageName = packageName; + this.packageCertificates = packageCertificates; + this.origin = origin; + } + + @Override + public int describeContents() { + return 0; + } + + /** + * the calling origin + */ + @NonNull + public String getOrigin() { + return origin; + } + + /** + * a list of byte arrays, one for each rotated signature in raw bytes + */ + @NonNull + public List getPackageCertificates() { + return packageCertificates; + } + + /** + * the calling app package name + */ + @NonNull + public String getPackageName() { + return packageName; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(packageName); + dest.writeInt(packageCertificates.size()); + for (byte[] packageCertificate : packageCertificates) { + dest.writeInt(packageCertificate.length); + dest.writeByteArray(packageCertificate); + } + dest.writeString(origin); + } + + public static final Creator CREATOR = new Creator() { + @Override + public CallingAppInfoParcelable createFromParcel(Parcel source) { + String packageName = source.readString(); + int numPackageCertificates = source.readInt(); + if (packageName == null || numPackageCertificates < 0) { + return null; + } + List packageCertificates = new ArrayList<>(numPackageCertificates); + for (int i = 0; i < numPackageCertificates; i++) { + byte[] packageCertificate = new byte[source.readInt()]; + source.readByteArray(packageCertificate); + packageCertificates.add(packageCertificate); + } + String origin = source.readString(); + return new CallingAppInfoParcelable(packageName, packageCertificates, origin); + } + + @Override + public CallingAppInfoParcelable[] newArray(int size) { + return new CallingAppInfoParcelable[size]; + } + }; +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCreationOptionsRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCreationOptionsRequest.java new file mode 100644 index 000000000..8bc2d94e9 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCreationOptionsRequest.java @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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 java.util.List; + +/** + * A request to clear the registries stored for your app. + */ +@SafeParcelable.Class +public class ClearCreationOptionsRequest extends AbstractSafeParcelable { + + @Field(value = 1, getterName = "getDeleteAll", defaultValue = "true") + private final boolean deleteAll; + @Field(value = 2, getterName = "getClearTypedRegistryOption") + @Nullable + private final ClearTypedCreationOption clearTypedRegistryOption; + + /** + * Cosntructs a request to clear all registries for your app that was registered with the IdentityCredentials.registerCredentials API. + */ + public ClearCreationOptionsRequest() { + this(true, null); + } + + /** + * constructs an instance of {@link ClearCreationOptionsRequest} + * + * @param deleteAll whether to delete all registries for your app + * @param clearTypedRegistryOption an option to clear the registries for a given type that matches the {@code RegisterCreationOptionsRequest.type} provided during registration + */ + @Constructor + public ClearCreationOptionsRequest(@Param(1) boolean deleteAll, @Param(2) @Nullable ClearTypedCreationOption clearTypedRegistryOption) { + this.deleteAll = deleteAll; + this.clearTypedRegistryOption = clearTypedRegistryOption; + } + + /** + * whether to delete all registries for your app + */ + public boolean getDeleteAll() { + return deleteAll; + } + + /** + * an option to clear the registries for a given type that matches the RegisterCreationOptionsRequest.type provided during registration + */ + @Nullable + public ClearTypedCreationOption getClearTypedRegistryOption() { + return clearTypedRegistryOption; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearCreationOptionsRequest.class); + + /** + * A request to configure how to clear the registries for a given type. + *

+ * The order of the conditions are important. If {@code deleteAllForType} is true, then the other conditions are ignored and all the + * registries for the given type are deleted. Otherwise, the registries with the IDs provided in {@code registryIds} will be deleted. + */ + @Class + public static class ClearTypedCreationOption extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getDeleteAllForType") + private final boolean deleteAllForType; + + @Field(value = 2, getterName = "getType") + @NonNull + private final String type; + + @Field(value = 3, getterName = "getRegistryIds") + @NonNull + private final List registryIds; + + /** + * constructs an instance of {@link ClearTypedCreationOption} + * + * @param deleteAllForType whether to delete all registries for the given type + * @param type the type of registry to clear, matching the RegistrationRequest.type provided during registration + * @param registryIds the IDs of the registries for the given type to delete + */ + @Constructor + public ClearTypedCreationOption(@Param(1) boolean deleteAllForType, @NonNull @Param(2) String type, @NonNull @Param(3) List registryIds) { + this.deleteAllForType = deleteAllForType; + this.type = type; + this.registryIds = registryIds; + } + + /** + * whether to delete all registries for the given type + */ + public final boolean getDeleteAllForType() { + return this.deleteAllForType; + } + + /** + * the IDs of the registries for the given type to delete + */ + @NonNull + public final List getRegistryIds() { + return this.registryIds; + } + + /** + * the type of registry to clear, matching the RegistrationRequest.type provided during registration + */ + @NonNull + public final String getType() { + return this.type; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearTypedCreationOption.class); + } +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCreationOptionsResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCreationOptionsResponse.java new file mode 100644 index 000000000..75216c275 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCreationOptionsResponse.java @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Response of a registry deletion operation. + */ +@SafeParcelable.Class +public class ClearCreationOptionsResponse extends AbstractSafeParcelable { + @Field(value = 1, getterName = "isDeleted") + private final boolean isDeleted; + + /** + * constructs an instance of {@link ClearCreationOptionsResponse} + * @param isDeleted if true, indicates clear operation deleted some registries, otherwise indicates there was no data to delete; unexpected failures will be thrown as exceptions + */ + @Constructor + public ClearCreationOptionsResponse(@Param(1) boolean isDeleted) { + this.isDeleted = isDeleted; + } + + /** + * if true, indicates clear operation deleted some registries, otherwise indicates there was no data to delete; unexpected failures will be thrown as exceptions + */ + public final boolean isDeleted() { + return this.isDeleted; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearCreationOptionsResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCredentialStateRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCredentialStateRequest.java new file mode 100644 index 000000000..1fd1d0ee8 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCredentialStateRequest.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Data interface for clearing a user's credential state from the credential providers. + */ +@SafeParcelable.Class +public class ClearCredentialStateRequest extends AbstractSafeParcelable { + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearCredentialStateRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCredentialStateResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCredentialStateResponse.java new file mode 100644 index 000000000..4e2e7fea9 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearCredentialStateResponse.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Response of a clear credential state request. + */ +@SafeParcelable.Class +public class ClearCredentialStateResponse extends AbstractSafeParcelable { + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearCredentialStateResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearExportRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearExportRequest.java new file mode 100644 index 000000000..5ef5e698a --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearExportRequest.java @@ -0,0 +1,74 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +import java.util.List; + +/** + * A request to clear the registries stored for your app. + *

+ * The order of the conditions are important. If {@code deleteAll} is true, then the other conditions are ignored, and all the registries for your app that + * was registered with the {@link IdentityCredentialClient#registerExport} API are deleted. + */ +@SafeParcelable.Class +public class ClearExportRequest extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getDeleteAll") + private final boolean deleteAll; + + @Field(value = 2, getterName = "getRegistryIds") + @NonNull + private final List registryIds; + + /** + * constructs an instance of {@link ClearExportRequest} + * + * @param deleteAll whether to delete all export registries for your app + * @param registryIds the IDs of the registries for the given type to delete + */ + @Constructor + public ClearExportRequest(@Param(1) boolean deleteAll, @NonNull @Param(2) List registryIds) { + this.deleteAll = deleteAll; + this.registryIds = registryIds; + } + + /** + * whether to delete all export registries for your app + */ + public final boolean getDeleteAll() { + return this.deleteAll; + } + + /** + * the IDs of the registries for the given type to delete + */ + @NonNull + public final List getRegistryIds() { + return this.registryIds; + } + + @NonNull + public final ClearRegistryRequest.ClearTypedRegistryOption getClearRegistryOption() { + return new ClearRegistryRequest.ClearTypedRegistryOption(this.deleteAll, "androidx.identitycredentials.TYPE_CREDENTIALS_SYNC", false, this.registryIds); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearExportRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearExportResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearExportResponse.java new file mode 100644 index 000000000..c8b6cc417 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearExportResponse.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Bundle; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +import org.microg.gms.common.Hide; + +/** + * Response of a registry deletion operation. + */ +@SafeParcelable.Class +public class ClearExportResponse extends AbstractSafeParcelable { + @Field(value = 1, getterName = "isDeleted") + private final boolean isDeleted; + + /** + * constructs an instance of {@link ClearExportResponse} + * + * @param isDeleted if true, indicates clear operation deleted some registries, otherwise indicates there was no data to delete; unexpected failures will be thrown as exceptions + */ + @Constructor + public ClearExportResponse(@Param(1) boolean isDeleted) { + this.isDeleted = isDeleted; + } + + /** + * if true, indicates clear operation deleted some registries, otherwise indicates there was no data to delete; unexpected failures will be thrown as exceptions + */ + public final boolean isDeleted() { + return this.isDeleted; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearExportResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearRegistryRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearRegistryRequest.java new file mode 100644 index 000000000..31ab7cba7 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearRegistryRequest.java @@ -0,0 +1,155 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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 java.util.List; + +/** + * A request to clear the registries stored for your app. + *

+ * The order of the conditions are important. If {@code deleteAll} is true, then the other conditions are ignored, and all the registries for your app that + * was registered with the {@link IdentityCredentialClient#registerCredentials} API are deleted. + */ +@SafeParcelable.Class +public class ClearRegistryRequest extends AbstractSafeParcelable { + + @Field(value = 1, getterName = "getDeleteAll", defaultValue = "true") + private final boolean deleteAll; + + @Field(value = 2, getterName = "getClearTypedRegistryOption") + @Nullable + private final ClearTypedRegistryOption clearTypedRegistryOption; + + /** + * Cosntructs a request to clear all registries for your app that was registered with the IdentityCredentials.registerCredentials API. + */ + public ClearRegistryRequest() { + this(true, null); + } + + /** + * constructs an instance of {@link ClearRegistryRequest} + * + * @param deleteAll whether to delete all registries for your app + * @param clearTypedRegistryOption an option to clear the registries for a given type that matches the RegistrationRequest.type provided during registration + */ + @Constructor + public ClearRegistryRequest(@Param(1) boolean deleteAll, @Param(2) @Nullable ClearTypedRegistryOption clearTypedRegistryOption) { + this.deleteAll = deleteAll; + this.clearTypedRegistryOption = clearTypedRegistryOption; + } + + /** + * whether to delete all registries for your app + */ + public final boolean getDeleteAll() { + return this.deleteAll; + } + + /** + * an option to clear the registries for a given type that matches the RegistrationRequest.type provided during registration + */ + @Nullable + public final ClearTypedRegistryOption getClearTypedRegistryOption() { + return this.clearTypedRegistryOption; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearRegistryRequest.class); + + /** + * A request to configure how to clear the registries for a given type. + *

+ * The order of the conditions are important. If {@code deleteAllForType} is true, then the other conditions are ignored and all the registries for the + * given type are deleted. Otherwise, if {@code deleteIdlessRegistry} is true, then the registry with an empty ID is deleted; at the same time, the + * registries with the IDs provided in {@code registryIds} will also be deleted. + */ + public static class ClearTypedRegistryOption extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getDeleteAllForType") + private final boolean deleteAllForType; + + @Field(value = 2, getterName = "getType") + @NonNull + private final String type; + + @Field(value = 3, getterName = "getDeleteIdlessRegistry") + private final boolean deleteIdlessRegistry; + + @Field(value = 4, getterName = "getRegistryIds") + @NonNull + private final List registryIds; + + /** + * constructs an instance of {@link ClearTypedRegistryOption} + * + * @param deleteAllForType whether to delete all registries for the given type + * @param type the type of registry to clear, matching the RegistrationRequest.type provided during registration + * @param deleteIdlessRegistry whether to delete the registry for the given type that was registered without an ID provided; in other words, the registry that was registered without providing RegistrationRequest.id + * @param registryIds the IDs of the registries for the given type to delete + */ + @Constructor + public ClearTypedRegistryOption(@Param(1) boolean deleteAllForType, @NonNull @Param(2) String type, @Param(3) boolean deleteIdlessRegistry, @NonNull @Param(4) List registryIds) { + this.deleteAllForType = deleteAllForType; + this.type = type; + this.deleteIdlessRegistry = deleteIdlessRegistry; + this.registryIds = registryIds; + } + + /** + * whether to delete all registries for the given type + */ + public final boolean getDeleteAllForType() { + return this.deleteAllForType; + } + + /** + * whether to delete the registry for the given type that was registered without an ID provided; in other words, the registry that was registered without providing RegistrationRequest.id + */ + public final boolean getDeleteIdlessRegistry() { + return this.deleteIdlessRegistry; + } + + /** + * the IDs of the registries for the given type to delete + */ + @NonNull + public final List getRegistryIds() { + return this.registryIds; + } + + /** + * the type of registry to clear, matching the RegistrationRequest.type provided during registration + */ + @NonNull + public final String getType() { + return this.type; + } + + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearTypedRegistryOption.class); + } +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearRegistryResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearRegistryResponse.java new file mode 100644 index 000000000..9b40e0072 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ClearRegistryResponse.java @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Response of a registry deletion operation. + */ +@SafeParcelable.Class +public class ClearRegistryResponse extends AbstractSafeParcelable { + @Field(value = 1, getterName = "isDeleted") + private final boolean isDeleted; + + /** + * constructs an instance of {@link ClearRegistryResponse} + * + * @param isDeleted if true, indicates clear operation deleted some registries, otherwise indicates there was no data to delete; unexpected failures will be thrown as exceptions + */ + @Constructor + public ClearRegistryResponse(@Param(1) boolean isDeleted) { + this.isDeleted = isDeleted; + } + + /** + * if true, indicates clear operation deleted some registries, otherwise indicates there was no data to delete; unexpected failures will be thrown as exceptions + */ + public final boolean isDeleted() { + return this.isDeleted; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ClearRegistryResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialHandle.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialHandle.java new file mode 100644 index 000000000..2ad729dd2 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialHandle.java @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.app.PendingIntent; +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; + +/** + * Returns a response for the {@link IdentityCredentialClient#createCredential} API that can be used to launch the credential selector UIs to + * finalize on a credential of the user's choice that can be used for app sign-in, or the actual credential response itself if no UI is needed. + */ +@SafeParcelable.Class +public class CreateCredentialHandle extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getPendingIntent") + @Nullable + private final PendingIntent pendingIntent; + @Field(value = 2, getterName = "getCreateCredentialResponse") + @Nullable + private final CreateCredentialResponse createCredentialResponse; + + /** + * constructs an instance of {@link CreateCredentialHandle} + * + * @param pendingIntent the {@link PendingIntent} to launch the credential selector UI + * @param createCredentialResponse the {@link CreateCredentialResponse} if no UI is needed + */ + @Constructor + public CreateCredentialHandle(@Param(1) @Nullable PendingIntent pendingIntent, @Param(2) @Nullable CreateCredentialResponse createCredentialResponse) { + if (pendingIntent == null && createCredentialResponse == null) { + throw new IllegalArgumentException("pendingIntent or createCredentialResponse must be specified."); + } + this.pendingIntent = pendingIntent; + this.createCredentialResponse = createCredentialResponse; + } + + /** + * the {@link CreateCredentialResponse} if no UI is needed + */ + @Nullable + public CreateCredentialResponse getCreateCredentialResponse() { + return createCredentialResponse; + } + + /** + * the {@link PendingIntent} to launch the credential selector UI + */ + @Nullable + public PendingIntent getPendingIntent() { + return pendingIntent; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CreateCredentialHandle.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialRequest.java new file mode 100644 index 000000000..5fe98f47c --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialRequest.java @@ -0,0 +1,112 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.ResultReceiver; + +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; + +/** + * Data interface for creating or saving a user credential. + */ +@SafeParcelable.Class +public class CreateCredentialRequest extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getType") + @NonNull + private final String type; + @Field(value = 2, getterName = "getCredentialData") + @NonNull + private final Bundle credentialData; + @Field(value = 3, getterName = "getCandidateQueryData") + @NonNull + private final Bundle candidateQueryData; + @Field(value = 4, getterName = "getOrigin") + @Nullable + private final String origin; + @Field(value = 5, getterName = "getRequestJson") + @Nullable + private final String requestJson; + @Field(value = 6, getterName = "getResultReceiver") + @Nullable + private final ResultReceiver resultReceiver; + + /** + * constructs an instance of {@link CreateCredentialRequest} + * + * @param type the type of the credential to be created or saved + * @param credentialData the complete request data in {@link Bundle} format, consisting of all the data that will be sent to the provider during the final credential creation stage + * @param candidateQueryData the partial request data in {@link Bundle} format, that will be sent to the provider during the initial candidate query stage, which will not contain sensitive user information + * @param origin the origin of the request, only settable by a browser + */ + @Constructor + public CreateCredentialRequest(@Param(1) @NonNull String type, @Param(2) @NonNull Bundle credentialData, @Param(3) @NonNull Bundle candidateQueryData, @Param(4) @Nullable String origin, @Param(5) @Nullable String requestJson, @Param(6) @Nullable ResultReceiver resultReceiver) { + this.type = type; + this.credentialData = credentialData; + this.candidateQueryData = candidateQueryData; + this.origin = origin; + this.requestJson = requestJson; + this.resultReceiver = resultReceiver; + } + + /** + * the partial request data in {@link Bundle} format, that will be sent to the provider during the initial candidate query stage, which will not contain sensitive user information + */ + @NonNull + public Bundle getCandidateQueryData() { + return candidateQueryData; + } + + /** + * the complete request data in {@link Bundle} format, consisting of all the data that will be sent to the provider during the final credential creation stage + */ + @NonNull + public Bundle getCredentialData() { + return credentialData; + } + + /** + * the origin of the request, only settable by a browser + */ + @Nullable + public String getOrigin() { + return origin; + } + + @Nullable + public String getRequestJson() { + return requestJson; + } + + @Nullable + public ResultReceiver getResultReceiver() { + return resultReceiver; + } + + /** + * the type of the credential to be created or saved + */ + @NonNull + public String getType() { + return type; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CreateCredentialRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialResponse.java new file mode 100644 index 000000000..1ec988a8b --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CreateCredentialResponse.java @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Bundle; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +/** + * Data interface for the response of creating or saving a user credential. + */ +@SafeParcelable.Class +public class CreateCredentialResponse extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getType") + @NonNull + public final String type; + @Field(value = 2, getterName = "getData") + @NonNull + public final Bundle data; + + /** + * constructs an instance of {@link CreateCredentialResponse} + */ + @Constructor + public CreateCredentialResponse(@Param(1) @NonNull String type, @Param(2) @NonNull Bundle data) { + this.type = type; + this.data = data; + } + + /** + * the data of the credential that was created or saved, in the form of a {@link Bundle} + */ + @NonNull + public Bundle getData() { + return data; + } + + /** + * the type of the credential that was created or saved + */ + @NonNull + public String getType() { + return type; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CreateCredentialResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/Credential.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/Credential.java new file mode 100644 index 000000000..1ee9417fc --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/Credential.java @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Bundle; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +/** + * Represents a user credential that can be used to authenticate to your app. + */ +@SafeParcelable.Class +public class Credential extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getType") + @NonNull + public final String type; + @Field(value = 2, getterName = "getData") + @NonNull + public final Bundle data; + + /** + * constructs an instance of Credential + * @param type the type of the credential + * @param data the data associated with the credential + */ + @Constructor + public Credential(@Param(1) @NonNull String type, @Param(2) @NonNull Bundle data) { + this.type = type; + this.data = data; + } + + /** + * the data associated with the credential + */ + @NonNull + public Bundle getData() { + return data; + } + + /** + * the type of the credential + */ + @NonNull + public String getType() { + return type; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(Credential.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformation.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformation.java new file mode 100644 index 000000000..538f40d8c --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformation.java @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +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; + +@SafeParcelable.Class +@Hide +public class CredentialInformation extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getPackageName") + @Nullable + private final String packageName; + @Field(value = 2, getterName = "getNumPasswordCredentials") + private final int numPasswordCredentials; + @Field(value = 3, getterName = "getNumPasskeyCredentials") + private final int numPasskeyCredentials; + @Field(value = 4, getterName = "getNumGoogleIdCredentials") + private final int numGoogleIdCredentials; + @Field(value = 5, getterName = "getNumCustomCredentials") + private final int numCustomCredentials; + + @Constructor + public CredentialInformation(@Param(1) @Nullable String packageName, @Param(2) int numPasswordCredentials, @Param(3) int numPasskeyCredentials, @Param(4) int numGoogleIdCredentials, @Param(5) int numCustomCredentials) { + this.packageName = packageName; + this.numPasswordCredentials = numPasswordCredentials; + this.numPasskeyCredentials = numPasskeyCredentials; + this.numGoogleIdCredentials = numGoogleIdCredentials; + this.numCustomCredentials = numCustomCredentials; + } + + @Nullable + public String getPackageName() { + return packageName; + } + + public int getNumPasswordCredentials() { + return numPasswordCredentials; + } + + public int getNumPasskeyCredentials() { + return numPasskeyCredentials; + } + + public int getNumGoogleIdCredentials() { + return numGoogleIdCredentials; + } + + public int getNumCustomCredentials() { + return numCustomCredentials; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CredentialInformation.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformationRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformationRequest.java new file mode 100644 index 000000000..c80ef8741 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformationRequest.java @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +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; + +@SafeParcelable.Class +@Hide +public class CredentialInformationRequest extends AbstractSafeParcelable { + @Field(1) + @Nullable + public final List packageNames; + + @Constructor + public CredentialInformationRequest(@Param(1) @Nullable List packageNames) { + this.packageNames = packageNames; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CredentialInformationRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformationResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformationResponse.java new file mode 100644 index 000000000..106b7ba19 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialInformationResponse.java @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.identitycredentials; + +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; + +@SafeParcelable.Class +@Hide +public class CredentialInformationResponse extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getCredentialInformationList") + @Nullable + private final List credentialInformationList; + + @Constructor + public CredentialInformationResponse(@Param(1) @Nullable List credentialInformationList) { + this.credentialInformationList = credentialInformationList; + } + + @Nullable + public List getCredentialInformationList() { + return credentialInformationList; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CredentialInformationResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialOption.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialOption.java new file mode 100644 index 000000000..d452cfe1c --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialOption.java @@ -0,0 +1,115 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Bundle; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +import org.microg.gms.common.Hide; + +/** + * Base class for getting a specific type of credentials. + *

+ * {@link GetCredentialRequest} will be composed of a list of {@link CredentialOption} subclasses to indicate the specific credential types and + * configurations that your app accepts. + */ +@SafeParcelable.Class +public class CredentialOption extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getType") + private final String type; + @Field(value = 2, getterName = "getCredentialRetrievalData") + private final Bundle credentialRetrievalData; + @Field(value = 3, getterName = "getCandidateQueryData") + private final Bundle candidateQueryData; + @Field(value = 4, getterName = "getRequestMatcher") + private final String requestMatcher; + @Field(value = 5, getterName = "getRequestType") + @Deprecated + private final String requestType; + @Field(value = 6, getterName = "getProtocolType") + @Deprecated + private final String protocolType; + + /** + * constructs an instance of {@link CredentialOption} + * + * @param type the type of the credential to be requested + * @param credentialRetrievalData the retrieval data in {@link Bundle} format + * @param candidateQueryData the partial request data in the {@link Bundle} format that will be sent to the provider during the initial candidate query stage, which will not contain sensitive user information + * @param requestMatcher the criteria used to filter the request (for instance, age 21) + * @param requestType deprecated + * @param protocolType deprecated + */ + @Constructor + public CredentialOption(@Param(1) String type, @Param(2) Bundle credentialRetrievalData, @Param(3) Bundle candidateQueryData, @Param(4) String requestMatcher, @Deprecated @Param(5) String requestType, @Deprecated @Param(6) String protocolType) { + this.type = type; + this.credentialRetrievalData = credentialRetrievalData; + this.candidateQueryData = candidateQueryData; + this.requestMatcher = requestMatcher; + this.requestType = requestType; + this.protocolType = protocolType; + } + + /** + * the partial request data in the {@link Bundle} format that will be sent to the provider during the initial candidate query stage, which will not contain sensitive user information + */ + public Bundle getCandidateQueryData() { + return candidateQueryData; + } + + /** + * the retrieval data in {@link Bundle} format + */ + public Bundle getCredentialRetrievalData() { + return credentialRetrievalData; + } + + /** + * @deprecated + */ + @Deprecated + public String getProtocolType() { + return protocolType; + } + + /** + * the criteria used to filter the request (for instance, age 21) + */ + public String getRequestMatcher() { + return requestMatcher; + } + + /** + * @deprecated + */ + @Deprecated + public String getRequestType() { + return requestType; + } + + /** + * the type of the credential to be requested + */ + public String getType() { + return type; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CredentialOption.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialTransferCapabilities.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialTransferCapabilities.java new file mode 100644 index 000000000..7b11ff432 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/CredentialTransferCapabilities.java @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * The state of the primary provider's credentials + */ +@SafeParcelable.Class +public class CredentialTransferCapabilities extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getResponseBundle") + private final Bundle responseBundle; + + @Constructor + public CredentialTransferCapabilities(@Param(1) Bundle responseBundle) { + this.responseBundle = responseBundle; + } + + /** + * Returns the number of requested credentials. + */ + @Nullable + public final Integer getNumCustomCredentials(@NonNull String key) { + if (this.responseBundle.containsKey(key)) { + return this.responseBundle.getInt(key); + } + return null; + } + + /** + * Returns the number of passkeys. + */ + @Nullable + public final Integer getNumPasskeys() { + if (this.responseBundle.containsKey("NUM_PASSKEYS")) { + return this.responseBundle.getInt("NUM_PASSKEYS"); + } + return null; + } + + /** + * Returns the number of passwords. + */ + @Nullable + public final Integer getNumPasswords() { + if (this.responseBundle.containsKey("NUM_PASSWORDS")) { + return this.responseBundle.getInt("NUM_PASSWORDS"); + } + return null; + } + + @Nullable + public final CallingAppInfoParcelable getProviderAppInfo() { + return (CallingAppInfoParcelable) this.responseBundle.getParcelable("PROVIDER_APP_INFO"); + } + + /** + * the bundle containing the credential transfer capabilities. + */ + public Bundle getResponseBundle() { + return responseBundle; + } + + /** + * Returns the total number of credentials. + */ + @Nullable + public final Integer getTotalNumCredentials() { + if (this.responseBundle.containsKey("TOTAL_NUM_CREDENTIALS")) { + return this.responseBundle.getInt("TOTAL_NUM_CREDENTIALS"); + } + return null; + } + + /** + * Returns the total size of credentials in bytes. + */ + @Nullable + public final Long getTotalSizeBytes() { + if (this.responseBundle.containsKey("TOTAL_SIZE_BYTES")) { + return this.responseBundle.getLong("TOTAL_SIZE_BYTES"); + } + return null; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CredentialTransferCapabilities.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupRequest.java new file mode 100644 index 000000000..b6fbf5b34 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupRequest.java @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +/** + * Request for exporting credentials to primary credential provider. + */ +@SafeParcelable.Class +public class ExportCredentialsToDeviceSetupRequest extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getUri") + @NonNull + private final Uri uri; + @Field(value = 2, getterName = "getRequestData") + @NonNull + private final Bundle requestData; + + /** + * @param uri the file URI responsible for the export transport. The export provider will write the credentials here. + * @param requestData the request bundle + */ + @Constructor + public ExportCredentialsToDeviceSetupRequest(@NonNull @Param(1) Uri uri, @NonNull @Param(2) Bundle requestData) { + this.uri = uri; + this.requestData = requestData; + } + + /** + * the request bundle + */ + @NonNull + public Bundle getRequestData() { + return requestData; + } + + /** + * the file URI responsible for the export transport. The export provider will write the credentials here. + */ + @NonNull + public Uri getUri() { + return uri; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ExportCredentialsToDeviceSetupRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupResponse.java new file mode 100644 index 000000000..599195f1d --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ExportCredentialsToDeviceSetupResponse.java @@ -0,0 +1,105 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Response for exporting the credentials to the primary provider + */ +@SafeParcelable.Class +public class ExportCredentialsToDeviceSetupResponse extends AbstractSafeParcelable { + @Field(1) + @NonNull + public final Bundle responseBundle; + + /** + * @param responseBundle the bundle containing response extras. + */ + @Constructor + public ExportCredentialsToDeviceSetupResponse(@NonNull @Param(1) Bundle responseBundle) { + this.responseBundle = responseBundle; + } + + /** + * Returns the number of credentials failed to stored. + * + * @deprecated + */ + @Deprecated + @Nullable + public final Integer getNumFailure() { + if (this.responseBundle.containsKey("NUM_FAILURE")) { + return this.responseBundle.getInt("NUM_FAILURE"); + } + return null; + } + + /** + * Returns the number of credentials that were ignored by the provider. + * + * @deprecated + */ + @Deprecated + @Nullable + public final Integer getNumIgnored() { + if (this.responseBundle.containsKey("NUM_IGNORED")) { + return this.responseBundle.getInt("NUM_IGNORED"); + } + return null; + } + + /** + * Returns the number of credentials successfully stored. + * + * @deprecated + */ + @Deprecated + @Nullable + public final Integer getNumSuccess() { + if (this.responseBundle.containsKey("NUM_SUCCESS")) { + return this.responseBundle.getInt("NUM_SUCCESS"); + } + return null; + } + + /** + * Returns the provider app info. + * + * @deprecated + */ + @Deprecated + @Nullable + public final CallingAppInfoParcelable getProviderAppInfo() { + return this.responseBundle.getParcelable("PROVIDER_APP_INFO"); + } + + /** + * the bundle containing response extras. + */ + @NonNull + public Bundle getResponseBundle() { + return responseBundle; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ExportCredentialsToDeviceSetupResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/GetCredentialRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/GetCredentialRequest.java new file mode 100644 index 000000000..f7fa99215 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/GetCredentialRequest.java @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.ResultReceiver; + +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 java.util.List; + +/** + * Data interface for retrieving a user credential. + */ +@SafeParcelable.Class +public class GetCredentialRequest extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getCredentialOptions") + @NonNull + private final List credentialOptions; + @Field(value = 2, getterName = "getData") + @NonNull + private final Bundle data; + @Field(value = 3, getterName = "getOrigin") + @Nullable + private final String origin; + @Field(value = 4, getterName = "getResultReceiver") + @Deprecated + @NonNull + private final ResultReceiver resultReceiver; + + /** + * constructs an instance of {@link GetCredentialRequest} + * + * @param credentialOptions the list of credential options + * @param data the additional data to be used for retrieving the credential + * @param origin the origin of the request, only settable by a browser + * @param resultReceiver deprecated + */ + @Constructor + public GetCredentialRequest(@NonNull @Param(1) List credentialOptions, @NonNull @Param(2) Bundle data, @Nullable @Param(3) String origin, @Deprecated @NonNull @Param(4) ResultReceiver resultReceiver) { + this.credentialOptions = credentialOptions; + this.data = data; + this.origin = origin; + this.resultReceiver = resultReceiver; + } + + /** + * the list of credential options + */ + @NonNull + public List getCredentialOptions() { + return credentialOptions; + } + + /** + * the additional data to be used for retrieving the credential + */ + @NonNull + public Bundle getData() { + return data; + } + + /** + * the origin of the request, only settable by a browser + */ + @Nullable + public String getOrigin() { + return origin; + } + + /** + * @deprecated + */ + @Deprecated + @NonNull + public ResultReceiver getResultReceiver() { + return resultReceiver; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GetCredentialRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/GetCredentialTransferCapabilitiesRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/GetCredentialTransferCapabilitiesRequest.java new file mode 100644 index 000000000..f476fc59a --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/GetCredentialTransferCapabilitiesRequest.java @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Bundle; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +/** + * Request for fetching the state of the credentials in the primary provider + */ +@SafeParcelable.Class +public class GetCredentialTransferCapabilitiesRequest extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getRequestData") + private final Bundle requestData; + + /** + * @param requestData the request bundle + */ + @Constructor + public GetCredentialTransferCapabilitiesRequest(@Param(1) Bundle requestData) { + this.requestData = requestData; + } + + /** + * the request bundle + */ + public Bundle getRequestData() { + return requestData; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GetCredentialTransferCapabilitiesRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/IdentityCredentialClient.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/IdentityCredentialClient.java new file mode 100644 index 000000000..794775d1e --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/IdentityCredentialClient.java @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.HasApiKey; +import com.google.android.gms.tasks.Task; + +/** + * A client for the Identity Credentials API. + */ +public interface IdentityCredentialClient extends HasApiKey { + /** + * Returns a {@link Task} which asynchronously generates a {@link ClearCreationOptionsResponse} on success or throws an {@code ApiException} on failure, + * when attempting to clear from the creation option registry that should match one registered with {@link IdentityCredentialClient#registerCreationOptions}. + * + * @param request informs the type of operation + */ + @NonNull + Task clearCreationOptions(@NonNull ClearCreationOptionsRequest request); + + /** + * Returns a {@link Task} which asynchronously generates a {@link ClearCredentialStateResponse} on success or throws an {@code ApiException} on failure, + * when attempting to clear credential state. + * + * @param request specifies the clear credential state request + */ + @NonNull + Task clearCredentialState(@NonNull ClearCredentialStateRequest request); + + /** + * Returns a {@link Task} which asynchronously generates a {@link ClearExportResponse} on success or throws an {@code ApiException} on failure, when + * attempting to clear from the registry. + * + * @param request informs the type of operation + */ + @NonNull + Task clearExport(@NonNull ClearExportRequest request); + + /** + * Returns a {@link Task} which asynchronously generates a {@link ClearRegistryResponse} on success or throws an {@code ApiException} on failure, when attempting to clear from the registry. + * + * @param request informs the type of operation + */ + @NonNull + Task clearRegistry(@NonNull ClearRegistryRequest request); + + /** + * Returns a {@link Task} which asynchronously generates a {@link CreateCredentialHandle} on success or throws an {@code ApiException} on failure. + * + * @param request containing parameters of the credential to be created + */ + @NonNull + Task createCredential(@NonNull CreateCredentialRequest request); + + /** + * Returns a {@link Task} which asynchronously generates a {@link PendingGetCredentialHandle} on success or throws an {@code ApiException} on failure. + * + * @param request the request for getting the credential + */ + @NonNull + Task getCredential(@NonNull GetCredentialRequest request); + + /** + * Returns a {@link Task} which asynchronously generates a {@link PendingImportCredentialsHandle} on success or throws an {@code ApiException} on failure. + * + * @param request the information needed to import credentials from another provider + */ + @NonNull + Task importCredentials(@NonNull ImportCredentialsRequest request); + + /** + * Register the creation options that may serve a {@link IdentityCredentialClient#createCredential} transaction. + *

+ * Returns a {@link Task} which asynchronously generates a {@link RegisterCreationOptionsResponse} on success or throws an {@code ApiException} on + * failure, when attempting to write to the registry. + * + * @param request specifies the credential information being written to the registry + */ + @NonNull + Task registerCreationOptions(@NonNull RegisterCreationOptionsRequest request); + + /** + * RRegister the credential options that may serve a {@link IdentityCredentialClient#getCredential} transaction. + *

+ * Returns a {@link Task} which asynchronously generates a {@link RegistrationResponse} on success or throws an {@code ApiException} on failure, when + * attempting to write to the registry. + * + * @param request specifies the credential information being written to the registry + */ + @NonNull + Task registerCredentials(@NonNull RegistrationRequest request); + + /** + * Returns a {@link Task} which asynchronously generates a {@link RegisterExportResponse} on success or throws an {@code ApiException} on failure, when + * attempting to write to the registry. + * + * @param request specifies the information being written to the registry + */ + @NonNull + Task registerExport(@NonNull RegisterExportRequest request); + + /** + * Returns a {@link Task} which asynchronously generates a {@link SignalCredentialStateResponse} on success or throws an {@code ApiException} on failure, + * when attempting to signal providers with credential state. + * + * @param request specifies the signal credential state request + */ + @NonNull + Task signalCredentialState(@NonNull SignalCredentialStateRequest request); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/IdentityCredentialManager.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/IdentityCredentialManager.java new file mode 100644 index 000000000..33832bb67 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/IdentityCredentialManager.java @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.app.Activity; +import android.content.Context; +import androidx.annotation.NonNull; +import org.microg.gms.identitycredentials.IdentityCredentialClientImpl; + +/** + * Entry point for Identity Credential API. + */ +public final class IdentityCredentialManager { + /** + * Creates a new instance of {@link IdentityCredentialClient}. + * + * @param activity the activity that is using this client. + */ + @NonNull + public static IdentityCredentialClient getClient(@NonNull Activity activity) { + return new IdentityCredentialClientImpl(activity); + } + + /** + * Creates a new instance of {@link IdentityCredentialClient}. + * + * @param context the context that is using this client. + */ + @NonNull + public static IdentityCredentialClient getClient(@NonNull Context context) { + return new IdentityCredentialClientImpl(context); + } + + private IdentityCredentialManager() { + } +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupRequest.java new file mode 100644 index 000000000..21d73aea4 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupRequest.java @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +/** + * Request for importing credentials from primary credential provider. + */ +@SafeParcelable.Class +public class ImportCredentialsForDeviceSetupRequest extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getRequestJson") + @NonNull + private final String requestJson; + @Field(value = 2, getterName = "getUri") + @NonNull + private final Uri uri; + @Field(value = 3, getterName = "getRequestData") + @NonNull + private final Bundle requestData; + + /** + * @param requestJson the request in JSON format, based on the CXF prototcol + * @param uri the file URI responsible for the credential transport. The importing provider will read the credentials from here. + * @param requestData the request bundle + */ + @Constructor + public ImportCredentialsForDeviceSetupRequest(@NonNull @Param(1) String requestJson, @NonNull @Param(2) Uri uri, @NonNull @Param(3) Bundle requestData) { + this.requestJson = requestJson; + this.uri = uri; + this.requestData = requestData; + } + + /** + * the request bundle + */ + @NonNull + public Bundle getRequestData() { + return requestData; + } + + /** + * the request in JSON format, based on the CXF prototcol + */ + @NonNull + public String getRequestJson() { + return requestJson; + } + + /** + * the file URI responsible for the credential transport. The importing provider will read the credentials from here. + */ + @NonNull + public Uri getUri() { + return uri; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ImportCredentialsForDeviceSetupRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupResponse.java new file mode 100644 index 000000000..cb76056d2 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsForDeviceSetupResponse.java @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Response for importing the credentials from the primary provider + */ +@SafeParcelable.Class +public class ImportCredentialsForDeviceSetupResponse extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getResponseBundle") + @NonNull + private final Bundle responseBundle; + + /** + * @param responseBundle the bundle containing response extras. + */ + @Constructor + public ImportCredentialsForDeviceSetupResponse(@NonNull @Param(1) Bundle responseBundle) { + this.responseBundle = responseBundle; + } + + /** + * the bundle containing response extras. + */ + @NonNull + public Bundle getResponseBundle() { + return responseBundle; + } + + /** + * Returns the provider app info. + */ + @Nullable + public final CallingAppInfoParcelable getProviderAppInfo() { + return this.responseBundle.getParcelable("PROVIDER_APP_INFO"); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ImportCredentialsForDeviceSetupResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsRequest.java new file mode 100644 index 000000000..a3b925315 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/ImportCredentialsRequest.java @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.net.Uri; +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; + +/** + * Request for importing credentials from another credential provider. + */ +@SafeParcelable.Class +public class ImportCredentialsRequest extends AbstractSafeParcelable { + @NonNull + public static final String REQUEST_TYPE = "androidx.identitycredentials.TYPE_CREDENTIALS_SYNC"; + @Field(value = 1, getterName = "getRequestJson") + @NonNull + private final String requestJson; + @Field(value = 2, getterName = "getUri") + @NonNull + private final Uri uri; + + /** + * @param requestJson the request in JSON format, based on the CXF prototcol + * @param uri the file URI responsible for the export transport + */ + @Constructor + public ImportCredentialsRequest(@NonNull @Param(1) String requestJson, @NonNull @Param(2) Uri uri) { + this.requestJson = requestJson; + this.uri = uri; + } + + /** + * the request in JSON format, based on the CXF prototcol + */ + @NonNull + public final String getRequestJson() { + return this.requestJson; + } + + /** + * the file URI responsible for the export transport + */ + @NonNull + public final Uri getUri() { + return this.uri; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ImportCredentialsRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/PendingGetCredentialHandle.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/PendingGetCredentialHandle.java new file mode 100644 index 000000000..2145fd0c6 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/PendingGetCredentialHandle.java @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.app.PendingIntent; +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; + +/** + * Returns a response for the {@link IdentityCredentialClient#getCredential} API that can be used to launch the credential selector UIs to + * finalize on a credential of the user's choice that can be used for app sign-in. + */ +@SafeParcelable.Class +public class PendingGetCredentialHandle extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getPendingIntent") + @NonNull + private final PendingIntent pendingIntent; + + /** + * constructs an instance of {@link PendingGetCredentialHandle} + * + * @param pendingIntent the {@link PendingIntent} to launch the credential selector UI + */ + @Constructor + public PendingGetCredentialHandle(@NonNull @Param(1) PendingIntent pendingIntent) { + this.pendingIntent = pendingIntent; + } + + /** + * the {@link PendingIntent} to launch the credential selector UI + */ + @NonNull + public PendingIntent getPendingIntent() { + return pendingIntent; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(PendingGetCredentialHandle.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/PendingImportCredentialsHandle.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/PendingImportCredentialsHandle.java new file mode 100644 index 000000000..69d68f58f --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/PendingImportCredentialsHandle.java @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.app.PendingIntent; +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; + +/** + * Response to {@link IdentityCredentialClient#importCredentials} API, containing a {@link PendingIntent} that can be used to launch a selector + * that allows the user to select a credential provider to import credentials from + */ +@SafeParcelable.Class +public class PendingImportCredentialsHandle extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getPendingIntent") + @NonNull + private final PendingIntent pendingIntent; + + /** + * @param pendingIntent the intent that launches the selector UI + */ + @Constructor + public PendingImportCredentialsHandle(@NonNull @Param(1) PendingIntent pendingIntent) { + this.pendingIntent = pendingIntent; + } + + /** + * the intent that launches the selector UI + */ + @NonNull + public PendingIntent getPendingIntent() { + return pendingIntent; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(PendingImportCredentialsHandle.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterCreationOptionsRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterCreationOptionsRequest.java new file mode 100644 index 000000000..996ef4cc1 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterCreationOptionsRequest.java @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * A registration request to store provision / creation candidates' metadata and matcher logic. + */ +@SafeParcelable.Class +public class RegisterCreationOptionsRequest extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getCreateOptions") + @NonNull + private final byte[] createOptions; + + @Field(value = 2, getterName = "getMatcher") + @NonNull + private final byte[] matcher; + + @Field(value = 3, getterName = "getType") + @NonNull + private final String type; + + @Field(value = 4, getterName = "getId") + @NonNull + private final String id; + + @Field(value = 5, getterName = "getFulfillmentActionName", defaultValue = "") + @NonNull + private final String fulfillmentActionName; + + /** + * constructs an instance of {@link RegisterCreationOptionsRequest} + * + * @param createOptions the creation candidates data used for display and matching purpose, as a ByteArray blob + * @param matcher the matcher for the credential info, as a ByteArray blob + * @param type the type of the creation options registered, matching the {@code CreateCredentialRequest.type} that this registry can handle + * @param id the id of the given registry data, so as not to overwrite existing data of different id + * @param fulfillmentActionName optionally specify a different intent action to be used for launching the fulfillment activity when one of the registered credentials is selected by the user; otherwise, the default action {@code "androidx.credentials.registry.provider.action.CREATE_CREDENTIAL"} will be used + */ + @Constructor + public RegisterCreationOptionsRequest(@NonNull @Param(1) byte[] createOptions, @NonNull @Param(2) byte[] matcher, @NonNull @Param(3) String type, @NonNull @Param(4) String id, @NonNull @Param(5) String fulfillmentActionName) { + this.createOptions = createOptions; + this.matcher = matcher; + this.type = type; + this.id = id; + this.fulfillmentActionName = fulfillmentActionName; + } + + /** + * the creation candidates data used for display and matching purpose, as a ByteArray blob + */ + @NonNull + public byte[] getCreateOptions() { + return createOptions; + } + + /** + * optionally specify a different intent action to be used for launching the fulfillment activity when one of the registered credentials is selected + * by the user; otherwise, the default action {@code "androidx.credentials.registry.provider.action.CREATE_CREDENTIAL"} will be used + */ + @NonNull + public String getFulfillmentActionName() { + return fulfillmentActionName; + } + + /** + * the id of the given registry data, so as not to overwrite existing data of different id + */ + @NonNull + public String getId() { + return id; + } + + /** + * the matcher for the credential info, as a ByteArray blob + */ + @NonNull + public byte[] getMatcher() { + return matcher; + } + + /** + * the type of the creation options registered, matching the {@code CreateCredentialRequest.type} that this registry can handle + */ + @NonNull + public String getType() { + return type; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(RegisterCreationOptionsRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterCreationOptionsResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterCreationOptionsResponse.java new file mode 100644 index 000000000..d81eac7c4 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterCreationOptionsResponse.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Response object for the {@link IdentityCredentialClient#registerCredentials} API. + */ +@SafeParcelable.Class +public class RegisterCreationOptionsResponse extends AbstractSafeParcelable { + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(RegisterCreationOptionsResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterExportRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterExportRequest.java new file mode 100644 index 000000000..2013c50f8 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterExportRequest.java @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * A registration request for declaring that the callee is a credential provider that supports exporting of credentials to other credential providers + */ +@SafeParcelable.Class +public class RegisterExportRequest extends AbstractSafeParcelable { + @NonNull + public static final String REQUEST_TYPE = "androidx.identitycredentials.TYPE_CREDENTIALS_SYNC"; + + @Field(value = 1, getterName = "getMatcher") + @NonNull + private final byte[] matcher; + @Field(value = 2, getterName = "getData") + @NonNull + private final byte[] data; + @Field(value = 3, getterName = "getId") + @NonNull + private final String id; + + /** + * @param matcher the matcher executor that runs the matching logic + * @param data any data to be registered along with the ability to export, typically empty for this use-case + * @param id the ID of the given registry data, so as not to overwrite existing data of different ID + */ + @Constructor + public RegisterExportRequest(@NonNull @Param(1) byte[] matcher, @NonNull @Param(2) byte[] data, @NonNull @Param(3) String id) { + this.matcher = matcher; + this.data = data; + this.id = id; + } + + /** + * any data to be registered along with the ability to export, typically empty for this use-case + */ + @NonNull + public byte[] getData() { + return data; + } + + /** + * the ID of the given registry data, so as not to overwrite existing data of different ID + */ + @NonNull + public String getId() { + return id; + } + + /** + * the matcher executor that runs the matching logic + */ + @NonNull + public byte[] getMatcher() { + return matcher; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(RegisterExportRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterExportResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterExportResponse.java new file mode 100644 index 000000000..818571b7a --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegisterExportResponse.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Response for registering the ability to export credentials to other providers + */ +@SafeParcelable.Class +public class RegisterExportResponse extends AbstractSafeParcelable { + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(RegisterExportResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegistrationRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegistrationRequest.java new file mode 100644 index 000000000..75fb55074 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegistrationRequest.java @@ -0,0 +1,142 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +import java.util.List; + +/** + * A registration request to store credential metadata and matcher logic. + */ +@SafeParcelable.Class +public class RegistrationRequest extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getCredentials") + @NonNull + private final byte[] credentials; + @Field(value = 2, getterName = "getMatcher") + @NonNull + private final byte[] matcher; + @Field(value = 3, getterName = "getType", defaultValue = "\"\"") + @NonNull + private final String type; + @Field(value = 4, getterName = "getRequestType", defaultValue = "\"\"") + @NonNull + @Deprecated + private final String requestType; + @Field(value = 5, getterName = "getProtocolTypes", defaultValue = "java.util.Collections.emptyList()") + @NonNull + @Deprecated + private final List protocolTypes; + @Field(value = 6, getterName = "getId", defaultValue = "\"\"") + @NonNull + private final String id; + + @Field(value = 7, getterName = "getFulfillmentActionName", defaultValue = "\"\"") + @NonNull + private final String fulfillmentActionName; + + public RegistrationRequest(@NonNull byte[] credentials, @NonNull byte[] matcher, @NonNull String type, @Deprecated @NonNull String requestType, @Deprecated @NonNull List protocolTypes) { + this(credentials, matcher, type, requestType, protocolTypes, "", ""); + } + + /** + * constructs an instance of {@link RegistrationRequest} + * + * @param credentials the credential information as a ByteArray blob + * @param matcher the matcher for the credential info, also as a ByteArray blob + * @param type the type of credentials matching the given registry data + * @param requestType deprecated + * @param protocolTypes deprecated + * @param id the id of the given registry data, so as not to overwrite existing data of different id + * @param fulfillmentActionName optionally specify a different intent action to be used for launching the fulfillment activity when one of the registered credentials is selected by the user; otherwise, the default action {@code "androidx.credentials.registry.provider.action.GET_CREDENTIAL"} will be used + */ + @Constructor + public RegistrationRequest(@NonNull @Param(1) byte[] credentials, @NonNull @Param(2) byte[] matcher, @NonNull @Param(3) String type, @Deprecated @NonNull @Param(4) String requestType, @Deprecated @NonNull @Param(5) List protocolTypes, @NonNull @Param(6) String id, @NonNull @Param(7) String fulfillmentActionName) { + this.credentials = credentials; + this.matcher = matcher; + this.type = type; + this.requestType = requestType; + this.protocolTypes = protocolTypes; + this.id = id; + this.fulfillmentActionName = fulfillmentActionName; + } + + /** + * the credential information as a ByteArray blob + */ + @NonNull + public byte[] getCredentials() { + return credentials; + } + + /** + * optionally specify a different intent action to be used for launching the fulfillment activity when one of the registered credentials is selected + * by the user; otherwise, the default action {@code "androidx.credentials.registry.provider.action.GET_CREDENTIAL"} will be used + */ + @NonNull + public String getFulfillmentActionName() { + return fulfillmentActionName; + } + + /** + * the id of the given registry data, so as not to overwrite existing data of different id + */ + @NonNull + public String getId() { + return id; + } + + /** + * the matcher for the credential info, also as a ByteArray blob + */ + @NonNull + public byte[] getMatcher() { + return matcher; + } + + /** + * @deprecated + */ + @Deprecated + @NonNull + public List getProtocolTypes() { + return protocolTypes; + } + + /** + * @deprecated + */ + @Deprecated + @NonNull + public String getRequestType() { + return requestType; + } + + /** + * the type of credentials matching the given registry data + */ + @NonNull + public String getType() { + return type; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(RegistrationRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegistrationResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegistrationResponse.java new file mode 100644 index 000000000..8f3544579 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/RegistrationResponse.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Response object for the IdentityCredentialClient.registerCredentials API. + */ +@SafeParcelable.Class +public class RegistrationResponse extends AbstractSafeParcelable { + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(RegistrationResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/SignalCredentialStateRequest.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/SignalCredentialStateRequest.java new file mode 100644 index 000000000..67f2a953e --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/SignalCredentialStateRequest.java @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Data interface for signaling a user's credential state from the RPs to the credential providers. + */ +@SafeParcelable.Class +public class SignalCredentialStateRequest extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getType") + @NonNull + private final String type; + + @Field(value = 2, getterName = "getOrigin") + @Nullable + private final String origin; + + @Field(value = 3, getterName = "getRequestData") + @NonNull + private final Bundle requestData; + + /** + * constructs an instance of {@link SignalCredentialStateRequest} + * + * @param type the type of signal request being sent to the provider + * @param origin the origin of the request, only settable by a browser + * @param requestData the request data, containing the credential state information + */ + @Constructor + public SignalCredentialStateRequest(@NonNull @Param(1) String type, @Param(2) @Nullable String origin, @NonNull @Param(3) Bundle requestData) { + this.type = type; + this.origin = origin; + this.requestData = requestData; + } + + /** + * the origin of the request, only settable by a browser + */ + @Nullable + public String getOrigin() { + return origin; + } + + /** + * the request data, containing the credential state information + */ + @NonNull + public Bundle getRequestData() { + return requestData; + } + + /** + * the type of signal request being sent to the provider + */ + @NonNull + public String getType() { + return type; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(SignalCredentialStateRequest.class); +} diff --git a/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/SignalCredentialStateResponse.java b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/SignalCredentialStateResponse.java new file mode 100644 index 000000000..fa7b353d2 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/com/google/android/gms/identitycredentials/SignalCredentialStateResponse.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.identitycredentials; + +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; + +/** + * Response of a signal credential state request. + */ +@SafeParcelable.Class +public class SignalCredentialStateResponse extends AbstractSafeParcelable { + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(SignalCredentialStateResponse.class); +} diff --git a/play-services-identity-credentials/src/main/java/org/microg/gms/identitycredentials/IdentityCredentialApiClient.java b/play-services-identity-credentials/src/main/java/org/microg/gms/identitycredentials/IdentityCredentialApiClient.java new file mode 100644 index 000000000..d71f3a356 --- /dev/null +++ b/play-services-identity-credentials/src/main/java/org/microg/gms/identitycredentials/IdentityCredentialApiClient.java @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.identitycredentials; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.ApiMetadata; +import com.google.android.gms.common.api.Status; +import com.google.android.gms.common.api.internal.ConnectionCallbacks; +import com.google.android.gms.common.api.internal.OnConnectionFailedListener; +import com.google.android.gms.identitycredentials.ClearCreationOptionsRequest; +import com.google.android.gms.identitycredentials.ClearCredentialStateRequest; +import com.google.android.gms.identitycredentials.ClearExportRequest; +import com.google.android.gms.identitycredentials.ClearRegistryRequest; +import com.google.android.gms.identitycredentials.CreateCredentialRequest; +import com.google.android.gms.identitycredentials.GetCredentialRequest; +import com.google.android.gms.identitycredentials.ImportCredentialsRequest; +import com.google.android.gms.identitycredentials.RegisterCreationOptionsRequest; +import com.google.android.gms.identitycredentials.RegisterExportRequest; +import com.google.android.gms.identitycredentials.RegistrationRequest; +import com.google.android.gms.identitycredentials.SignalCredentialStateRequest; +import com.google.android.gms.identitycredentials.internal.IIdentityCredentialCallbacks; +import com.google.android.gms.identitycredentials.internal.IIdentityCredentialService; +import org.microg.gms.common.GmsClient; +import org.microg.gms.common.GmsService; + +public class IdentityCredentialApiClient extends GmsClient { + public static final Api API = new Api<>( + (options, context, looper, clientSettings, callbacks, connectionFailedListener) -> + new IdentityCredentialApiClient(context, callbacks, connectionFailedListener)); + + public IdentityCredentialApiClient(Context context, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) { + super(context, callbacks, connectionFailedListener, GmsService.IDENTITY_CREDENTIALS.ACTION); + serviceId = GmsService.IDENTITY_CREDENTIALS.SERVICE_ID; + } + + @Override + protected IIdentityCredentialService interfaceFromBinder(IBinder binder) { + return IIdentityCredentialService.Stub.asInterface(binder); + } + + public void getCredential(IIdentityCredentialCallbacks callbacks, GetCredentialRequest request) { + try { + getServiceInterface().getCredential(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onGetCredential(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void registerCredentials(IIdentityCredentialCallbacks callbacks, RegistrationRequest request) { + try { + getServiceInterface().register(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onRegister(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void clearRegistry(IIdentityCredentialCallbacks callbacks, ClearRegistryRequest request) { + try { + getServiceInterface().clearRegistry(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onClearRegistry(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void importCredentials(IIdentityCredentialCallbacks callbacks, ImportCredentialsRequest request) { + try { + getServiceInterface().importCredentials(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onImportCredentials(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void registerExport(IIdentityCredentialCallbacks callbacks, RegisterExportRequest request) { + try { + getServiceInterface().registerExport(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onRegisterExport(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void createCredential(IIdentityCredentialCallbacks callbacks, CreateCredentialRequest request) { + try { + getServiceInterface().createCredential(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onCreateCredential(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void registerCreationOptions(IIdentityCredentialCallbacks callbacks, RegisterCreationOptionsRequest request) { + try { + getServiceInterface().registerCreationOptions(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onRegisterCreationOptions(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void clearCredentialState(IIdentityCredentialCallbacks callbacks, ClearCredentialStateRequest request) { + try { + getServiceInterface().clearCredentialState(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onClearCredentialState(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void signalCredentialState(IIdentityCredentialCallbacks callbacks, SignalCredentialStateRequest request) { + try { + getServiceInterface().signalCredentialState(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onSignalCredentialState(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void clearExport(IIdentityCredentialCallbacks callbacks, ClearExportRequest request) { + try { + getServiceInterface().clearExport(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onClearExport(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + public void clearCreationOptions(IIdentityCredentialCallbacks callbacks, ClearCreationOptionsRequest request) { + try { + getServiceInterface().clearCreationOptions(callbacks, request, ApiMetadata.SKIP); + } catch (RemoteException e) { + tryNotifyError(() -> callbacks.onClearCreationOptions(Status.INTERNAL_ERROR, null, ApiMetadata.SKIP)); + } + } + + private interface RemoteRunnable { + void run() throws RemoteException; + } + + private static void tryNotifyError(RemoteRunnable runnable) { + try { + runnable.run(); + } catch (RemoteException ignored) { + } + } +} diff --git a/play-services-identity-credentials/src/main/java/org/microg/gms/identitycredentials/IdentityCredentialClientImpl.java b/play-services-identity-credentials/src/main/java/org/microg/gms/identitycredentials/IdentityCredentialClientImpl.java new file mode 100644 index 000000000..e82d7d8cf --- /dev/null +++ b/play-services-identity-credentials/src/main/java/org/microg/gms/identitycredentials/IdentityCredentialClientImpl.java @@ -0,0 +1,212 @@ +/* + * SPDX-FileCopyrightText: 2026 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.identitycredentials; + +import android.content.Context; +import androidx.annotation.NonNull; +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.ApiException; +import com.google.android.gms.common.api.ApiMetadata; +import com.google.android.gms.common.api.GoogleApi; +import com.google.android.gms.common.api.Status; +import com.google.android.gms.identitycredentials.ClearCreationOptionsRequest; +import com.google.android.gms.identitycredentials.ClearCreationOptionsResponse; +import com.google.android.gms.identitycredentials.ClearCredentialStateRequest; +import com.google.android.gms.identitycredentials.ClearCredentialStateResponse; +import com.google.android.gms.identitycredentials.ClearExportRequest; +import com.google.android.gms.identitycredentials.ClearExportResponse; +import com.google.android.gms.identitycredentials.ClearRegistryRequest; +import com.google.android.gms.identitycredentials.ClearRegistryResponse; +import com.google.android.gms.identitycredentials.CreateCredentialHandle; +import com.google.android.gms.identitycredentials.CreateCredentialRequest; +import com.google.android.gms.identitycredentials.CreateCredentialResponse; +import com.google.android.gms.identitycredentials.CredentialInformationResponse; +import com.google.android.gms.identitycredentials.CredentialTransferCapabilities; +import com.google.android.gms.identitycredentials.ExportCredentialsToDeviceSetupResponse; +import com.google.android.gms.identitycredentials.GetCredentialRequest; +import com.google.android.gms.identitycredentials.IdentityCredentialClient; +import com.google.android.gms.identitycredentials.ImportCredentialsForDeviceSetupResponse; +import com.google.android.gms.identitycredentials.ImportCredentialsRequest; +import com.google.android.gms.identitycredentials.PendingGetCredentialHandle; +import com.google.android.gms.identitycredentials.PendingImportCredentialsHandle; +import com.google.android.gms.identitycredentials.RegisterCreationOptionsRequest; +import com.google.android.gms.identitycredentials.RegisterCreationOptionsResponse; +import com.google.android.gms.identitycredentials.RegisterExportRequest; +import com.google.android.gms.identitycredentials.RegisterExportResponse; +import com.google.android.gms.identitycredentials.RegistrationRequest; +import com.google.android.gms.identitycredentials.RegistrationResponse; +import com.google.android.gms.identitycredentials.SignalCredentialStateRequest; +import com.google.android.gms.identitycredentials.SignalCredentialStateResponse; +import com.google.android.gms.identitycredentials.internal.IIdentityCredentialCallbacks; +import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.TaskCompletionSource; +import org.microg.gms.common.api.PendingGoogleApiCall; + +public class IdentityCredentialClientImpl extends GoogleApi implements IdentityCredentialClient { + public IdentityCredentialClientImpl(Context context) { + super(context, IdentityCredentialApiClient.API, Api.ApiOptions.NO_OPTIONS); + } + + @NonNull + @Override + public Task clearCreationOptions(@NonNull ClearCreationOptionsRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.clearCreationOptions(new BaseCallbacks() { + @Override + public void onClearCreationOptions(Status status, ClearCreationOptionsResponse response, ApiMetadata apiMetadata) { + complete(source, status, response); + } + }, request)); + } + + @NonNull + @Override + public Task clearCredentialState(@NonNull ClearCredentialStateRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.clearCredentialState(new BaseCallbacks() { + @Override + public void onClearCredentialState(Status status, ClearCredentialStateResponse response, ApiMetadata apiMetadata) { + complete(source, status, response); + } + }, request)); + } + + @NonNull + @Override + public Task clearExport(@NonNull ClearExportRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.clearExport(new BaseCallbacks() { + @Override + public void onClearExport(Status status, ClearExportResponse response, ApiMetadata apiMetadata) { + complete(source, status, response); + } + }, request)); + } + + @NonNull + @Override + public Task clearRegistry(@NonNull ClearRegistryRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.clearRegistry(new BaseCallbacks() { + @Override + public void onClearRegistry(Status status, ClearRegistryResponse response, ApiMetadata apiMetadata) { + complete(source, status, response); + } + }, request)); + } + + @NonNull + @Override + public Task createCredential(@NonNull CreateCredentialRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.createCredential(new BaseCallbacks() { + @Override + public void onCreateCredential(Status status, CreateCredentialHandle handle, ApiMetadata apiMetadata) { + complete(source, status, handle); + } + }, request)); + } + + @NonNull + @Override + public Task getCredential(@NonNull GetCredentialRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.getCredential(new BaseCallbacks() { + @Override + public void onGetCredential(Status status, PendingGetCredentialHandle handle, ApiMetadata apiMetadata) { + complete(source, status, handle); + } + }, request)); + } + + @NonNull + @Override + public Task importCredentials(@NonNull ImportCredentialsRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.importCredentials(new BaseCallbacks() { + @Override + public void onImportCredentials(Status status, PendingImportCredentialsHandle handle, ApiMetadata apiMetadata) { + complete(source, status, handle); + } + }, request)); + } + + @NonNull + @Override + public Task registerCreationOptions(@NonNull RegisterCreationOptionsRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.registerCreationOptions(new BaseCallbacks() { + @Override + public void onRegisterCreationOptions(Status status, RegisterCreationOptionsResponse response, ApiMetadata apiMetadata) { + complete(source, status, response); + } + }, request)); + } + + @NonNull + @Override + public Task registerCredentials(@NonNull RegistrationRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.registerCredentials(new BaseCallbacks() { + @Override + public void onRegister(Status status, RegistrationResponse response, ApiMetadata apiMetadata) { + complete(source, status, response); + } + }, request)); + } + + @NonNull + @Override + public Task registerExport(@NonNull RegisterExportRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.registerExport(new BaseCallbacks() { + @Override + public void onRegisterExport(Status status, RegisterExportResponse response, ApiMetadata apiMetadata) { + complete(source, status, response); + } + }, request)); + } + + @NonNull + @Override + public Task signalCredentialState(@NonNull SignalCredentialStateRequest request) { + return scheduleTask((PendingGoogleApiCall) (client, source) -> + client.signalCredentialState(new BaseCallbacks() { + @Override + public void onSignalCredentialState(Status status, SignalCredentialStateResponse response, ApiMetadata apiMetadata) { + complete(source, status, response); + } + }, request)); + } + + private static void complete(TaskCompletionSource source, Status status, T result) { + if (status != null && status.isSuccess()) { + source.trySetResult(result); + } else { + source.trySetException(new ApiException(status == null ? Status.INTERNAL_ERROR : status)); + } + } + + // Empty defaults so each scheduleTask can override only the relevant callback. + private static abstract class BaseCallbacks extends IIdentityCredentialCallbacks.Stub { + @Override public void onGetCredential(Status status, PendingGetCredentialHandle handle, ApiMetadata apiMetadata) {} + @Override public void onRegister(Status status, RegistrationResponse response, ApiMetadata apiMetadata) {} + @Override public void onClearRegistry(Status status, ClearRegistryResponse response, ApiMetadata apiMetadata) {} + @Override public void onImportCredentials(Status status, PendingImportCredentialsHandle handle, ApiMetadata apiMetadata) {} + @Override public void onRegisterExport(Status status, RegisterExportResponse response, ApiMetadata apiMetadata) {} + @Override public void onCreateCredentialLegacy(Status status, CreateCredentialResponse response, ApiMetadata apiMetadata) {} + @Override public void onCreateCredential(Status status, CreateCredentialHandle handle, ApiMetadata apiMetadata) {} + @Override public void onRegisterCreationOptions(Status status, RegisterCreationOptionsResponse response, ApiMetadata apiMetadata) {} + @Override public void onClearCredentialState(Status status, ClearCredentialStateResponse response, ApiMetadata apiMetadata) {} + @Override public void onSignalCredentialState(Status status, SignalCredentialStateResponse response, ApiMetadata apiMetadata) {} + @Override public void onClearExport(Status status, ClearExportResponse response, ApiMetadata apiMetadata) {} + @Override public void onImportCredentialsForDeviceSetup(Status status, ImportCredentialsForDeviceSetupResponse response, ApiMetadata apiMetadata) {} + @Override public void onExportCredentialsToDeviceSetup(Status status, ExportCredentialsToDeviceSetupResponse response, ApiMetadata apiMetadata) {} + @Override public void onGetCredentialTransferCapabilities(Status status, CredentialTransferCapabilities capabilities, ApiMetadata apiMetadata) {} + @Override public void onClearCreationOptions(Status status, ClearCreationOptionsResponse response, ApiMetadata apiMetadata) {} + @Override public void onGetCredentialInformation(Status status, CredentialInformationResponse response) {} + } +} diff --git a/settings.gradle b/settings.gradle index a5bc84ca3..331ed3319 100644 --- a/settings.gradle +++ b/settings.gradle @@ -43,6 +43,7 @@ include ':play-services-fido' include ':play-services-games' include ':play-services-gcm' include ':play-services-gmscompliance' +include ':play-services-identity-credentials' include ':play-services-iid' include ':play-services-location' include ':play-services-maps' @@ -98,6 +99,7 @@ sublude ':play-services-droidguard:core' sublude ':play-services-fido:core' sublude ':play-services-fitness:core' sublude ':play-services-gmscompliance:core' +sublude ':play-services-identity-credentials:core' sublude ':play-services-location:core' sublude ':play-services-location:core:base' sublude ':play-services-location:core:provider'