CredentialProviderFactory.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
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
/**
* Factory that returns the credential provider to be used by Credential Manager.
*
* @hide
*/
class CredentialProviderFactory {
companion object {
private const val TAG = "CredProviderFactory"
private const val MAX_CRED_MAN_PRE_FRAMEWORK_API_LEVEL = Build.VERSION_CODES.TIRAMISU
/** The metadata key to be used when specifying the provider class name in the
* android manifest file. */
private const val CREDENTIAL_PROVIDER_KEY = "androidx.credentials.CREDENTIAL_PROVIDER_KEY"
/**
* Returns the best available provider.
* Pre-U, the provider is determined by the provider library that the developer includes in
* the app. Developer must not add more than one provider library.
* Post-U, providers will be registered with the framework, and enabled by the user.
*/
fun getBestAvailableProvider(context: Context): CredentialProvider? {
if (Build.VERSION.SDK_INT <= MAX_CRED_MAN_PRE_FRAMEWORK_API_LEVEL) {
return tryCreatePreUOemProvider(context)
} else {
// TODO("Implement")
throw UnsupportedOperationException("Post-U not supported yet")
}
}
private fun tryCreatePreUOemProvider(context: Context): CredentialProvider? {
val classNames = getAllowedProvidersFromManifest(context)
if (classNames.isEmpty()) {
return null
} else {
return instantiatePreUProvider(classNames, context)
}
}
private fun instantiatePreUProvider(classNames: List<String>, context: Context):
CredentialProvider? {
var provider: CredentialProvider? = null
for (className in classNames) {
try {
val klass = Class.forName(className)
val p = klass.getConstructor(Context::class.java).newInstance(context) as
CredentialProvider
// TODO("Retrys and look into multiple constructors")
if (p.isAvailableOnDevice()) {
if (provider != null) {
Log.i(TAG, "Only one active OEM CredentialProvider allowed")
return null
}
provider = p
}
} catch (_: Throwable) {
}
}
return provider
}
@Suppress("deprecation")
private fun getAllowedProvidersFromManifest(context: Context): List<String> {
val packageInfo = context.packageManager
.getPackageInfo(
context.packageName, PackageManager.GET_META_DATA or
PackageManager.GET_SERVICES
)
val classNames = mutableListOf<String>()
if (packageInfo.services != null) {
for (serviceInfo in packageInfo.services) {
if (serviceInfo.metaData != null) {
val className = serviceInfo.metaData.getString(CREDENTIAL_PROVIDER_KEY)
if (className != null) {
classNames.add(className)
}
}
}
}
return classNames.toList()
}
}
}