Close lid upon suspend and open upon resume

This is a hack to force update the UI when the VM is resumed.

Bug: 348380730
Test: run a VM, go to a UI which is not auto-updated (ex: no cursor).
go out of the VM, and the come back to VM. see the UI is updated.

Change-Id: I7b49f3b68187ab69edb11e53ad82192eeecd6eb8
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachine.java b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
index 5febed2..f5ce102 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachine.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
@@ -172,6 +172,7 @@
     private ParcelFileDescriptor mTouchSock;
     private ParcelFileDescriptor mKeySock;
     private ParcelFileDescriptor mMouseSock;
+    private ParcelFileDescriptor mSwitchesSock;
 
     /**
      * Status of a virtual machine
@@ -921,6 +922,13 @@
                 m.pfd = pfds[1];
                 inputDevices.add(InputDevice.mouse(m));
             }
+            if (vmConfig.getCustomImageConfig().useSwitches()) {
+                ParcelFileDescriptor[] pfds = ParcelFileDescriptor.createSocketPair();
+                mSwitchesSock = pfds[0];
+                InputDevice.Switches s = new InputDevice.Switches();
+                s.pfd = pfds[1];
+                inputDevices.add(InputDevice.switches(s));
+            }
         }
         rawConfig.inputDevices = inputDevices.toArray(new InputDevice[0]);
 
@@ -1069,6 +1077,25 @@
                         new InputEvent(EV_SYN, SYN_REPORT, 0)));
     }
 
+    /** @hide */
+    public boolean sendLidEvent(boolean close) {
+        if (mSwitchesSock == null) {
+            Log.d(TAG, "mSwitcheSock == null");
+            return false;
+        }
+
+        // from include/uapi/linux/input-event-codes.h in the kernel.
+        short EV_SYN = 0x00;
+        short EV_SW = 0x05;
+        short SW_LID = 0x00;
+        short SYN_REPORT = 0x00;
+        return writeEventsToSock(
+                mSwitchesSock,
+                Arrays.asList(
+                        new InputEvent(EV_SW, SW_LID, close ? 1 : 0),
+                        new InputEvent(EV_SYN, SYN_REPORT, 0)));
+    }
+
     private boolean writeEventsToSock(ParcelFileDescriptor sock, List<InputEvent> evtList) {
         ByteBuffer byteBuffer =
                 ByteBuffer.allocate(8 /* (type: u16 + code: u16 + value: i32) */ * evtList.size());
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
index 8d4886a..7fbfb33 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
@@ -36,6 +36,7 @@
     private static final String KEY_TOUCH = "touch";
     private static final String KEY_KEYBOARD = "keyboard";
     private static final String KEY_MOUSE = "mouse";
+    private static final String KEY_SWITCHES = "switches";
     private static final String KEY_NETWORK = "network";
     private static final String KEY_GPU = "gpu";
 
@@ -49,6 +50,7 @@
     private final boolean touch;
     private final boolean keyboard;
     private final boolean mouse;
+    private final boolean switches;
     private final boolean network;
     @Nullable private final GpuConfig gpuConfig;
 
@@ -94,6 +96,10 @@
         return mouse;
     }
 
+    public boolean useSwitches() {
+        return switches;
+    }
+
     public boolean useNetwork() {
         return network;
     }
@@ -110,6 +116,7 @@
             boolean touch,
             boolean keyboard,
             boolean mouse,
+            boolean switches,
             boolean network,
             GpuConfig gpuConfig) {
         this.name = name;
@@ -122,6 +129,7 @@
         this.touch = touch;
         this.keyboard = keyboard;
         this.mouse = mouse;
+        this.switches = switches;
         this.network = network;
         this.gpuConfig = gpuConfig;
     }
@@ -187,6 +195,7 @@
         pb.putBoolean(KEY_TOUCH, touch);
         pb.putBoolean(KEY_KEYBOARD, keyboard);
         pb.putBoolean(KEY_MOUSE, mouse);
+        pb.putBoolean(KEY_SWITCHES, switches);
         pb.putBoolean(KEY_NETWORK, network);
         pb.putPersistableBundle(
                 KEY_GPU,
@@ -247,6 +256,7 @@
         private boolean touch;
         private boolean keyboard;
         private boolean mouse;
+        private boolean switches;
         private boolean network;
         private GpuConfig gpuConfig;
 
@@ -320,6 +330,12 @@
         }
 
         /** @hide */
+        public Builder useSwitches(boolean switches) {
+            this.switches = switches;
+            return this;
+        }
+
+        /** @hide */
         public Builder useNetwork(boolean network) {
             this.network = network;
             return this;
@@ -338,6 +354,7 @@
                     touch,
                     keyboard,
                     mouse,
+                    switches,
                     network,
                     gpuConfig);
         }
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 575af6b..2abf98c 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -798,6 +798,9 @@
         InputDevice::Mouse(mouse) => InputDeviceOption::Mouse(clone_file(
             mouse.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
         )?),
+        InputDevice::Switches(switches) => InputDeviceOption::Switches(clone_file(
+            switches.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
+        )?),
     })
 }
 /// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 102ac51..13c018b 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -208,6 +208,7 @@
     SingleTouch { file: File, width: u32, height: u32, name: Option<String> },
     Keyboard(File),
     Mouse(File),
+    Switches(File),
 }
 
 type VfioDevice = Strong<dyn IBoundDevice>;
@@ -1137,6 +1138,9 @@
                     height,
                     name.as_ref().map_or("".into(), |n| format!(",name={}", n))
                 ),
+                InputDeviceOption::Switches(file) => {
+                    format!("switches[path={}]", add_preserved_fd(&mut preserved_fds, file))
+                }
             });
         }
     }
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl
index 56c5b6d..5a7ed4a 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl
@@ -38,8 +38,15 @@
     parcelable Mouse {
         ParcelFileDescriptor pfd;
     }
+
+    // Switches input
+    parcelable Switches {
+        ParcelFileDescriptor pfd;
+    }
+
     SingleTouch singleTouch;
     EvDev evDev;
     Keyboard keyboard;
     Mouse mouse;
+    Switches switches;
 }
diff --git a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
index 054be51..a401de3 100644
--- a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
@@ -188,6 +188,7 @@
             customImageConfigBuilder.useTouch(true);
             customImageConfigBuilder.useKeyboard(true);
             customImageConfigBuilder.useMouse(true);
+            customImageConfigBuilder.useSwitches(true);
             customImageConfigBuilder.useNetwork(true);
 
             configBuilder.setCustomImageConfig(customImageConfigBuilder.build());
@@ -420,6 +421,7 @@
         super.onStop();
         if (mVirtualMachine != null) {
             try {
+                mVirtualMachine.sendLidEvent(/* close */ true);
                 mVirtualMachine.suspend();
             } catch (VirtualMachineException e) {
                 Log.e(TAG, "Failed to suspend VM" + e);
@@ -433,6 +435,7 @@
         if (mVirtualMachine != null) {
             try {
                 mVirtualMachine.resume();
+                mVirtualMachine.sendLidEvent(/* close */ false);
             } catch (VirtualMachineException e) {
                 Log.e(TAG, "Failed to resume VM" + e);
             }