diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec887b76bd..aada208207 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: # Log available space df -h - name: "Checkout sources" - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 - name: "Setup Java" diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml index 4b6ba2c45c..f1f0048570 100644 --- a/.github/workflows/dependency-submission.yml +++ b/.github/workflows/dependency-submission.yml @@ -13,7 +13,7 @@ jobs: steps: - name: "Checkout sources" - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 - name: "Setup Java" diff --git a/build.gradle b/build.gradle index 296bd87097..d64e7e78a5 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 97b1364265..0026c72789 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 967b2d442b..d7134cab31 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 a17d7f03ef..ec46dd59de 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 ed24e4cb09..489b9b5802 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 c2d4076990..72c35dc181 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 e598964e87..49e90df780 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-core/src/main/kotlin/org/microg/gms/games/ui/GameDeletePlayAccountFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/games/ui/GameDeletePlayAccountFragment.kt index 1b74aa2e14..d63e6d2b44 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/games/ui/GameDeletePlayAccountFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/games/ui/GameDeletePlayAccountFragment.kt @@ -84,28 +84,33 @@ class GameDeletePlayAccountFragment : Fragment() { private fun loadIndividualGames() { lifecycleScope.launchWhenStarted { - val response = withContext(Dispatchers.IO) { - val scopes = arrayListOf(Scope(Scopes.GAMES_LITE), Scope(Scopes.GAMES_FIRSTPARTY)) - val authToken = requestGameToken(requireContext(), lastChoosePlayer!!.first, scopes) ?: throw RuntimeException("authToken is null") - getApplicationsFirstPartyClient(requireContext(), authToken).ListApplicationsWithUserDataFirstParty().execute(ListApplicationsWithUserDataRequest.build { - locale = Locale.getDefault().language - androidSdk = "android:${SDK_INT}" - }) - } - val dataList = response.firstPartyApplication.map { - GameDataDeleteItem( - iconUrl = it.application?.gameIcon?.url, - gameName = it.application?.gameName, - gameId = it.application?.gameId, - tips = "${it.unlockAchievementsNum} / ${it.application?.achievementsNum} ${requireContext().getString(R.string.games_achievement_list_title)}" - ) - } - individualGameRecyclerView.layoutManager = LinearLayoutManager(requireContext()) - individualGameRecyclerView.adapter = GameDataDeleteAdapter(dataList) { item -> - showDeleteConfirmationDialog(item) + try { + val response = withContext(Dispatchers.IO) { + val scopes = arrayListOf(Scope(Scopes.GAMES_LITE), Scope(Scopes.GAMES_FIRSTPARTY)) + val authToken = requestGameToken(requireContext(), lastChoosePlayer!!.first, scopes) ?: throw RuntimeException("authToken is null") + getApplicationsFirstPartyClient(requireContext(), authToken).ListApplicationsWithUserDataFirstParty().execute(ListApplicationsWithUserDataRequest.build { + locale = Locale.getDefault().language + androidSdk = "android:${SDK_INT}" + }) + } + val dataList = response.firstPartyApplication.map { + GameDataDeleteItem( + iconUrl = it.application?.gameIcon?.url, + gameName = it.application?.gameName, + gameId = it.application?.gameId, + tips = "${it.unlockAchievementsNum} / ${it.application?.achievementsNum} ${requireContext().getString(R.string.games_achievement_list_title)}" + ) + } + individualGameRecyclerView.layoutManager = LinearLayoutManager(requireContext()) + individualGameRecyclerView.adapter = GameDataDeleteAdapter(dataList) { item -> + showDeleteConfirmationDialog(item) + } + individualGameRecyclerView.visibility = View.VISIBLE + loadingBar.visibility = View.GONE + } catch (e: Exception) { + Log.w(TAG, "loadIndividualGames: ", e) + loadingBar.visibility = View.GONE } - individualGameRecyclerView.visibility = View.VISIBLE - loadingBar.visibility = View.GONE } } diff --git a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/TransportHandler.kt b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/TransportHandler.kt index e3a3669782..63fe3c79da 100644 --- a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/TransportHandler.kt +++ b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/TransportHandler.kt @@ -515,9 +515,9 @@ abstract class TransportHandler(val transport: Transport, val callback: Transpor clientData, response.authData, response.signature, - null + response.user?.id ), - null + response.user ) } 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 ec4bcf8b55..64300b7076 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-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/UsbDevicePermissionManager.kt b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/UsbDevicePermissionManager.kt index 72e9e17c99..b0e8799395 100644 --- a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/UsbDevicePermissionManager.kt +++ b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/UsbDevicePermissionManager.kt @@ -9,12 +9,16 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.util.Log import android.hardware.usb.UsbDevice import android.hardware.usb.UsbManager +import android.os.Build import androidx.core.app.PendingIntentCompat import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat.RECEIVER_NOT_EXPORTED import kotlinx.coroutines.CompletableDeferred +import java.util.Timer +import java.util.TimerTask private val Context.usbPermissionCallbackAction get() = "$packageName.USB_PERMISSION_CALLBACK" @@ -40,6 +44,10 @@ private object UsbDevicePermissionReceiver : BroadcastReceiver() { } } + fun isDeferred(device: UsbDevice): Boolean = synchronized(this) { + return pendingRequests.containsKey(device) + } + fun unregister(context: Context) = synchronized(this) { if (registered) { context.unregisterReceiver(this) @@ -75,10 +83,39 @@ class UsbDevicePermissionManager(private val context: Context) { if (context.usbManager?.hasPermission(device) == true) return true val res = CompletableDeferred() if (UsbDevicePermissionReceiver.addDeferred(device, res)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Log.d(TAG, "PermissionReceiver added for ${device.productName} (${context.packageName})") + } UsbDevicePermissionReceiver.register(context) - val intent = PendingIntentCompat.getBroadcast(context, 0, Intent(context.usbPermissionCallbackAction).apply { `package` = context.packageName }, 0, true) - context.usbManager?.requestPermission(device, intent) + schedulePermissionRequest(device, 5) + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Log.d(TAG, "PermissionReceiver already added for ${device.productName}") + } } + requestPermission(device) return res.await() } + + // In case we couldn't ask for permission, retry every secondes, with a maximum of `maxRetries` + private fun schedulePermissionRequest(device: UsbDevice, maxRetries: Int) { + if (maxRetries < 1) return + Timer().schedule(object : TimerTask() { + override fun run() { + if (UsbDevicePermissionReceiver.isDeferred(device)) { + requestPermission(device) + schedulePermissionRequest(device, maxRetries - 1) + } + } + }, 1000) + } + + private fun requestPermission(device: UsbDevice) { + val intent = PendingIntentCompat.getBroadcast(context, 0, Intent(context.usbPermissionCallbackAction).apply { `package` = context.packageName }, 0, true) + context.usbManager?.requestPermission(device, intent) + } + + companion object { + private const val TAG = "UsbDevicePermissionMan" + } } diff --git a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/UsbTransportHandler.kt b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/UsbTransportHandler.kt index f023c9bd63..292ff4cb8c 100644 --- a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/UsbTransportHandler.kt +++ b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/transport/usb/UsbTransportHandler.kt @@ -24,6 +24,7 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletableDeferred import org.microg.gms.fido.core.* import org.microg.gms.fido.core.transport.AuthenticatorResponseWithUser +import org.microg.gms.fido.core.transport.Ctap2StatusException import org.microg.gms.fido.core.transport.Transport import org.microg.gms.fido.core.transport.TransportHandler import org.microg.gms.fido.core.transport.TransportHandlerCallback @@ -155,7 +156,9 @@ class UsbTransportHandler(private val context: Context, callback: TransportHandl throw e } catch (e: WrongPinException) { throw e - } catch (e: Exception) { + } catch (e: Ctap2StatusException) { + throw e + } catch (e: Exception) { Log.w(TAG, e) } } @@ -167,6 +170,12 @@ class UsbTransportHandler(private val context: Context, callback: TransportHandl return handle(options, callerPackage, device, iface, pinRequested, pin) } catch (e: CancellationException) { throw e + } catch (e: MissingPinException) { + throw e + } catch (e: WrongPinException) { + throw e + } catch (e: Ctap2StatusException) { + throw e } catch (e: Exception) { Log.w(TAG, e) } diff --git a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/AuthenticatorActivity.kt b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/AuthenticatorActivity.kt index d3cfdf3567..b55e38d98f 100644 --- a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/AuthenticatorActivity.kt +++ b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/AuthenticatorActivity.kt @@ -62,6 +62,10 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { val source: String? get() = intent.getStringExtra(KEY_SOURCE) + enum class AllowedInstantLevel { + NONE, PRESELECT, INSTANT + } + private val service: GmsService get() = GmsService.byServiceId(intent.getIntExtra(KEY_SERVICE, GmsService.UNKNOWN.SERVICE_ID)) private val database by lazy { Database(this) } @@ -79,6 +83,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { lateinit var callerSignature: String private lateinit var navHostFragment: NavHostFragment private var preselectedCredentialId: String? = null + private var preselectedTransport: Transport? = null private inline fun getTransportHandler(): T? = transportHandlers.filterIsInstance().firstOrNull { it.isSupported } @@ -106,14 +111,23 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { this.callerSignature = packageManager.getFirstSignatureDigest(callerPackage, "SHA-256")?.toBase64() ?: return finishWithError(UNKNOWN_ERR, "Could not determine signature of app") this.preselectedCredentialId = intent.getStringExtra(KEY_CREDENTIAL_ID) + this.preselectedTransport = intent.getStringExtra(KEY_PRESELECTED_TRANSPORT)?.let { + runCatching { Transport.valueOf(it) }.getOrNull() + } Log.d(TAG, "onCreate caller=$callerPackage options=$options preselectedCredentialId=$preselectedCredentialId") + // By default, we allow instants. This extra may be used by app using fido-core library + // To avoid instant. + val allowInstant = AllowedInstantLevel.entries.getOrNull( + intent.getIntExtra(KEY_ALLOW_INSTANT, AllowedInstantLevel.INSTANT.ordinal) + ) ?: AllowedInstantLevel.INSTANT + val requiresPrivilege = source == SOURCE_BROWSER && !database.isPrivileged(callerPackage, callerSignature) // Check if we can directly open screen lock handling - if (!requiresPrivilege) { + if (allowInstant == AllowedInstantLevel.INSTANT && !requiresPrivilege) { val instantTransport = transportHandlers.firstOrNull { it.isSupported && it.shouldBeUsedInstantly(options, preselectedCredentialId) } @@ -128,7 +142,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { setContentView(R.layout.fido_authenticator_activity) lifecycleScope.launchWhenCreated { - handleRequest(options) + handleRequest(options, allowInstant) } } catch (e: RequestHandlingException) { finishWithError(e.errorCode, e.message ?: e.errorCode.name) @@ -139,7 +153,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { } @RequiresApi(24) - suspend fun handleRequest(options: RequestOptions, allowInstant: Boolean = true) { + suspend fun handleRequest(options: RequestOptions, allowInstantLevel: AllowedInstantLevel = AllowedInstantLevel.INSTANT) { try { val origin = getOrigin(this, options, callerPackage) options.checkIsValid(this, origin, callerPackage) @@ -154,7 +168,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { val noLocalUserForSignInstantBlock = options.type == RequestOptionsType.SIGN && database.getKnownRegistrationInfo(options.rpId).isEmpty() // Check if we can directly open screen lock handling - if (!requiresPrivilege && allowInstant && !noLocalUserForSignInstantBlock) { + if (!requiresPrivilege && allowInstantLevel == AllowedInstantLevel.INSTANT && !noLocalUserForSignInstantBlock) { val instantTransport = transportHandlers.firstOrNull { it.isSupported && it.shouldBeUsedInstantly(options, preselectedCredentialId) } @@ -170,8 +184,16 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { this.privilegedCallerName = callerName.takeIf { options is BrowserRequestOptions } this.requiresPrivilege = requiresPrivilege this.supportedTransports = transportHandlers.filter { it.isSupported }.map { it.transport }.toSet() + // To sign-in with screen-lock, there must be a local user. + if (options.type == RequestOptionsType.SIGN && noLocalUserForSignInstantBlock) { + this.implementedTransports = this.implementedTransports.filter { it != SCREEN_LOCK }.toSet() + } }.arguments - val next = if (!requiresPrivilege) { + val next = if (requiresPrivilege) { + null + } else if (allowInstantLevel == AllowedInstantLevel.NONE) { + R.id.transportSelectionFragment + } else { val knownRegistrationTransports = mutableSetOf() val allowedTransports = mutableSetOf() if (options.type == RequestOptionsType.SIGN) { @@ -203,7 +225,12 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { } } } - val preselectedTransport = knownRegistrationTransports.singleOrNull() ?: allowedTransports.singleOrNull() + // We do not control if preselectedTransport is in allowedTransports, or if + // allowedTransports is empty, as allowedTransports is a *hint*. + // This is particularly useful if the application sends a credential with + // transport=internal, but we login with another device (via hybrid connection) + val preselectedTransport = preselectedTransport + ?: knownRegistrationTransports.singleOrNull() ?: allowedTransports.singleOrNull() if (database.wasUsed()) { when (preselectedTransport) { USB -> R.id.usbFragment @@ -215,8 +242,6 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { } else { null } - } else { - null } navHostFragment = NavHostFragment() supportFragmentManager.commit { @@ -321,7 +346,7 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { } catch (e: SecurityException) { Log.w(TAG, e) if (instant) { - handleRequest(options, false) + handleRequest(options, AllowedInstantLevel.PRESELECT) } else { finishWithError(SECURITY_ERR, e.message ?: e.javaClass.simpleName) } @@ -381,9 +406,11 @@ class AuthenticatorActivity : AppCompatActivity(), TransportHandlerCallback { const val KEY_SERVICE = "service" const val KEY_SOURCE = "source" const val KEY_TYPE = "type" + const val KEY_ALLOW_INSTANT = "allowInstant" const val KEY_OPTIONS = "options" const val KEY_USER_JSON = "userInfo" const val KEY_CREDENTIAL_ID = "credential" + const val KEY_PRESELECTED_TRANSPORT = "transport" val REQUIRED_EXTRAS = setOf(KEY_SOURCE, KEY_TYPE, KEY_OPTIONS) const val SOURCE_BROWSER = "browser" diff --git a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/AuthenticatorActivityFragmentData.kt b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/AuthenticatorActivityFragmentData.kt index 8625b74a5b..fb1d7f378d 100644 --- a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/AuthenticatorActivityFragmentData.kt +++ b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/AuthenticatorActivityFragmentData.kt @@ -22,8 +22,10 @@ class AuthenticatorActivityFragmentData(val arguments: Bundle = Bundle()) { get() = arguments.getStringArrayList(KEY_SUPPORTED_TRANSPORTS)?.map { Transport.valueOf(it) }?.toSet().orEmpty() set(value) = arguments.putStringArrayList(KEY_SUPPORTED_TRANSPORTS, ArrayList(value.map { it.name })) - val implementedTransports: Set - get() = AuthenticatorActivity.IMPLEMENTED_TRANSPORTS + var implementedTransports: Set + get() = arguments.getStringArrayList(KEY_IMPLEMENTED_TRANSPORTS)?.map { Transport.valueOf(it) }?.toSet() + ?: AuthenticatorActivity.IMPLEMENTED_TRANSPORTS + set(value) = arguments.putStringArrayList(KEY_IMPLEMENTED_TRANSPORTS, ArrayList(value.map { it.name })) var privilegedCallerName: String? get() = arguments.getString(KEY_PRIVILEGED_CALLER_NAME) @@ -37,6 +39,7 @@ class AuthenticatorActivityFragmentData(val arguments: Bundle = Bundle()) { const val KEY_APP_NAME = "appName" const val KEY_IS_FIRST = "isFirst" const val KEY_SUPPORTED_TRANSPORTS = "supportedTransports" + const val KEY_IMPLEMENTED_TRANSPORTS = "implementedTransports" const val KEY_REQUIRES_PRIVILEGE = "requiresPrivilege" const val KEY_PRIVILEGED_CALLER_NAME = "privilegedCallerName" } diff --git a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/hybrid/HybridAuthenticateActivity.kt b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/hybrid/HybridAuthenticateActivity.kt index 8c01710b51..309f14ff85 100644 --- a/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/hybrid/HybridAuthenticateActivity.kt +++ b/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/ui/hybrid/HybridAuthenticateActivity.kt @@ -121,12 +121,14 @@ class HybridAuthenticateActivity : AppCompatActivity() { hybridAuthenticatorController = hybridAuthenticatorController ?: HybridAuthenticatorController(this@HybridAuthenticateActivity) try { hybridAuthenticatorController?.startAuth(qrCodeData!!, handleAuthenticator = { - when (it) { - is AuthenticatorMakeCredentialRequest -> handleMakeCredential(it) - is AuthenticatorGetAssertionRequest -> handleGetAssertion(it) - is AuthenticatorGetInfoRequest -> handleGetInfo(it) - else -> null - } + runCatching { + when (it) { + is AuthenticatorMakeCredentialRequest -> handleMakeCredential(it) + is AuthenticatorGetAssertionRequest -> handleGetAssertion(it) + is AuthenticatorGetInfoRequest -> handleGetInfo(it) + else -> null + } + }.getOrNull() }, completed = { if (it) finishWithSuccess() else finishWithError("auth error") }) diff --git a/play-services-identity-credentials/build.gradle b/play-services-identity-credentials/build.gradle new file mode 100644 index 0000000000..6f374dbff5 --- /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 0000000000..30ef5927c3 --- /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 0000000000..f474e84df9 --- /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 0000000000..15efd6b0c7 --- /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 0000000000..8bdb7e14b3 --- /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 0000000000..a9069fdf40 --- /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 0000000000..f877c7b611 --- /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 0000000000..73923bbcc6 --- /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 0000000000..18cb0c4957 --- /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 0000000000..b483e6fac6 --- /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 0000000000..a64474f1b0 --- /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 0000000000..586d1c4359 --- /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 0000000000..f6ea9b8c75 --- /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 0000000000..100d74aa1d --- /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 0000000000..3fa9e19661 --- /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 0000000000..a78de7c617 --- /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 0000000000..02180d175e --- /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 0000000000..7704b5cce0 --- /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 0000000000..d8efeec230 --- /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 0000000000..f6ef69abbd --- /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 0000000000..aa24fa68f2 --- /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 0000000000..7bf033ae9b --- /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 0000000000..595d594c1e --- /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 0000000000..86bb3fbfc3 --- /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 0000000000..8ce822b3d4 --- /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 0000000000..23a7832ea9 --- /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 0000000000..03d4a4cdae --- /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 0000000000..f39b7be3a2 --- /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 0000000000..ba6c013532 --- /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 0000000000..c98462fe33 --- /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 0000000000..78a4981f50 --- /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 0000000000..404814b38b --- /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 0000000000..0c43c73cd3 --- /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 0000000000..af6a88ff30 --- /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 0000000000..8af24f09ef --- /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 0000000000..8b7fbde20a --- /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 0000000000..3195d46c7a --- /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 0000000000..69b38a5ae4 --- /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 0000000000..80749deee2 --- /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 0000000000..ba20b3a110 --- /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 0000000000..8bc2d94e91 --- /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 0000000000..75216c2751 --- /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 0000000000..1fd1d0ee82 --- /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 0000000000..4e2e7fea9e --- /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 0000000000..5ef5e698ad --- /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 0000000000..c8b6cc4171 --- /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 0000000000..31ab7cba73 --- /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 0000000000..9b40e00726 --- /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 0000000000..2ad729dd29 --- /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 0000000000..5fe98f47c0 --- /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 0000000000..1ec988a8bc --- /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 0000000000..1ee9417fcb --- /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 0000000000..538f40d8c5 --- /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 0000000000..c80ef87414 --- /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 0000000000..106b7ba19d --- /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 0000000000..d452cfe1c6 --- /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 0000000000..7b11ff432c --- /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 0000000000..b6fbf5b344 --- /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 0000000000..599195f1d9 --- /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 0000000000..f7fa992159 --- /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 0000000000..f476fc59a2 --- /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 0000000000..794775d1e3 --- /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 0000000000..33832bb67e --- /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 0000000000..21d73aea4e --- /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 0000000000..cb76056d25 --- /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 0000000000..a3b925315a --- /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 0000000000..2145fd0c64 --- /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 0000000000..69d68f58fa --- /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 0000000000..996ef4cc10 --- /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 0000000000..d81eac7c4e --- /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 0000000000..2013c50f8a --- /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 0000000000..818571b7ab --- /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 0000000000..75fb550741 --- /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 0000000000..8f3544579e --- /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 0000000000..67f2a953e6 --- /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 0000000000..fa7b353d24 --- /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 0000000000..d71f3a356f --- /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 0000000000..e82d7d8cf5 --- /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 a5bc84ca3c..331ed33190 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'