Harden clipboard handling

* Rename connectClipboardSharingServer() to more generic name.
* Use try-with-resource to manage pfd life cycle.
* Check the length of read bytes.
* Ensure onWindowFocusChanged() doesn't throw.

Bug: 348303697
Test: Launch FerrochromeApp and test clipboard
Change-Id: Ibf340a5b27f359ee3b22d693f1206399439bcf18
diff --git a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
index 694aa57..47a6fe3 100644
--- a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
@@ -17,8 +17,6 @@
 package com.android.virtualization.vmlauncher;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.ParcelFileDescriptor.AutoCloseInputStream;
-import static android.os.ParcelFileDescriptor.AutoCloseOutputStream;
 import static android.system.virtualmachine.VirtualMachineConfig.CPU_TOPOLOGY_MATCH_HOST;
 
 import android.Manifest.permission;
@@ -64,6 +62,8 @@
 
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -556,7 +556,7 @@
         Log.d(TAG, "destroyed");
     }
 
-    private static final int CLIPBOARD_SHARING_SERVER_PORT = 3580;
+    private static final int DATA_SHARING_SERVICE_PORT = 3580;
     private static final byte READ_CLIPBOARD_FROM_VM = 0;
     private static final byte WRITE_CLIPBOARD_TYPE_EMPTY = 1;
     private static final byte WRITE_CLIPBOARD_TYPE_TEXT_PLAIN = 2;
@@ -581,16 +581,9 @@
         return header.array();
     }
 
-    private ParcelFileDescriptor connectClipboardSharingServer() {
-        ParcelFileDescriptor pfd;
-        try {
-            // TODO(349702313): Consider when clipboard sharing server is started to run in VM.
-            pfd = mVirtualMachine.connectVsock(CLIPBOARD_SHARING_SERVER_PORT);
-        } catch (VirtualMachineException e) {
-            Log.d(TAG, "cannot connect to the clipboard sharing server", e);
-            return null;
-        }
-        return pfd;
+    private ParcelFileDescriptor connectDataSharingService() throws VirtualMachineException {
+        // TODO(349702313): Consider when clipboard sharing server is started to run in VM.
+        return mVirtualMachine.connectVsock(DATA_SHARING_SERVICE_PORT);
     }
 
     private boolean writeClipboardToVm() {
@@ -604,64 +597,58 @@
         byte[] header =
                 constructClipboardHeader(
                         WRITE_CLIPBOARD_TYPE_TEXT_PLAIN, text.getBytes().length + 1);
-        ParcelFileDescriptor pfd = connectClipboardSharingServer();
-        if (pfd == null) {
-            Log.d(TAG, "file descriptor of ClipboardSharingServer is null");
-            return false;
-        }
-        try (OutputStream stream = new AutoCloseOutputStream(pfd)) {
+        try (ParcelFileDescriptor pfd = connectDataSharingService();
+                OutputStream stream = new FileOutputStream(pfd.getFileDescriptor())) {
             stream.write(header);
             stream.write(text.getBytes());
             stream.write('\0');
-            stream.flush();
             Log.d(TAG, "successfully wrote clipboard data to the VM");
             return true;
-        } catch (IOException e) {
+        } catch (IOException | VirtualMachineException e) {
             Log.e(TAG, "failed to write clipboard data to the VM", e);
             return false;
         }
     }
 
+    private byte[] readExactly(InputStream stream, int len) throws IOException {
+        byte[] buf = stream.readNBytes(len);
+        if (buf.length != len) {
+            throw new IOException("Cannot read enough bytes");
+        }
+        return buf;
+    }
+
     private boolean readClipboardFromVm() {
         byte[] request = constructClipboardHeader(READ_CLIPBOARD_FROM_VM, 0);
-        ParcelFileDescriptor pfd = connectClipboardSharingServer();
-        if (pfd == null) {
-            Log.d(TAG, "file descriptor of ClipboardSharingServer is null");
-            return false;
-        }
-        try (OutputStream output = new AutoCloseOutputStream(pfd.dup())) {
-            output.write(request);
-            output.flush();
-            Log.d(TAG, "successfully send request to the VM for reading clipboard");
-        } catch (IOException e) {
-            Log.e(TAG, "failed to send request to the VM for read clipboard", e);
-            try {
-                pfd.close();
-            } catch (IOException err) {
-                Log.e(TAG, "failed to close file descriptor", err);
+        try (ParcelFileDescriptor pfd = connectDataSharingService()) {
+            try (OutputStream output = new FileOutputStream(pfd.getFileDescriptor())) {
+                output.write(request);
+                Log.d(TAG, "successfully send request to the VM for reading clipboard");
+            } catch (IOException e) {
+                Log.e(TAG, "failed to send request to the VM for read clipboard");
+                throw e;
             }
-            return false;
-        }
-
-        try (InputStream input = new AutoCloseInputStream(pfd)) {
-            ByteBuffer header = ByteBuffer.wrap(input.readNBytes(8));
-            header.order(ByteOrder.LITTLE_ENDIAN);
-            switch (header.get(0)) {
-                case WRITE_CLIPBOARD_TYPE_EMPTY:
-                    Log.d(TAG, "clipboard data in VM is empty");
-                    return true;
-                case WRITE_CLIPBOARD_TYPE_TEXT_PLAIN:
-                    int dataSize = header.getInt(4);
-                    String text_data =
-                            new String(input.readNBytes(dataSize), StandardCharsets.UTF_8);
-                    getClipboardManager().setPrimaryClip(ClipData.newPlainText(null, text_data));
-                    Log.d(TAG, "successfully received clipboard data from VM");
-                    return true;
-                default:
-                    Log.e(TAG, "unknown clipboard response type");
-                    return false;
+            try (InputStream input = new FileInputStream(pfd.getFileDescriptor())) {
+                ByteBuffer header = ByteBuffer.wrap(readExactly(input, 8));
+                header.order(ByteOrder.LITTLE_ENDIAN);
+                switch (header.get(0)) {
+                    case WRITE_CLIPBOARD_TYPE_EMPTY:
+                        Log.d(TAG, "clipboard data in VM is empty");
+                        return true;
+                    case WRITE_CLIPBOARD_TYPE_TEXT_PLAIN:
+                        int dataSize = header.getInt(4);
+                        String text_data =
+                                new String(readExactly(input, dataSize), StandardCharsets.UTF_8);
+                        getClipboardManager()
+                                .setPrimaryClip(ClipData.newPlainText(null, text_data));
+                        Log.d(TAG, "successfully received clipboard data from VM");
+                        return true;
+                    default:
+                        Log.e(TAG, "unknown clipboard response type");
+                        return false;
+                }
             }
-        } catch (IOException e) {
+        } catch (IOException | VirtualMachineException e) {
             Log.e(TAG, "failed to receive clipboard content from the VM", e);
             return false;
         }
@@ -676,12 +663,16 @@
             surfaceView.requestPointerCapture();
         }
         if (mVirtualMachine != null) {
-            if (hasFocus) {
-                Log.d(TAG, "writing clipboard of host device into VM");
-                writeClipboardToVm();
-            } else {
-                Log.d(TAG, "reading clipboard of VM");
-                readClipboardFromVm();
+            try {
+                if (hasFocus) {
+                    Log.d(TAG, "writing clipboard of host device into VM");
+                    writeClipboardToVm();
+                } else {
+                    Log.d(TAG, "reading clipboard of VM");
+                    readClipboardFromVm();
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "read/write clipboard error", e);
             }
         }
     }