Use mDNS to get the IP and port of ttyd

Note that the IP address is still statically assigned, though Terminal
app doesn't rely on that.

Bug: 373533555
Test: run terminal
Change-Id: Ie11f6114f89bbabd7abb1aa15b5201fcbfa3af12
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index 016af83..3fea0dc 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -28,11 +28,12 @@
 import android.graphics.fonts.FontStyle;
 import android.net.Uri;
 import android.net.http.SslError;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.ConditionVariable;
 import android.os.Environment;
-import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.util.Log;
@@ -62,10 +63,8 @@
 import com.google.android.material.appbar.MaterialToolbar;
 
 import java.io.IOException;
-import java.net.InetAddress;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.UnknownHostException;
 import java.security.KeyStore;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
@@ -77,8 +76,6 @@
         implements VmLauncherService.VmLauncherServiceCallback, AccessibilityStateChangeListener {
     static final String TAG = "VmTerminalApp";
     static final String KEY_DISK_SIZE = "disk_size";
-    private static final String VM_ADDR = "192.168.0.2";
-    private static final int TTYD_PORT = 7681;
     private static final int TERMINAL_CONNECTION_TIMEOUT_MS;
     private static final int REQUEST_CODE_INSTALLER = 0x33;
     private static final int FONT_SIZE_DEFAULT = 13;
@@ -234,7 +231,7 @@
         activityResultLauncher.launch(intent);
     }
 
-    private URL getTerminalServiceUrl() {
+    private URL getTerminalServiceUrl(String ipAddress, int port) {
         Configuration config = getResources().getConfiguration();
 
         String query =
@@ -249,8 +246,9 @@
                         + "&titleFixed="
                         + getString(R.string.app_name);
 
+
         try {
-            return new URL("https", VM_ADDR, TTYD_PORT, "/" + query);
+            return new URL("https", ipAddress, port, "/" + query);
         } catch (MalformedURLException e) {
             // this cannot happen
             return null;
@@ -266,7 +264,6 @@
     }
 
     private void connectToTerminalService() {
-        Log.i(TAG, "URL=" + getTerminalServiceUrl().toString());
         mTerminalView.setWebViewClient(
                 new WebViewClient() {
                     private boolean mLoadFailed = false;
@@ -344,46 +341,38 @@
                         handler.proceed();
                     }
                 });
-        mExecutorService.execute(
-                () -> {
-                    // TODO(b/376793781): Remove polling
-                    waitUntilVmStarts();
-                    runOnUiThread(() -> mTerminalView.loadUrl(getTerminalServiceUrl().toString()));
+
+        // TODO: refactor this block as a method
+        NsdManager nsdManager = getSystemService(NsdManager.class);
+        NsdServiceInfo info = new NsdServiceInfo();
+        info.setServiceType("_http._tcp");
+        info.setServiceName("ttyd");
+        nsdManager.registerServiceInfoCallback(
+                info,
+                mExecutorService,
+                new NsdManager.ServiceInfoCallback() {
+                    @Override
+                    public void onServiceInfoCallbackRegistrationFailed(int errorCode) {}
+
+                    @Override
+                    public void onServiceInfoCallbackUnregistered() {}
+
+                    @Override
+                    public void onServiceLost() {}
+
+                    @Override
+                    public void onServiceUpdated(NsdServiceInfo info) {
+                        nsdManager.unregisterServiceInfoCallback(this);
+
+                        Log.i(TAG, "Service found: " + info.toString());
+                        String ipAddress = info.getHostAddresses().get(0).getHostAddress();
+                        int port = info.getPort();
+                        URL url = getTerminalServiceUrl(ipAddress, port);
+                        runOnUiThread(() -> mTerminalView.loadUrl(url.toString()));
+                    }
                 });
     }
 
-    private static void waitUntilVmStarts() {
-        InetAddress addr = null;
-        try {
-            addr = InetAddress.getByName(VM_ADDR);
-        } catch (UnknownHostException e) {
-            // this can never happen.
-        }
-        long startTime = SystemClock.elapsedRealtime();
-        while (true) {
-            int remainingTime =
-                    TERMINAL_CONNECTION_TIMEOUT_MS
-                            - (int) (SystemClock.elapsedRealtime() - startTime);
-
-            if (Thread.interrupted()) {
-                Log.d(TAG, "the waiting thread is interrupted");
-                return;
-            }
-            if (remainingTime <= 0) {
-                throw new RuntimeException("Connection to terminal timeout");
-            }
-            try {
-                // Note: this quits immediately if VM is unreachable.
-                if (addr.isReachable(remainingTime)) {
-                    return;
-                }
-            } catch (IOException e) {
-                // give up on network error
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
     @Override
     protected void onDestroy() {
         if (mExecutorService != null) {