SdkProviderV1.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.privacysandbox.sdkruntime.client.loader.impl
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.os.IBinder
import androidx.annotation.RestrictTo
import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
import androidx.privacysandbox.sdkruntime.client.loader.LocalSdkProvider
import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
/**
* Provides interface for interaction with locally loaded SDK with ApiVersion 1.
*
* @suppress
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal class SdkProviderV1 private constructor(
sdkProvider: Any,
private val onLoadSdkMethod: Method,
private val beforeUnloadSdkMethod: Method,
private val sandboxedSdkCompatBuilder: SandboxedSdkCompatBuilderV1,
private val loadSdkCompatExceptionBuilder: LoadSdkCompatExceptionBuilderV1
) : LocalSdkProvider(sdkProvider) {
@SuppressLint("BanUncheckedReflection") // using reflection on library classes
override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
try {
val rawResult = onLoadSdkMethod.invoke(sdkProvider, params)
return sandboxedSdkCompatBuilder.build(rawResult!!)
} catch (e: InvocationTargetException) {
throw loadSdkCompatExceptionBuilder.tryRebuildCompatException(e.targetException)
} catch (ex: Exception) {
throw LoadSdkCompatException(
LoadSdkCompatException.LOAD_SDK_INTERNAL_ERROR,
"Failed during onLoadSdk call",
ex
)
}
}
@SuppressLint("BanUncheckedReflection") // using reflection on library classes
override fun beforeUnloadSdk() {
beforeUnloadSdkMethod.invoke(sdkProvider)
}
internal class SandboxedSdkCompatBuilderV1 private constructor(
private val sdkInfo: SandboxedSdkInfo?,
private val getInterfaceMethod: Method
) {
@SuppressLint("BanUncheckedReflection") // calling method on SandboxedSdkCompat class
fun build(rawObject: Any): SandboxedSdkCompat {
val binder = getInterfaceMethod.invoke(rawObject) as IBinder
return SandboxedSdkCompat(binder, sdkInfo)
}
companion object {
fun create(
classLoader: ClassLoader,
sdkConfig: LocalSdkConfig
): SandboxedSdkCompatBuilderV1 {
val sandboxedSdkCompatClass = Class.forName(
SandboxedSdkCompat::class.java.name,
/* initialize = */ false,
classLoader
)
val getInterfaceMethod = sandboxedSdkCompatClass.getMethod("getInterface")
val sdkInfo = sdkInfo(sdkConfig)
return SandboxedSdkCompatBuilderV1(sdkInfo, getInterfaceMethod)
}
private fun sdkInfo(sdkConfig: LocalSdkConfig): SandboxedSdkInfo? {
return if (sdkConfig.versionMajor == null) {
null
} else {
SandboxedSdkInfo(sdkConfig.packageName, sdkConfig.versionMajor.toLong())
}
}
}
}
internal class LoadSdkCompatExceptionBuilderV1 private constructor(
private val getLoadSdkErrorCodeMethod: Method,
private val getExtraInformationMethod: Method
) {
@SuppressLint("BanUncheckedReflection") // calling method on LoadSdkCompatException class
fun tryRebuildCompatException(rawException: Throwable): Throwable {
if (rawException.javaClass.name != LoadSdkCompatException::class.java.name) {
return rawException
}
return try {
val loadSdkErrorCode = getLoadSdkErrorCodeMethod.invoke(rawException) as Int
val extraInformation = getExtraInformationMethod.invoke(rawException) as Bundle
LoadSdkCompatException(
loadSdkErrorCode,
rawException.message,
rawException.cause,
extraInformation
)
} catch (ex: Throwable) {
// failed to rebuild, just wrap original
LoadSdkCompatException(
LoadSdkCompatException.LOAD_SDK_INTERNAL_ERROR,
"Failed to rebuild exception with error ${ex.message}",
rawException
)
}
}
companion object {
fun create(classLoader: ClassLoader): LoadSdkCompatExceptionBuilderV1 {
val loadSdkCompatExceptionClass = Class.forName(
LoadSdkCompatException::class.java.name,
/* initialize = */ false,
classLoader
)
val getLoadSdkErrorCodeMethod = loadSdkCompatExceptionClass.getMethod(
"getLoadSdkErrorCode"
)
val getExtraInformationMethod = loadSdkCompatExceptionClass.getMethod(
"getExtraInformation"
)
return LoadSdkCompatExceptionBuilderV1(
getLoadSdkErrorCodeMethod,
getExtraInformationMethod
)
}
}
}
companion object {
@SuppressLint("BanUncheckedReflection") // calling method of SandboxedSdkProviderCompat
fun create(
classLoader: ClassLoader,
sdkConfig: LocalSdkConfig,
appContext: Context
): SdkProviderV1 {
val sdkProviderClass = Class.forName(
sdkConfig.entryPoint,
/* initialize = */ false,
classLoader
)
val attachContextMethod =
sdkProviderClass.getMethod("attachContext", Context::class.java)
val onLoadSdkMethod = sdkProviderClass.getMethod("onLoadSdk", Bundle::class.java)
val beforeUnloadSdkMethod = sdkProviderClass.getMethod("beforeUnloadSdk")
val sandboxedSdkCompatBuilder =
SandboxedSdkCompatBuilderV1.create(classLoader, sdkConfig)
val loadSdkCompatExceptionBuilder =
LoadSdkCompatExceptionBuilderV1.create(classLoader)
val sdkProvider = sdkProviderClass.getConstructor().newInstance()
val sandboxedSdkContextCompat = SandboxedSdkContextCompat(appContext, classLoader)
attachContextMethod.invoke(sdkProvider, sandboxedSdkContextCompat)
return SdkProviderV1(
sdkProvider,
onLoadSdkMethod,
beforeUnloadSdkMethod,
sandboxedSdkCompatBuilder,
loadSdkCompatExceptionBuilder
)
}
}
}