PendingIntentHandler.kt
/*
* 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.provider
import android.app.PendingIntent
import android.content.Intent
import android.credentials.CreateCredentialResponse
import android.service.credentials.BeginGetCredentialRequest
import android.service.credentials.CreateCredentialRequest
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.service.credentials.CredentialEntry
import android.service.credentials.BeginGetCredentialResponse
import android.service.credentials.BeginCreateCredentialResponse
import androidx.annotation.RequiresApi
import androidx.credentials.GetCredentialResponse
import android.app.Activity
import androidx.credentials.exceptions.CreateCredentialException
import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.provider.utils.BeginGetCredentialUtil
/**
* PendingIntentHandler to be used by credential providers to extract requests from
* [PendingIntent] invoked when a given [CreateEntry], or a [CustomCredentialEntry]
* is selected by the user.
*
* This handler can also be used to set [android.credentials.CreateCredentialResponse] and
* [android.credentials.GetCredentialResponse] on the result of the activity
* invoked by the [PendingIntent]
*/
@RequiresApi(34)
class PendingIntentHandler {
companion object {
private const val TAG = "PendingIntentHandler"
/**
* Extracts the [ProviderCreateCredentialRequest] from the provider's
* [PendingIntent] invoked by the Android system.
*
* @param intent the intent associated with the [Activity] invoked through the
* [PendingIntent]
*
* @throws NullPointerException If [intent] is null
*/
@JvmStatic
fun retrieveProviderCreateCredentialRequest(intent: Intent):
ProviderCreateCredentialRequest? {
val frameworkReq: CreateCredentialRequest? =
intent.getParcelableExtra(
CredentialProviderService
.EXTRA_CREATE_CREDENTIAL_REQUEST, CreateCredentialRequest::class.java
)
if (frameworkReq == null) {
Log.i(TAG, "Request not found in pendingIntent")
return frameworkReq
}
return ProviderCreateCredentialRequest(
androidx.credentials.CreateCredentialRequest
.createFrom(
frameworkReq.type,
frameworkReq.data,
frameworkReq.data,
requireSystemProvider = false,
frameworkReq.callingAppInfo.origin) ?: return null,
frameworkReq.callingAppInfo)
}
/**
* Extracts the [BeginGetCredentialRequest] from the provider's
* [PendingIntent] invoked by the Android system when the user
* selects an [AuthenticationAction].
*
* @param intent the intent associated with the [Activity] invoked through the
* [PendingIntent]
*
* @throws NullPointerException If [intent] is null
*/
@JvmStatic
fun retrieveBeginGetCredentialRequest(intent: Intent): BeginGetCredentialRequest? {
val request = intent.getParcelableExtra(
"android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST",
BeginGetCredentialRequest::class.java
)
return request?.let { BeginGetCredentialUtil.convertToStructuredRequest(it) }
}
/**
* Sets the [CreateCredentialResponse] on the result of the
* activity invoked by the [PendingIntent] set on a
* [CreateEntry].
*
* @param intent the intent to be set on the result of the [Activity] invoked through the
* [PendingIntent]
* @param response the response to be set as an extra on the [intent]
*
* @throws NullPointerException If [intent], or [response] is null
*/
@JvmStatic
fun setCreateCredentialResponse(
intent: Intent,
response: androidx.credentials.CreateCredentialResponse
) {
intent.putExtra(
CredentialProviderService.EXTRA_CREATE_CREDENTIAL_RESPONSE,
CreateCredentialResponse(response.data))
}
/**
* Extracts the [ProviderGetCredentialRequest] from the provider's
* [PendingIntent] invoked by the Android system, when the user selects a
* [CredentialEntry].
*
* @param intent the intent associated with the [Activity] invoked through the
* [PendingIntent]
*
* @throws NullPointerException If [intent] is null
*/
@JvmStatic
fun retrieveProviderGetCredentialRequest(intent: Intent):
ProviderGetCredentialRequest? {
val frameworkReq = intent.getParcelableExtra(
CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
android.service.credentials.GetCredentialRequest::class.java
)
if (frameworkReq == null) {
Log.i(TAG, "Get request from framework is null")
return null
}
return ProviderGetCredentialRequest.createFrom(frameworkReq)
}
/**
* Sets the [android.credentials.GetCredentialResponse] on the result of the
* activity invoked by the [PendingIntent], set on a [CreateEntry].
*
* @param intent the intent to be set on the result of the [Activity] invoked through the
* [PendingIntent]
* @param response the response to be set as an extra on the [intent]
*
* @throws NullPointerException If [intent], or [response] is null
*/
@JvmStatic
fun setGetCredentialResponse(
intent: Intent,
response: GetCredentialResponse
) {
intent.putExtra(
CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
android.credentials.GetCredentialResponse(
android.credentials.Credential(response.credential.type,
response.credential.data))
)
}
/**
* Sets the [android.service.credentials.BeginGetCredentialResponse] on the result of the
* activity invoked by the [PendingIntent], set on an [AuthenticationAction].
*
* @param intent the intent to be set on the result of the [Activity] invoked through the
* [PendingIntent]
* @param response the response to be set as an extra on the [intent]
*
* @throws NullPointerException If [intent], or [response] is null
*/
@JvmStatic
fun setBeginGetCredentialResponse(
intent: Intent,
response: BeginGetCredentialResponse
) {
intent.putExtra(
CredentialProviderService.EXTRA_BEGIN_GET_CREDENTIAL_RESPONSE,
response
)
}
/**
* Sets the [androidx.credentials.exceptions.GetCredentialException] if an error is
* encountered during the final phase of the get credential flow.
*
* A credential provider service returns a list of [CredentialEntry] as part of
* the [BeginGetCredentialResponse] to the query phase of the get-credential flow.
* If the user selects one of these entries, the corresponding [PendingIntent]
* is fired and the provider's activity is invoked.
* If there is an error encountered during the lifetime of that activity, the provider
* must use this API to set an exception before finishing this activity.
*
* @param intent the intent to be set on the result of the [Activity] invoked through the
* [PendingIntent]
* @param exception the exception to be set as an extra to the [intent]
*
* @throws NullPointerException If [intent], or [exception] is null
*/
@JvmStatic
fun setGetCredentialException(
intent: Intent,
exception: GetCredentialException
) {
intent.putExtra(
CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
android.credentials.GetCredentialException(exception.type, exception.message)
)
}
/**
* Sets the [androidx.credentials.exceptions.CreateCredentialException] if an error is
* encountered during the final phase of the create credential flow.
*
* A credential provider service returns a list of [CreateEntry] as part of
* the [BeginCreateCredentialResponse] to the query phase of the get-credential flow.
*
* If the user selects one of these entries, the corresponding [PendingIntent]
* is fired and the provider's activity is invoked. If there is an error encountered
* during the lifetime of that activity, the provider must use this API to set
* an exception before finishing the activity.
*
* @param intent the intent to be set on the result of the [Activity] invoked through the
* [PendingIntent]
* @param exception the exception to be set as an extra to the [intent]
*
* @throws NullPointerException If [intent], or [exception] is null
*/
@JvmStatic
fun setCreateCredentialException(
intent: Intent,
exception: CreateCredentialException
) {
intent.putExtra(
CredentialProviderService.EXTRA_CREATE_CREDENTIAL_EXCEPTION,
android.credentials.CreateCredentialException(exception.type, exception.message)
)
}
}
}