Add SurfacePackage rendering

Adds logic within CodeHolder to handle requesting
a View from loaded code, and passing this as a
SurfacePackage to SupplementalProcessManager. Each
SurfacePackage is allocated a unique id. When a
SurfacePackage is ready, SupplementalProcessManager
will call back into the calling package.

Test: atest SupplementalProcessTests
Bug: 204989872
Change-Id: I30f06482f91602d1a11d8f1f452bd09694557650
diff --git a/sdksandbox/SupplementalProcess/src/com/android/supplemental/process/CodeHolder.java b/sdksandbox/SupplementalProcess/src/com/android/supplemental/process/CodeHolder.java
index a968f3b..8907426 100644
--- a/sdksandbox/SupplementalProcess/src/com/android/supplemental/process/CodeHolder.java
+++ b/sdksandbox/SupplementalProcess/src/com/android/supplemental/process/CodeHolder.java
@@ -17,10 +17,19 @@
 package com.android.supplemental.process;
 
 import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.supplementalprocess.CodeProvider;
 import android.util.Log;
+import android.util.SparseArray;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.WindowManager;
+
+import java.security.SecureRandom;
+import java.util.Random;
 
 /**
  * A holder for loaded code.
@@ -34,6 +43,11 @@
     private CodeProvider mCode;
     private Context mContext;
 
+    private DisplayManager mDisplayManager;
+    private final Random mRandom = new SecureRandom();
+    private final SparseArray<SurfaceControlViewHost.SurfacePackage> mSurfacePackages =
+            new SparseArray<>();
+
     void init(Context context, Bundle params,
             ISupplementalProcessToSupplementalProcessManagerCallback callback,
             String codeProviderClassName, ClassLoader loader) {
@@ -43,6 +57,7 @@
         mInitialized = true;
         mCallback = callback;
         mContext = context;
+        mDisplayManager = mContext.getSystemService(DisplayManager.class);
         try {
             Class<?> clz = Class.forName(codeProviderClassName, true, loader);
             mCode = (CodeProvider) clz.getConstructor(Context.class).newInstance(mContext);
@@ -68,9 +83,7 @@
 
     private void sendLoadCodeSuccess() {
         try {
-            // TODO(b/204989872): return a
-            // ISupplementalProcessManagerToSupplementalProcessCallback from here
-            mCallback.onLoadCodeSuccess(new Bundle(), /*callback=*/null);
+            mCallback.onLoadCodeSuccess(new Bundle(), new SupplementalProcessCallbackImpl());
         } catch (RemoteException e) {
             Log.e(TAG, "Could not send onLoadCodeSuccess: " + e);
         }
@@ -86,4 +99,44 @@
             Log.e(TAG, "Could not send onLoadCodeError: " + e);
         }
     }
+
+    private int allocateSurfacePackageId(SurfaceControlViewHost.SurfacePackage surfacePackage) {
+        synchronized (mSurfacePackages) {
+            for (int i = 0; i < 32; i++) {
+                int id = mRandom.nextInt();
+                if (!mSurfacePackages.contains(id)) {
+                    mSurfacePackages.put(id, surfacePackage);
+                    return id;
+                }
+            }
+            throw new IllegalStateException("Could not allocate surfacePackageId");
+        }
+    }
+
+    private class SupplementalProcessCallbackImpl
+            extends ISupplementalProcessManagerToSupplementalProcessCallback.Stub {
+
+        @Override
+        public void onSurfacePackageRequested(IBinder token, int displayId, Bundle params) {
+            try {
+                Context displayContext = mContext.createDisplayContext(
+                        mDisplayManager.getDisplay(displayId));
+                // TODO(b/209009304): Support other window contexts?
+                Context windowContext = displayContext.createWindowContext(
+                        WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, null);
+                final View view = mCode.getView(windowContext, params);
+                SurfaceControlViewHost host = new SurfaceControlViewHost(windowContext,
+                        mDisplayManager.getDisplay(displayId), token);
+                host.setView(view, view.getWidth(), view.getHeight());
+                SurfaceControlViewHost.SurfacePackage surfacePackage  = host.getSurfacePackage();
+                int surfacePackageId = allocateSurfacePackageId(surfacePackage);
+                mCallback.onSurfacePackageReady(surfacePackage, surfacePackageId, params);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not send onSurfacePackageReady", e);
+            } catch (Throwable e) {
+                // TODO(b/209396156): pass error back to calling package.
+                Log.e(TAG, "Error thrown while getting surface package", e);
+            }
+        }
+    }
 }
diff --git a/sdksandbox/SupplementalProcess/tests/src/com/android/supplemental/process/SupplementalProcessTest.java b/sdksandbox/SupplementalProcess/tests/src/com/android/supplemental/process/SupplementalProcessTest.java
index d4e0c7e..428b57f 100644
--- a/sdksandbox/SupplementalProcess/tests/src/com/android/supplemental/process/SupplementalProcessTest.java
+++ b/sdksandbox/SupplementalProcess/tests/src/com/android/supplemental/process/SupplementalProcessTest.java
@@ -23,12 +23,14 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Process;
 import android.view.SurfaceControlViewHost;
 
 import androidx.test.InstrumentationRegistry;
 
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -44,6 +46,11 @@
     private ApplicationInfo mApplicationInfo;
     private static final String CODE_PROVIDER_CLASS = "com.android.testprovider.TestProvider";
 
+    @BeforeClass
+    public static void setupClass() {
+        // Required to create a SurfaceControlViewHost
+        Looper.prepare();
+    }
     @Before
     public void setup() throws Exception {
         mService = new FakeSupplementalProcessService();
@@ -92,13 +99,31 @@
         assertThat(mRemoteCode2.mSuccessful).isTrue();
     }
 
+    @Test
+    public void testRequestSurfacePackage() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        RemoteCode mRemoteCode = new RemoteCode(latch);
+        mService.loadCode(new Binder(), mApplicationInfo, CODE_PROVIDER_CLASS,
+                new Bundle(), mRemoteCode);
+        assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue();
+        CountDownLatch surfaceLatch = new CountDownLatch(1);
+        mRemoteCode.setLatch(surfaceLatch);
+        mRemoteCode.getCallback().onSurfacePackageRequested(new Binder(),
+                mContext.getDisplayId(), null);
+        assertThat(surfaceLatch.await(1, TimeUnit.MINUTES)).isTrue();
+        assertThat(mRemoteCode.mSurfacePackage).isNotNull();
+    }
+
     private static class RemoteCode
             extends ISupplementalProcessToSupplementalProcessManagerCallback.Stub {
 
-        private final CountDownLatch mLatch;
+        private CountDownLatch mLatch;
+        private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
         boolean mSuccessful = false;
         int mErrorCode = -1;
 
+        private ISupplementalProcessManagerToSupplementalProcessCallback mCallback;
+
         RemoteCode(CountDownLatch latch) {
             mLatch = latch;
         }
@@ -107,6 +132,7 @@
         public void onLoadCodeSuccess(Bundle params,
                 ISupplementalProcessManagerToSupplementalProcessCallback callback)  {
             mLatch.countDown();
+            mCallback = callback;
             mSuccessful = true;
         }
 
@@ -120,7 +146,18 @@
         @Override
         public void onSurfacePackageReady(
                 SurfaceControlViewHost.SurfacePackage surfacePackage,
-                int displayId, Bundle params) {}
+                int displayId, Bundle params) {
+            mLatch.countDown();
+            mSurfacePackage = surfacePackage;
+        }
+
+        private void setLatch(CountDownLatch latch) {
+            mLatch = latch;
+        }
+
+        private ISupplementalProcessManagerToSupplementalProcessCallback getCallback() {
+            return mCallback;
+        }
     }
 
     private static class FakeSupplementalProcessService extends SupplementalProcessServiceImpl {
diff --git a/sdksandbox/SupplementalProcess/tests/test-apps/TestProvider/src/com/android/testprovider/TestProvider.java b/sdksandbox/SupplementalProcess/tests/test-apps/TestProvider/src/com/android/testprovider/TestProvider.java
index 3124596..780cf7d 100644
--- a/sdksandbox/SupplementalProcess/tests/test-apps/TestProvider/src/com/android/testprovider/TestProvider.java
+++ b/sdksandbox/SupplementalProcess/tests/test-apps/TestProvider/src/com/android/testprovider/TestProvider.java
@@ -18,9 +18,8 @@
 
 import android.content.Context;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.supplementalprocess.CodeProvider;
-import android.view.SurfaceControlViewHost;
+import android.view.View;
 
 import java.util.concurrent.Executor;
 
@@ -36,9 +35,8 @@
     }
 
     @Override
-    public SurfaceControlViewHost.SurfacePackage getSurfacePackage(IBinder hostToken, int displayId,
-            Bundle params) {
-        return null;
+    public View getView(Context windowContext, Bundle params) {
+        return new View(windowContext);
     }
 
     @Override
diff --git a/sdksandbox/framework/java/android/supplementalprocess/CodeProvider.java b/sdksandbox/framework/java/android/supplementalprocess/CodeProvider.java
index 6381b4e..ae2410d 100644
--- a/sdksandbox/framework/java/android/supplementalprocess/CodeProvider.java
+++ b/sdksandbox/framework/java/android/supplementalprocess/CodeProvider.java
@@ -20,8 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Bundle;
-import android.os.IBinder;
-import android.view.SurfaceControlViewHost.SurfacePackage;
+import android.view.View;
 
 import java.util.concurrent.Executor;
 
@@ -48,8 +47,7 @@
      * Returns view that will be used for remote rendering.
      */
     @NonNull
-    public abstract SurfacePackage getSurfacePackage(
-            @NonNull IBinder hostToken, int displayId, @NonNull Bundle params);
+    public abstract View getView(Context windowContext, @NonNull Bundle params);
 
     /**
      * Called when extra data sent from the app is received by code.