[UI Lib] Simplify LocalAdapter logic
- Removed unnecessary use of Dynamic Proxy.
- Resolved methods during construction of class, instead of resolving
them each time a call is made on the proxy. Improves performance and
provides error to user earlier.
- BinderAdapterDelegate now implements SandboxedUiAdapter and we search
for the interface instead of implementation of it during construction.
Bug: 282918647
Test: ./gradlew :privacysandbox:ui:ui-tests:connectedAndroidTest
Change-Id: I74b82438152567513def86802ac77f0e6be06839
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
index 4722066..0fcb553 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
@@ -79,10 +79,26 @@
* [LocalAdapter] fetches UI from a provider living on same process as the client but on a
* different class loader.
*/
- private class LocalAdapter(private val adapterInterface: ISandboxedUiAdapter) :
+ private class LocalAdapter(adapterInterface: ISandboxedUiAdapter) :
SandboxedUiAdapter {
private val uiProviderBinder = adapterInterface.asBinder()
+ private val targetSessionClientClass = Class.forName(
+ SandboxedUiAdapter.SessionClient::class.java.name,
+ /* initialize = */ false,
+ uiProviderBinder.javaClass.classLoader
+ )
+
+ // The adapterInterface provided must have a openSession method on its class.
+ // Since the object itself has been instantiated on a different classloader, we
+ // need reflection to get hold of it.
+ private val openSessionMethod: Method = Class.forName(
+ SandboxedUiAdapter::class.java.name,
+ /*initialize=*/ false,
+ uiProviderBinder.javaClass.classLoader
+ ).getMethod("openSession", Context::class.java, IBinder::class.java, Int::class.java,
+ Int::class.java, Boolean::class.java, Executor::class.java, targetSessionClientClass)
+
@SuppressLint("BanUncheckedReflection") // using reflection on library classes
override fun openSession(
context: Context,
@@ -94,28 +110,13 @@
client: SandboxedUiAdapter.SessionClient
) {
try {
- // openSession call needs to be forwarded to uiProvider object instantiated
- // on a different classloader.
- val uiProviderClassLoader = uiProviderBinder.javaClass.classLoader
- val classOnUiProviderClassLoader = Class.forName(uiProviderBinder::class.java.name,
- /*initialize=*/false, uiProviderBinder.javaClass.classLoader)
- val openSessionMethod = classOnUiProviderClassLoader.methods.first {
- method -> method.name.equals("openSession")
- }
-
// We can't pass the client object as-is since it's been created on a different
// classloader.
- val targetSessionClientClass = Class.forName(
- SandboxedUiAdapter.SessionClient::class.java.name,
- /*initialize=*/ false,
- uiProviderClassLoader
- )
val sessionClientProxy = Proxy.newProxyInstance(
- uiProviderClassLoader,
+ uiProviderBinder.javaClass.classLoader,
arrayOf(targetSessionClientClass),
SessionClientProxyHandler(client)
)
-
openSessionMethod.invoke(uiProviderBinder, context, windowInputToken, initialWidth,
initialHeight, isZOrderOnTop, clientExecutor, sessionClientProxy)
} catch (exception: Throwable) {
@@ -123,51 +124,32 @@
}
}
- private inner class SessionClientProxyHandler(
+ private class SessionClientProxyHandler(
private val origClient: SandboxedUiAdapter.SessionClient,
) : InvocationHandler {
@SuppressLint("BanUncheckedReflection") // using reflection on library classes
- override fun invoke(proxy: Any, method: Method, args: Array<Any>?): Any? {
+ override fun invoke(proxy: Any, method: Method, args: Array<Any>?): Any {
return when (method.name) {
"onSessionOpened" -> {
- args!! // This method will always have an argument, so safe to !!
-
// We have to forward the call to original client, but it won't
- // recognize Session class on targetClassLoader. We need another proxy.
- val origSessionClass = Class.forName(
- SandboxedUiAdapter.Session::class.java.name,
- /*initialize=*/ false,
- origClient.javaClass.classLoader
- )
- val sessionProxy = Proxy.newProxyInstance(
- origClient.javaClass.classLoader,
- arrayOf(origSessionClass),
- SessionProxyHandler(args[0])
- )
-
- val methodOrig = origClient.javaClass.getMethod("onSessionOpened",
- SandboxedUiAdapter.Session::class.java)
- methodOrig.invoke(origClient, sessionProxy)
+ // recognize Session class on targetClassLoader. We need proxy for it
+ // on local ClassLoader.
+ args!! // This method will always have an argument, so safe to !!
+ origClient.onSessionOpened(SessionProxy(args[0]))
}
"onSessionError" -> {
args!! // This method will always have an argument, so safe to !!
-
val throwable = args[0] as Throwable
- val methodOrig = origClient.javaClass.getMethod("onSessionError",
- Throwable::class.java)
- methodOrig.invoke(origClient, throwable)
+ origClient.onSessionError(throwable)
}
"onResizeRequested" -> {
args!! // This method will always have an argument, so safe to !!
-
- val methodOrig = origClient.javaClass.getMethod("onResizeRequested",
- Int::class.java, Int::class.java)
- methodOrig.invoke(origClient, args[0], args[1])
+ val width = args[0] as Int
+ val height = args[1] as Int
+ origClient.onResizeRequested(width, height)
}
- "toString" -> {
- origClient.javaClass.getMethod("toString").invoke(origClient)
- }
+ "toString" -> origClient.toString()
else -> {
// TODO(b/282918647): Implement other methods required
throw UnsupportedOperationException(
@@ -179,52 +161,51 @@
}
/**
- * Create [SandboxedUiAdapter.Session] on [targetClassLoader] that proxies to [origClient]
+ * Create [SandboxedUiAdapter.Session] that proxies to [origSession]
*/
- private inner class SessionProxyHandler(
- private val origClient: Any,
- ) : InvocationHandler {
+ private class SessionProxy(
+ private val origSession: Any,
+ ) : SandboxedUiAdapter.Session {
+
+ private val targetClass = Class.forName(
+ SandboxedUiAdapter.Session::class.java.name,
+ /* initialize = */ false,
+ origSession.javaClass.classLoader
+ ).also {
+ it.cast(origSession)
+ }
+
+ private val getViewMethod = targetClass.getMethod("getView")
+ private val notifyResizedMethod = targetClass.getMethod(
+ "notifyResized", Int::class.java, Int::class.java)
+ private val notifyZOrderChangedMethod =
+ targetClass.getMethod("notifyZOrderChanged", Boolean::class.java)
+ private val notifyConfigurationChangedMethod = targetClass.getMethod(
+ "notifyConfigurationChanged", Configuration::class.java)
+ private val closeMethod = targetClass.getMethod("close")
+
+ override val view: View
+ @SuppressLint("BanUncheckedReflection") // using reflection on library classes
+ get() = getViewMethod.invoke(origSession) as View
@SuppressLint("BanUncheckedReflection") // using reflection on library classes
- override fun invoke(proxy: Any, method: Method, args: Array<Any>?): Any? {
- return when (method.name) {
- "close" -> {
- origClient.javaClass.getMethod("close").invoke(origClient)
- }
- "getView" -> {
- origClient.javaClass.getMethod("getView").invoke(origClient)
- }
- "notifyResized" -> {
- args!! // This method will always have an argument, so safe to !!
+ override fun notifyResized(width: Int, height: Int) {
+ notifyResizedMethod.invoke(origSession, width, height)
+ }
- val methodOrig = origClient.javaClass.getMethod("notifyResized",
- Int::class.java, Int::class.java)
- methodOrig.invoke(origClient, args[0], args[1])
- }
- "notifyZOrderChanged" -> {
- args!! // This method will always have an argument, so safe to !!
+ @SuppressLint("BanUncheckedReflection") // using reflection on library classes
+ override fun notifyZOrderChanged(isZOrderOnTop: Boolean) {
+ notifyZOrderChangedMethod.invoke(origSession, isZOrderOnTop)
+ }
- val methodOrig = origClient.javaClass.getMethod("notifyZOrderChanged",
- Boolean::class.java)
- methodOrig.invoke(origClient, args[0])
- }
- "notifyConfigurationChanged" -> {
- args!! // This method will always have an argument, so safe to !!
+ @SuppressLint("BanUncheckedReflection") // using reflection on library classes
+ override fun notifyConfigurationChanged(configuration: Configuration) {
+ notifyConfigurationChangedMethod.invoke(origSession, configuration)
+ }
- val methodOrig = origClient.javaClass.getMethod(
- "notifyConfigurationChanged", Configuration::class.java)
- methodOrig.invoke(origClient, args[0])
- }
- "toString" -> {
- origClient.javaClass.getMethod("toString").invoke(origClient)
- }
- else -> {
- // TODO(b/282918647): Implement other methods required
- throw UnsupportedOperationException(
- "Unexpected method call object:$proxy, method: $method, args: $args"
- )
- }
- }
+ @SuppressLint("BanUncheckedReflection") // using reflection on library classes
+ override fun close() {
+ closeMethod.invoke(origSession)
}
}
}
diff --git a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
index 77138eb..5134d71 100644
--- a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
+++ b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
@@ -55,14 +55,14 @@
private class BinderAdapterDelegate(
private val sandboxContext: Context,
private val adapter: SandboxedUiAdapter
-) : ISandboxedUiAdapter.Stub() {
+) : ISandboxedUiAdapter.Stub(), SandboxedUiAdapter {
companion object {
private const val TAG = "BinderAdapterDelegate"
private const val FRAME_TIMEOUT_MILLIS = 1000.toLong()
}
- fun openSession(
+ override fun openSession(
context: Context,
windowInputToken: IBinder,
initialWidth: Int,