VmLauncherApp supports the display from vm

Bug: 331708504
Test: check if the display shows
Change-Id: I18a70bb9520c91c8a26f5d64e982550b01351e4e
diff --git a/vmlauncher_app/Android.bp b/vmlauncher_app/Android.bp
index cd40448..06dcf7a 100644
--- a/vmlauncher_app/Android.bp
+++ b/vmlauncher_app/Android.bp
@@ -10,6 +10,10 @@
         "androidx-constraintlayout_constraintlayout",
         "androidx.appcompat_appcompat",
         "com.google.android.material_material",
+        // TODO(b/330257000): will be removed when binder RPC is used
+        "android.system.virtualizationservice_internal-java",
+        // TODO(b/331708504): will be removed when AVF framework handles surface
+        "libcrosvm_android_display_service-java",
     ],
     libs: [
         "framework-virtualization.impl",
diff --git a/vmlauncher_app/AndroidManifest.xml b/vmlauncher_app/AndroidManifest.xml
index de9d094..860c03f 100644
--- a/vmlauncher_app/AndroidManifest.xml
+++ b/vmlauncher_app/AndroidManifest.xml
@@ -7,7 +7,11 @@
     <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
     <application
         android:label="VmLauncherApp">
-        <activity android:name=".MainActivity" android:exported="true">
+        <activity android:name=".MainActivity"
+                  android:screenOrientation="landscape"
+                  android:configChanges="orientation|screenSize|keyboard|keyboardHidden|navigation|uiMode"
+                  android:theme="@style/MyTheme"
+                  android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
index 7c927c9..2af1e2f 100644
--- a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
@@ -20,6 +20,10 @@
 
 import android.app.Activity;
 import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.crosvm.ICrosvmAndroidDisplayService;
+import android.system.virtualizationservice_internal.IVirtualizationServiceInternal;
 import android.system.virtualmachine.VirtualMachineCustomImageConfig;
 import android.util.Log;
 import android.system.virtualmachine.VirtualMachine;
@@ -27,6 +31,11 @@
 import android.system.virtualmachine.VirtualMachineConfig;
 import android.system.virtualmachine.VirtualMachineException;
 import android.system.virtualmachine.VirtualMachineManager;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.WindowManager;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -108,6 +117,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        getWindow().setDecorFitsSystemWindows(false);
         setContentView(R.layout.activity_main);
         VirtualMachineCallback callback =
                 new VirtualMachineCallback() {
@@ -184,6 +194,70 @@
         } catch (VirtualMachineException e) {
             throw new RuntimeException(e);
         }
+
+        SurfaceView surfaceView = findViewById(R.id.surface_view);
+        surfaceView
+                .getHolder()
+                .addCallback(
+                        // TODO(b/331708504): it should be handled in AVF framework.
+                        new SurfaceHolder.Callback() {
+                            @Override
+                            public void surfaceCreated(SurfaceHolder holder) {
+                                Log.d(
+                                        TAG,
+                                        "surface size: "
+                                                + holder.getSurfaceFrame().flattenToString());
+                                Log.d(
+                                        TAG,
+                                        "ICrosvmAndroidDisplayService.setSurface("
+                                                + holder.getSurface()
+                                                + ")");
+                                runWithDisplayService(
+                                        (service) -> service.setSurface(holder.getSurface()));
+                            }
+
+                            @Override
+                            public void surfaceChanged(
+                                    SurfaceHolder holder, int format, int width, int height) {
+                                Log.d(TAG, "width: " + width + ", height: " + height);
+                            }
+
+                            @Override
+                            public void surfaceDestroyed(SurfaceHolder holder) {
+                                Log.d(TAG, "ICrosvmAndroidDisplayService.removeSurface()");
+                                runWithDisplayService((service) -> service.removeSurface());
+                            }
+                        });
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+        // Fullscreen:
+        WindowInsetsController windowInsetsController = surfaceView.getWindowInsetsController();
+        windowInsetsController.setSystemBarsBehavior(
+                WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
+        windowInsetsController.hide(WindowInsets.Type.systemBars());
+    }
+
+    @FunctionalInterface
+    public interface RemoteExceptionCheckedFunction<T> {
+        void apply(T t) throws RemoteException;
+    }
+
+    private void runWithDisplayService(
+            RemoteExceptionCheckedFunction<ICrosvmAndroidDisplayService> func) {
+        IVirtualizationServiceInternal vs =
+                IVirtualizationServiceInternal.Stub.asInterface(
+                        ServiceManager.getService("android.system.virtualizationservice"));
+        try {
+            assert vs != null;
+            Log.d(TAG, "wait for the service");
+            ICrosvmAndroidDisplayService service =
+                    ICrosvmAndroidDisplayService.Stub.asInterface(vs.waitDisplayService());
+            assert service != null;
+            func.apply(service);
+            Log.d(TAG, "job done");
+        } catch (Exception e) {
+            Log.d(TAG, "error", e);
+        }
     }
 
     /** Reads data from an input stream and posts it to the output data */
diff --git a/vmlauncher_app/res/layout/activity_main.xml b/vmlauncher_app/res/layout/activity_main.xml
index 5cbda78..6cc899f 100644
--- a/vmlauncher_app/res/layout/activity_main.xml
+++ b/vmlauncher_app/res/layout/activity_main.xml
@@ -7,12 +7,10 @@
     android:scrollbars="horizontal|vertical"
     android:textAlignment="textStart"
     tools:context=".MainActivity">
-
-    <LinearLayout
+    <SurfaceView
+        android:id="@+id/surface_view"
+        android:focusable="true"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical">
-
-    </LinearLayout>
+        android:layout_height="match_parent" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/vmlauncher_app/res/values/themes.xml b/vmlauncher_app/res/values/themes.xml
new file mode 100644
index 0000000..395f089
--- /dev/null
+++ b/vmlauncher_app/res/values/themes.xml
@@ -0,0 +1,14 @@
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <style name="MyTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar">
+        <item name="android:navigationBarColor">
+            @android:color/transparent
+        </item>
+        <item name="android:statusBarColor">
+            @android:color/transparent
+        </item>
+        <item name="android:windowLayoutInDisplayCutoutMode">
+            shortEdges
+        </item>
+    </style>
+</resources>