blob: 1df0251f4995e5eddc027b5d6e6e73390bbe4b3b [file] [log] [blame]
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.credentials.playservices
import android.app.Activity
import android.app.PendingIntent
import android.content.Intent
import android.content.IntentSender
import android.os.Bundle
import android.os.ResultReceiver
import android.util.Log
import androidx.credentials.exceptions.CreateCredentialInterruptedException
import androidx.credentials.exceptions.CreateCredentialUnknownException
import androidx.credentials.exceptions.GetCredentialInterruptedException
import androidx.credentials.exceptions.GetCredentialUnknownException
import androidx.credentials.exceptions.NoCredentialException
import androidx.credentials.playservices.controllers.CredentialProviderBaseController
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.Identity
import com.google.android.gms.auth.api.identity.SavePasswordRequest
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.fido.Fido
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
/**
* An activity used to ensure all required API versions work as intended.
* @hide
*/
@Suppress("Deprecation", "ForbiddenSuperClass")
open class HiddenActivity : Activity() {
private var resultReceiver: ResultReceiver? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
overridePendingTransition(0, 0)
val type: String? = intent.getStringExtra(CredentialProviderBaseController.TYPE_TAG)
resultReceiver = intent.getParcelableExtra(
CredentialProviderBaseController.RESULT_RECEIVER_TAG)
if (resultReceiver == null) {
finish()
}
when (type) {
CredentialProviderBaseController.BEGIN_SIGN_IN_TAG -> {
handleBeginSignIn()
}
CredentialProviderBaseController.CREATE_PASSWORD_TAG -> {
handleCreatePassword()
}
CredentialProviderBaseController.CREATE_PUBLIC_KEY_CREDENTIAL_TAG -> {
handleCreatePublicKeyCredential()
} else -> {
Log.w(TAG, "Activity handed an unsupported type")
finish()
}
}
}
private fun handleCreatePublicKeyCredential() {
val fidoRegistrationRequest: PublicKeyCredentialCreationOptions? = intent
.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
val requestCode: Int = intent.getIntExtra(
CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
DEFAULT_VALUE)
fidoRegistrationRequest?.let {
Fido.getFido2ApiClient(this)
.getRegisterPendingIntent(fidoRegistrationRequest)
.addOnSuccessListener { result: PendingIntent ->
try {
startIntentSenderForResult(
result.intentSender,
requestCode,
null, /* fillInIntent= */
0, /* flagsMask= */
0, /* flagsValue= */
0, /* extraFlags= */
null /* options= */
)
} catch (e: IntentSender.SendIntentException) {
setupFailure(resultReceiver!!,
CreateCredentialUnknownException::class.java.name,
"During public key credential, found IntentSender " +
"failure on public key creation: ${e.message}")
}
}
.addOnFailureListener { e: Exception ->
var errName: String = CreateCredentialUnknownException::class.java.name
if (e is ApiException && e.statusCode in
CredentialProviderBaseController.retryables) {
errName = CreateCredentialInterruptedException::class.java.name
}
setupFailure(resultReceiver!!, errName,
"During create public key credential, fido registration " +
"failure: ${e.message}")
}
} ?: run {
Log.w(TAG, "During create public key credential, request is null, so nothing to " +
"launch for public key credentials")
finish()
}
}
private fun setupFailure(resultReceiver: ResultReceiver, errName: String, errMsg: String) {
val bundle = Bundle()
bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, true)
bundle.putString(CredentialProviderBaseController.EXCEPTION_TYPE_TAG, errName)
bundle.putString(CredentialProviderBaseController.EXCEPTION_MESSAGE_TAG, errMsg)
resultReceiver.send(Integer.MAX_VALUE, bundle)
finish()
}
private fun handleBeginSignIn() {
val params: BeginSignInRequest? = intent.getParcelableExtra(
CredentialProviderBaseController.REQUEST_TAG)
val requestCode: Int = intent.getIntExtra(
CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
DEFAULT_VALUE)
params?.let {
Identity.getSignInClient(this).beginSignIn(params).addOnSuccessListener {
try {
startIntentSenderForResult(
it.pendingIntent.intentSender,
requestCode,
null,
0,
0,
0,
null
)
} catch (e: IntentSender.SendIntentException) {
setupFailure(resultReceiver!!,
GetCredentialUnknownException::class.java.name,
"During begin sign in, one tap ui intent sender " +
"failure: ${e.message}")
}
}.addOnFailureListener { e: Exception ->
var errName: String = NoCredentialException::class.java.name
if (e is ApiException && e.statusCode in
CredentialProviderBaseController.retryables) {
errName = GetCredentialInterruptedException::class.java.name
}
setupFailure(resultReceiver!!, errName,
"During begin sign in, failure response from one tap: ${e.message}")
}
} ?: run {
Log.i(TAG, "During begin sign in, params is null, nothing to launch for " +
"begin sign in")
finish()
}
}
private fun handleCreatePassword() {
val params: SavePasswordRequest? = intent.getParcelableExtra(
CredentialProviderBaseController.REQUEST_TAG)
val requestCode: Int = intent.getIntExtra(
CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
DEFAULT_VALUE)
params?.let {
Identity.getCredentialSavingClient(this).savePassword(params)
.addOnSuccessListener {
try {
startIntentSenderForResult(
it.pendingIntent.intentSender,
requestCode,
null,
0,
0,
0,
null
)
} catch (e: IntentSender.SendIntentException) {
setupFailure(resultReceiver!!,
GetCredentialUnknownException::class.java.name,
"During save password, found UI intent sender " +
"failure: ${e.message}")
}
}.addOnFailureListener { e: Exception ->
var errName: String = CreateCredentialUnknownException::class.java.name
if (e is ApiException && e.statusCode in
CredentialProviderBaseController.retryables) {
errName = CreateCredentialInterruptedException::class.java.name
}
setupFailure(resultReceiver!!, errName, "During save password, found " +
"password failure response from one tap ${e.message}")
}
} ?: run {
Log.i(TAG, "During save password, params is null, nothing to launch for create" +
" password")
finish()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val bundle = Bundle()
bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
resultReceiver?.send(resultCode, bundle)
finish()
}
companion object {
private const val DEFAULT_VALUE: Int = 1
private val TAG: String = HiddenActivity::class.java.name
}
}