Define DebianService for host-guest communication

1. Use TCP/IP socket for now
2. IP address reporter uses that
3. Refactoring: remove timeout for shell page because systemd unit for
   ip addr reporter 'requires' ttyd
4. move source files for guest(debian) to guest dir

Bug: 372666638
Test: check if shell shows
Change-Id: Ida94a05de9998a06b4b5c7efebbae97c78617bf4
diff --git a/libs/debian_service/Android.bp b/libs/debian_service/Android.bp
new file mode 100644
index 0000000..0495825
--- /dev/null
+++ b/libs/debian_service/Android.bp
@@ -0,0 +1,56 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+JAVA_LITE_PROTO_CMD = "mkdir -p $(genDir)/gen && " +
+    "$(location aprotoc) --java_opt=annotate_code=false " +
+    "-Iexternal/protobuf/src " +
+    "-Ipackages/modules/Virtualization/libs/debian_service/proto " +
+    "--plugin=protoc-gen-grpc-java=$(location protoc-gen-grpc-java-plugin) " +
+    "--grpc-java_out=lite:$(genDir)/gen $(in) && " +
+    "$(location soong_zip) -o $(out) -C $(genDir)/gen -D $(genDir)/gen"
+
+java_genrule {
+    name: "debian-service-stub-lite",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-java-plugin",
+        "soong_zip",
+    ],
+    cmd: JAVA_LITE_PROTO_CMD,
+    srcs: [
+        "proto/*.proto",
+        ":libprotobuf-internal-protos",
+    ],
+    out: [
+        "protos.srcjar",
+    ],
+}
+
+java_library {
+    name: "debian-service-grpclib-lite",
+    proto: {
+        type: "lite",
+        include_dirs: [
+            "external/protobuf/src",
+            "external/protobuf/java",
+        ],
+    },
+    srcs: [
+        ":debian-service-stub-lite",
+        "proto/*.proto",
+        ":libprotobuf-internal-protos",
+    ],
+    libs: ["javax_annotation-api_1.3.2"],
+    static_libs: [
+        "libprotobuf-java-lite",
+        "grpc-java-core-android",
+        "grpc-java-okhttp-client-lite",
+        "guava",
+    ],
+    sdk_version: "current",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
+}
diff --git a/libs/debian_service/proto/DebianService.proto b/libs/debian_service/proto/DebianService.proto
new file mode 100644
index 0000000..5adf615
--- /dev/null
+++ b/libs/debian_service/proto/DebianService.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package com.android.virtualization.vmlauncher.proto;
+
+option java_package = "com.android.virtualization.vmlauncher.proto";
+option java_multiple_files = true;
+
+service DebianService {
+  rpc ReportVmIpAddr (IpAddr) returns (ReportVmIpAddrResponse) {}
+}
+
+message IpAddr {
+  string addr = 1;
+}
+
+message ReportVmIpAddrResponse {
+    bool success = 1;
+}
\ No newline at end of file
diff --git a/libs/vm_launcher_lib/Android.bp b/libs/vm_launcher_lib/Android.bp
index cb6fc9e..5267508 100644
--- a/libs/vm_launcher_lib/Android.bp
+++ b/libs/vm_launcher_lib/Android.bp
@@ -12,6 +12,7 @@
     platform_apis: true,
     static_libs: [
         "gson",
+        "debian-service-grpclib-lite",
     ],
     libs: [
         "framework-virtualization.impl",
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
new file mode 100644
index 0000000..2f7666a
--- /dev/null
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.virtualization.vmlauncher;
+
+import android.util.Log;
+
+import com.android.virtualization.vmlauncher.proto.DebianServiceGrpc;
+import com.android.virtualization.vmlauncher.proto.IpAddr;
+import com.android.virtualization.vmlauncher.proto.ReportVmIpAddrResponse;
+
+import io.grpc.stub.StreamObserver;
+
+class DebianServiceImpl extends DebianServiceGrpc.DebianServiceImplBase {
+    public static final String TAG = "DebianService";
+    private final DebianServiceCallback mCallback;
+
+    protected DebianServiceImpl(DebianServiceCallback callback) {
+        super();
+        mCallback = callback;
+    }
+
+    @Override
+    public void reportVmIpAddr(
+            IpAddr request, StreamObserver<ReportVmIpAddrResponse> responseObserver) {
+        Log.d(DebianServiceImpl.TAG, "reportVmIpAddr: " + request.toString());
+        mCallback.onIpAddressAvailable(request.getAddr());
+        ReportVmIpAddrResponse reply = ReportVmIpAddrResponse.newBuilder().setSuccess(true).build();
+        responseObserver.onNext(reply);
+        responseObserver.onCompleted();
+    }
+
+    protected interface DebianServiceCallback {
+        void onIpAddressAvailable(String ipAddr);
+    }
+}
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
index 5e78f99..4cca110 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
@@ -22,25 +22,23 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.ParcelFileDescriptor;
 import android.os.ResultReceiver;
 import android.system.virtualmachine.VirtualMachine;
 import android.system.virtualmachine.VirtualMachineConfig;
 import android.system.virtualmachine.VirtualMachineException;
 import android.util.Log;
 
-import java.io.BufferedReader;
-import java.io.FileInputStream;
+import io.grpc.InsecureServerCredentials;
+import io.grpc.Server;
+import io.grpc.okhttp.OkHttpServerBuilder;
+
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.nio.file.Path;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-public class VmLauncherService extends Service {
+public class VmLauncherService extends Service implements DebianServiceImpl.DebianServiceCallback {
     private static final String TAG = "VmLauncherService";
     // TODO: this path should be from outside of this service
     private static final String VM_CONFIG_PATH = "/data/local/tmp/vm_config.json";
@@ -54,6 +52,7 @@
     private ExecutorService mExecutorService;
     private VirtualMachine mVirtualMachine;
     private ResultReceiver mResultReceiver;
+    private Server mServer;
 
     @Override
     public IBinder onBind(Intent intent) {
@@ -113,10 +112,9 @@
         startForeground();
 
         mResultReceiver.send(RESULT_START, null);
-        if (config.getCustomImageConfig().useNetwork()) {
-            Handler handler = new Handler(Looper.getMainLooper());
-            gatherIpAddrFromVm(handler);
-        }
+
+        startDebianServer();
+
         return START_NOT_STICKY;
     }
 
@@ -134,6 +132,7 @@
             mExecutorService = null;
             mVirtualMachine = null;
         }
+        stopDebianServer();
     }
 
     private boolean isVmRunning() {
@@ -141,34 +140,37 @@
                 && mVirtualMachine.getStatus() == VirtualMachine.STATUS_RUNNING;
     }
 
-    // TODO(b/359523803): Use AVF API to get ip addr when it exists
-    private void gatherIpAddrFromVm(Handler handler) {
-        handler.postDelayed(
-                () -> {
-                    if (!isVmRunning()) {
-                        Log.d(TAG, "A virtual machine instance isn't running");
-                        return;
-                    }
-                    int INTERNAL_VSOCK_SERVER_PORT = 1024;
-                    try (ParcelFileDescriptor pfd =
-                            mVirtualMachine.connectVsock(INTERNAL_VSOCK_SERVER_PORT)) {
-                        try (BufferedReader input =
-                                new BufferedReader(
-                                        new InputStreamReader(
-                                                new FileInputStream(pfd.getFileDescriptor())))) {
-                            String vmIpAddr = input.readLine().strip();
-                            Bundle b = new Bundle();
-                            b.putString(KEY_VM_IP_ADDR, vmIpAddr);
-                            mResultReceiver.send(RESULT_IPADDR, b);
-                            return;
-                        } catch (IOException e) {
-                            Log.e(TAG, e.toString());
-                        }
-                    } catch (Exception e) {
-                        Log.e(TAG, e.toString());
-                    }
-                    gatherIpAddrFromVm(handler);
-                },
-                1000);
+    private void startDebianServer() {
+        new Thread(
+                        () -> {
+                            // TODO(b/372666638): gRPC for java doesn't support vsock for now.
+                            // In addition, let's consider using a dynamic port and SSL(and client
+                            // certificate)
+                            int port = 12000;
+                            try {
+                                mServer =
+                                        OkHttpServerBuilder.forPort(
+                                                        port, InsecureServerCredentials.create())
+                                                .addService(new DebianServiceImpl(this))
+                                                .build()
+                                                .start();
+                            } catch (IOException e) {
+                                Log.d(TAG, "grpc server error", e);
+                            }
+                        })
+                .start();
+    }
+
+    private void stopDebianServer() {
+        if (mServer != null) {
+            mServer.shutdown();
+        }
+    }
+
+    @Override
+    public void onIpAddressAvailable(String ipAddr) {
+        Bundle b = new Bundle();
+        b.putString(VmLauncherService.KEY_VM_IP_ADDR, ipAddr);
+        mResultReceiver.send(VmLauncherService.RESULT_IPADDR, b);
     }
 }