Use statically assigned IP for the VM
Ideally, we will use mDNS to resolve the IP address of the Debian OS VM.
However, currently, mDNS doesn't seem to work on tethered network.
Until then, let's statically assign IP to the VM. The assumption here is
that no multiple VMs with network access exist, which is a valid
assumption at least for now.
The Terminal app now doesn't wait to the message from the guest agent.
Instead, it waits for the static IP to become reachable, and also
retries when the terminal service fails to load.
In addition, the WebView is invisible in the beginning and becomes
visible only after the page is fully loaded.
Bug: N/A
Test: run the app.
Change-Id: Ibf084a9c6e15835394bf404ed1ddbcbd1e3b463c
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index 84c3eee..1fabf8d 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -25,11 +25,13 @@
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.webkit.WebChromeClient;
+import android.webkit.WebResourceError;
+import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
@@ -43,13 +45,20 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
public class MainActivity extends AppCompatActivity
implements VmLauncherServices.VmLauncherServiceCallback,
AccessibilityManager.TouchExplorationStateChangeListener {
+
private static final String TAG = "VmTerminalApp";
- private String mVmIpAddr;
+ private static final String VM_ADDR = "192.168.0.2";
+ private static final int TTYD_PORT = 7681;
private WebView mWebView;
+ private AccessibilityManager mAccessibilityManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -78,21 +87,73 @@
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebChromeClient(new WebChromeClient());
+
+ mAccessibilityManager = getSystemService(AccessibilityManager.class);
+ mAccessibilityManager.addTouchExplorationStateChangeListener(this);
+
+ connectToTerminalService();
+ }
+
+ private URL getTerminalServiceUrl() {
+ boolean needsAccessibility = mAccessibilityManager.isTouchExplorationEnabled();
+ String file = "/";
+ String query = needsAccessibility ? "?screenReaderMode=true" : "";
+
+ try {
+ return new URL("http", VM_ADDR, TTYD_PORT, file + query);
+ } catch (MalformedURLException e) {
+ // this cannot happen
+ return null;
+ }
+ }
+
+ private void connectToTerminalService() {
+ Log.i(TAG, "URL=" + getTerminalServiceUrl().toString());
mWebView.setWebViewClient(
new WebViewClient() {
@Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- view.loadUrl(url);
- return true;
+ public boolean shouldOverrideUrlLoading(
+ WebView view, WebResourceRequest request) {
+ return false;
+ }
+
+ @Override
+ public void onReceivedError(
+ WebView view, WebResourceRequest request, WebResourceError error) {
+ switch (error.getErrorCode()) {
+ case WebViewClient.ERROR_CONNECT:
+ case WebViewClient.ERROR_HOST_LOOKUP:
+ view.reload();
+ return;
+ default:
+ String url = request.getUrl().toString();
+ CharSequence msg = error.getDescription();
+ Log.e(TAG, "Failed to load " + url + ": " + msg);
+ }
}
@Override
public void onPageFinished(WebView view, String url) {
- android.os.Trace.endAsyncSection("executeTerminal", 0);
+ URL loadedUrl = null;
+ try {
+ loadedUrl = new URL(url);
+ } catch (MalformedURLException e) {
+ // cannot happen.
+ }
+ Log.i(TAG, "on page finished. URL=" + loadedUrl);
+ if (getTerminalServiceUrl().toString().equals(url)) {
+ android.os.Trace.endAsyncSection("executeTerminal", 0);
+ view.setVisibility(View.VISIBLE);
+ }
}
});
-
- getSystemService(AccessibilityManager.class).addTouchExplorationStateChangeListener(this);
+ new Thread(
+ () -> {
+ waitUntilVmStarts();
+ runOnUiThread(
+ () -> mWebView.loadUrl(getTerminalServiceUrl().toString()));
+ })
+ .start();
}
private void diskResize(Context context, long sizeInBytes) throws IOException {
@@ -179,6 +240,22 @@
}
}
+ private static void waitUntilVmStarts() {
+ InetAddress addr = null;
+ try {
+ addr = InetAddress.getByName(VM_ADDR);
+ } catch (UnknownHostException e) {
+ // this can never happen.
+ }
+ try {
+ while (!addr.isReachable(10000)) {}
+ } catch (IOException e) {
+ // give up on network error
+ throw new RuntimeException(e);
+ }
+ return;
+ }
+
@Override
protected void onDestroy() {
getSystemService(AccessibilityManager.class).removeTouchExplorationStateChangeListener(this);
@@ -186,23 +263,6 @@
super.onDestroy();
}
- private void gotoTerminalURL() {
- if (mVmIpAddr == null) {
- Log.d(TAG, "ip addr is not set yet");
- return;
- }
-
- boolean isTouchExplorationEnabled =
- getSystemService(AccessibilityManager.class).isTouchExplorationEnabled();
-
- String url =
- "http://"
- + mVmIpAddr
- + ":7681/"
- + (isTouchExplorationEnabled ? "?screenReaderMode=true" : "");
- runOnUiThread(() -> mWebView.loadUrl(url));
- }
-
@Override
public void onVmStart() {
Log.i(TAG, "onVmStart()");
@@ -224,9 +284,7 @@
@Override
public void onIpAddrAvailable(String ipAddr) {
- mVmIpAddr = ipAddr;
- ((TextView) findViewById(R.id.ip_addr_textview)).setText(mVmIpAddr);
- gotoTerminalURL();
+ // TODO: remove this
}
@Override
@@ -241,10 +299,11 @@
if (id == R.id.copy_ip_addr) {
// TODO(b/340126051): remove this menu item when port forwarding is supported.
getSystemService(ClipboardManager.class)
- .setPrimaryClip(ClipData.newPlainText("A VM's IP address", mVmIpAddr));
+ .setPrimaryClip(ClipData.newPlainText("A VM's IP address", VM_ADDR));
return true;
} else if (id == R.id.stop_vm) {
VmLauncherServices.stopVmLauncherService(this);
+ mWebView.setVisibility(View.INVISIBLE);
return true;
} else if (id == R.id.menu_item_settings) {
@@ -257,6 +316,6 @@
@Override
public void onTouchExplorationStateChanged(boolean enabled) {
- gotoTerminalURL();
+ connectToTerminalService();
}
}
diff --git a/android/TerminalApp/res/layout/activity_headless.xml b/android/TerminalApp/res/layout/activity_headless.xml
index f786a0f..736b45a 100644
--- a/android/TerminalApp/res/layout/activity_headless.xml
+++ b/android/TerminalApp/res/layout/activity_headless.xml
@@ -20,6 +20,7 @@
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginBottom="5dp" />
+ android:layout_marginBottom="5dp"
+ android:visibility="invisible"/>
</LinearLayout>
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index c3a3348..821a2ef 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -17,9 +17,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">Terminal</string>
- <string name="vm_creation_message">Virtual machine is booting. Please wait.</string>
- <string name="vm_stop_message">Virtual machine is stopped. Exiting.</string>
- <string name="vm_error_message">Virtual machine crashed. Exiting.</string>
+ <string name="vm_creation_message">Preparing terminal.</string>
+ <string name="vm_stop_message">Stopping terminal.</string>
+ <string name="vm_error_message">Terminal crashed.</string>
<string name="settings_disk_resize_title">Disk Resize</string>
<string name="settings_disk_resize_sub_title">Resize / Rootfs</string>
diff --git a/libs/service-virtualization/src/com/android/system/virtualmachine/VirtualizationSystemService.java b/libs/service-virtualization/src/com/android/system/virtualmachine/VirtualizationSystemService.java
index 241eef4..998389b 100644
--- a/libs/service-virtualization/src/com/android/system/virtualmachine/VirtualizationSystemService.java
+++ b/libs/service-virtualization/src/com/android/system/virtualmachine/VirtualizationSystemService.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.LinkAddress;
import android.net.TetheringManager;
import android.net.TetheringManager.StartTetheringCallback;
import android.net.TetheringManager.TetheringRequest;
@@ -157,8 +158,11 @@
@Override
public void enableVmTethering() {
+ LinkAddress local = new LinkAddress("192.168.0.1/24");
+ LinkAddress client = new LinkAddress("192.168.0.2/24");
final TetheringRequest tr =
new TetheringRequest.Builder(TetheringManager.TETHERING_VIRTUAL)
+ .setStaticIpv4Addresses(local, client)
.setConnectivityScope(TetheringManager.CONNECTIVITY_SCOPE_GLOBAL)
.build();