Merge "generate_assets.sh for LinuxInstaller" into main
diff --git a/android/LinuxInstaller/linux_image_builder/commands b/android/LinuxInstaller/linux_image_builder/commands
deleted file mode 100644
index 4d27475..0000000
--- a/android/LinuxInstaller/linux_image_builder/commands
+++ /dev/null
@@ -1,11 +0,0 @@
-upload init.sh:/root
-upload vsock.py:/usr/local/bin
-upload /tmp/ttyd:/usr/local/bin
-upload ttyd.service:/etc/systemd/system
-upload vsockip.service:/etc/systemd/system
-chmod 0777:/root/init.sh
-firstboot-command "/root/init.sh"
-chmod 0644:/etc/systemd/system/vsockip.service
-chmod 0644:/etc/systemd/system/ttyd.service
-chmod 0777:/usr/local/bin/vsock.py
-chmod 0777:/usr/local/bin/ttyd
diff --git a/android/LinuxInstaller/linux_image_builder/init.sh b/android/LinuxInstaller/linux_image_builder/init.sh
deleted file mode 100644
index bec5ac5..0000000
--- a/android/LinuxInstaller/linux_image_builder/init.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-systemctl daemon-reload
-systemctl start ttyd && sudo systemctl enable ttyd
-systemctl start vsockip && sudo systemctl enable vsockip
diff --git a/android/LinuxInstaller/linux_image_builder/setup.sh b/android/LinuxInstaller/linux_image_builder/setup.sh
deleted file mode 100755
index 2883e61..0000000
--- a/android/LinuxInstaller/linux_image_builder/setup.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-
-pushd $(dirname $0) > /dev/null
-tempdir=$(mktemp -d)
-echo Get Debian image and dependencies...
-wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-arm64.raw -O ${tempdir}/debian.img
-wget https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.aarch64 -O ${tempdir}/ttyd
-
-echo Customize the image...
-virt-customize --commands-from-file <(sed "s|/tmp|$tempdir|g" commands) -a ${tempdir}/debian.img
-
-asset_dir=../assets/linux
-mkdir -p ${asset_dir}
-
-echo Copy files...
-
-pushd ${tempdir} > /dev/null
-tar czvS -f images.tar.gz debian.img
-popd > /dev/null
-mv ${tempdir}/images.tar.gz ${asset_dir}/images.tar.gz
-cp vm_config.json ${asset_dir}
-
-echo Calculating hash...
-hash=$(cat ${asset_dir}/images.tar.gz ${asset_dir}/vm_config.json | sha1sum | cut -d' ' -f 1)
-echo ${hash} > ${asset_dir}/hash
-
-popd > /dev/null
-echo Cleaning up...
-rm -rf ${tempdir}
\ No newline at end of file
diff --git a/android/LinuxInstaller/linux_image_builder/setup_x86_64.sh b/android/LinuxInstaller/linux_image_builder/setup_x86_64.sh
deleted file mode 100755
index c543b2a..0000000
--- a/android/LinuxInstaller/linux_image_builder/setup_x86_64.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-
-pushd $(dirname $0) > /dev/null
-tempdir=$(mktemp -d)
-echo Get Debian image and dependencies...
-wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.raw -O ${tempdir}/debian.img
-wget https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.x86_64 -O ${tempdir}/ttyd
-
-echo Customize the image...
-virt-customize --commands-from-file <(sed "s|/tmp|$tempdir|g" commands) -a ${tempdir}/debian.img
-
-asset_dir=../assets/linux
-mkdir -p ${asset_dir}
-
-echo Copy files...
-
-pushd ${tempdir} > /dev/null
-tar czvS -f images.tar.gz debian.img
-popd > /dev/null
-mv ${tempdir}/images.tar.gz ${asset_dir}/images.tar.gz
-cp vm_config.json ${asset_dir}
-
-echo Calculating hash...
-hash=$(cat ${asset_dir}/images.tar.gz ${asset_dir}/vm_config.json | sha1sum | cut -d' ' -f 1)
-echo ${hash} > ${asset_dir}/hash
-
-popd > /dev/null
-echo Cleaning up...
-rm -rf ${tempdir}
\ No newline at end of file
diff --git a/android/LinuxInstaller/linux_image_builder/ttyd.service b/android/LinuxInstaller/linux_image_builder/ttyd.service
deleted file mode 100644
index f71557d..0000000
--- a/android/LinuxInstaller/linux_image_builder/ttyd.service
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-Description=TTYD
-After=syslog.target
-After=network.target
-[Service]
-ExecStart=/usr/local/bin/ttyd -W screen -aAxR -S main login
-Type=simple
-Restart=always
-User=root
-Group=root
-[Install]
-WantedBy=multi-user.target
diff --git a/android/LinuxInstaller/linux_image_builder/vm_config.json b/android/LinuxInstaller/linux_image_builder/vm_config.json
deleted file mode 100644
index 21462b8..0000000
--- a/android/LinuxInstaller/linux_image_builder/vm_config.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "name": "debian",
- "disks": [
- {
- "image": "/data/local/tmp/debian.img",
- "partitions": [],
- "writable": true
- }
- ],
- "protected": false,
- "cpu_topology": "match_host",
- "platform_version": "~1.0",
- "memory_mib": 4096,
- "debuggable": true,
- "console_out": true,
- "connect_console": true,
- "console_input_device": "ttyS0",
- "network": true
-}
diff --git a/android/LinuxInstaller/linux_image_builder/vsock.py b/android/LinuxInstaller/linux_image_builder/vsock.py
deleted file mode 100644
index 292d953..0000000
--- a/android/LinuxInstaller/linux_image_builder/vsock.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python3
-
-import socket
-
-# Constants for vsock (from linux/vm_sockets.h)
-AF_VSOCK = 40
-SOCK_STREAM = 1
-VMADDR_CID_ANY = -1
-
-def get_local_ip():
- """Retrieves the first IPv4 address found on the system.
-
- Returns:
- str: The local IPv4 address, or '127.0.0.1' if no IPv4 address is found.
- """
-
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- try:
- s.connect(('8.8.8.8', 80))
- ip = s.getsockname()[0]
- except Exception:
- ip = '127.0.0.1'
- finally:
- s.close()
- return ip
-
-def main():
- PORT = 1024
-
- # Create a vsock socket
- server_socket = socket.socket(AF_VSOCK, SOCK_STREAM)
-
- # Bind the socket to the server address
- server_address = (VMADDR_CID_ANY, PORT)
- server_socket.bind(server_address)
-
- # Listen for incoming connections
- server_socket.listen(1)
- print(f"VSOCK server listening on port {PORT}...")
-
- while True:
- # Accept a connection
- connection, client_address = server_socket.accept()
- print(f"Connection from: {client_address}")
-
- try:
- # Get the local IP address
- local_ip = get_local_ip()
-
- # Send the IP address to the client
- connection.sendall(local_ip.encode())
- finally:
- # Close the connection
- connection.close()
-
-if __name__ == "__main__":
- main()
diff --git a/android/LinuxInstaller/linux_image_builder/vsockip.service b/android/LinuxInstaller/linux_image_builder/vsockip.service
deleted file mode 100644
index a29020b..0000000
--- a/android/LinuxInstaller/linux_image_builder/vsockip.service
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-Description=vsock ip service
-After=syslog.target
-After=network.target
-[Service]
-ExecStart=/usr/bin/python3 /usr/local/bin/vsock.py
-Type=simple
-Restart=always
-User=root
-Group=root
-[Install]
-WantedBy=multi-user.target
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index 018916d..84c3eee 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -17,11 +17,15 @@
import android.content.ClipData;
import android.content.ClipboardManager;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.accessibility.AccessibilityManager;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@@ -34,8 +38,15 @@
import com.google.android.material.appbar.MaterialToolbar;
-public class MainActivity extends AppCompatActivity implements
- VmLauncherServices.VmLauncherServiceCallback {
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+public class MainActivity extends AppCompatActivity
+ implements VmLauncherServices.VmLauncherServiceCallback,
+ AccessibilityManager.TouchExplorationStateChangeListener {
private static final String TAG = "VmTerminalApp";
private String mVmIpAddr;
private WebView mWebView;
@@ -43,7 +54,19 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ try {
+ // No resize for now.
+ long newSizeInBytes = 0;
+ diskResize(this, newSizeInBytes);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to resize disk", e);
+ Toast.makeText(this, "Error resizing disk: " + e.getMessage(), Toast.LENGTH_LONG)
+ .show();
+ }
+
Toast.makeText(this, R.string.vm_creation_message, Toast.LENGTH_SHORT).show();
+ android.os.Trace.beginAsyncSection("executeTerminal", 0);
VmLauncherServices.startVmLauncherService(this, this);
setContentView(R.layout.activity_headless);
@@ -62,16 +85,121 @@
view.loadUrl(url);
return true;
}
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ android.os.Trace.endAsyncSection("executeTerminal", 0);
+ }
});
+
+ getSystemService(AccessibilityManager.class).addTouchExplorationStateChangeListener(this);
+ }
+
+ private void diskResize(Context context, long sizeInBytes) throws IOException {
+ try {
+ if (sizeInBytes == 0) {
+ return;
+ }
+ File file = getPartitionFile(context, "root_part");
+ String filePath = file.getAbsolutePath();
+ Log.d(TAG, "Disk-resize in progress for partition: " + filePath);
+
+ long currentSize = Os.stat(filePath).st_size;
+ runE2fsck(filePath);
+ if (sizeInBytes > currentSize) {
+ allocateSpace(file, sizeInBytes);
+ }
+
+ resizeFilesystem(filePath, sizeInBytes);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "ErrnoException during disk resize", e);
+ throw new IOException("ErrnoException during disk resize", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to resize disk", e);
+ throw e;
+ }
+ }
+
+ private static File getPartitionFile(Context context, String fileName)
+ throws FileNotFoundException {
+ File file = new File(context.getFilesDir(), fileName);
+ if (!file.exists()) {
+ Log.d(TAG, fileName + " - file not found");
+ throw new FileNotFoundException("File not found: " + fileName);
+ }
+ return file;
+ }
+
+ private static void allocateSpace(File file, long sizeInBytes) throws IOException {
+ try {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+ FileDescriptor fd = raf.getFD();
+ Os.posix_fallocate(fd, 0, sizeInBytes);
+ raf.close();
+ Log.d(TAG, "Allocated space to: " + sizeInBytes + " bytes");
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Failed to allocate space", e);
+ throw new IOException("Failed to allocate space", e);
+ }
+ }
+
+ private static void runE2fsck(String filePath) throws IOException {
+ try {
+ runCommand("/system/bin/e2fsck", "-f", filePath);
+ Log.d(TAG, "e2fsck completed: " + filePath);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to run e2fsck", e);
+ throw e;
+ }
+ }
+
+ private static void resizeFilesystem(String filePath, long sizeInBytes) throws IOException {
+ long sizeInMB = sizeInBytes / (1024 * 1024);
+ if (sizeInMB == 0) {
+ Log.e(TAG, "Invalid size: " + sizeInBytes + " bytes");
+ throw new IllegalArgumentException("Size cannot be zero MB");
+ }
+ String sizeArg = sizeInMB + "M";
+ try {
+ runCommand("/system/bin/resize2fs", filePath, sizeArg);
+ Log.d(TAG, "resize2fs completed: " + filePath + ", size: " + sizeArg);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to run resize2fs", e);
+ throw e;
+ }
+ }
+
+ private static void runCommand(String... command) throws IOException {
+ try {
+ Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
+ process.waitFor();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException("Command interrupted", e);
+ }
}
@Override
protected void onDestroy() {
+ getSystemService(AccessibilityManager.class).removeTouchExplorationStateChangeListener(this);
VmLauncherServices.stopVmLauncherService(this);
super.onDestroy();
}
- private void gotoURL(String url) {
+ 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));
}
@@ -98,7 +226,7 @@
public void onIpAddrAvailable(String ipAddr) {
mVmIpAddr = ipAddr;
((TextView) findViewById(R.id.ip_addr_textview)).setText(mVmIpAddr);
- gotoURL("http://" + mVmIpAddr + ":7681");
+ gotoTerminalURL();
}
@Override
@@ -126,4 +254,9 @@
}
return super.onOptionsItemSelected(item);
}
+
+ @Override
+ public void onTouchExplorationStateChanged(boolean enabled) {
+ gotoTerminalURL();
+ }
}
diff --git a/build/debian/fai_config/scripts/AVF/20-useradd b/build/debian/fai_config/scripts/AVF/20-useradd
index 9fbcd43..b5887c5 100755
--- a/build/debian/fai_config/scripts/AVF/20-useradd
+++ b/build/debian/fai_config/scripts/AVF/20-useradd
@@ -1,4 +1,4 @@
#!/bin/bash
-$ROOTCMD useradd -m -u 1000 -N -G sudo droid
+$ROOTCMD useradd -m -u 1000 -g 1000 -N -G sudo droid
$ROOTCMD echo 'droid ALL=(ALL) NOPASSWD:ALL' >> $target/etc/sudoers
\ No newline at end of file
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/build.sh b/build/debian/kokoro/gcp_ubuntu_docker/build.sh
index 5023d09..c1524dd 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/build.sh
+++ b/build/debian/kokoro/gcp_ubuntu_docker/build.sh
@@ -6,8 +6,7 @@
sudo losetup -D
grep vmx /proc/cpuinfo || true
sudo ./build.sh
-tar czvS -f ${KOKORO_ARTIFACTS_DIR}/image.tar.gz image.raw
+tar czvS -f ${KOKORO_ARTIFACTS_DIR}/images.tar.gz image.raw
mkdir -p ${KOKORO_ARTIFACTS_DIR}/logs
-# TODO(b/372162211): Find exact location of log without breaking kokoro build.
-cp -r /var/log/fai/*/last/* ${KOKORO_ARTIFACTS_DIR}/logs || true
+sudo cp -r /var/log/fai/* ${KOKORO_ARTIFACTS_DIR}/logs || true
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg b/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg
index ddb4ef7..a41c032 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg
+++ b/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg
@@ -8,7 +8,7 @@
action {
define_artifacts {
- regex: "image.tar.gz"
- regex: "logs/*"
+ regex: "images.tar.gz"
+ regex: "logs/**"
}
}
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/hourly.cfg b/build/debian/kokoro/gcp_ubuntu_docker/hourly.cfg
index ddb4ef7..a41c032 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/hourly.cfg
+++ b/build/debian/kokoro/gcp_ubuntu_docker/hourly.cfg
@@ -8,7 +8,7 @@
action {
define_artifacts {
- regex: "image.tar.gz"
- regex: "logs/*"
+ regex: "images.tar.gz"
+ regex: "logs/**"
}
}
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/presubmit.cfg b/build/debian/kokoro/gcp_ubuntu_docker/presubmit.cfg
index ddb4ef7..a41c032 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/presubmit.cfg
+++ b/build/debian/kokoro/gcp_ubuntu_docker/presubmit.cfg
@@ -8,7 +8,7 @@
action {
define_artifacts {
- regex: "image.tar.gz"
- regex: "logs/*"
+ regex: "images.tar.gz"
+ regex: "logs/**"
}
}
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 4cca110..3d5c345 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
@@ -86,7 +86,10 @@
Runner runner;
try {
+ android.os.Trace.beginSection("vmCreate");
runner = Runner.create(this, config);
+ android.os.Trace.endSection();
+ android.os.Trace.beginAsyncSection("debianBoot", 0);
} catch (VirtualMachineException e) {
Log.e(TAG, "cannot create runner", e);
stopSelf();
@@ -169,6 +172,7 @@
@Override
public void onIpAddressAvailable(String ipAddr) {
+ android.os.Trace.endAsyncSection("debianBoot", 0);
Bundle b = new Bundle();
b.putString(VmLauncherService.KEY_VM_IP_ADDR, ipAddr);
mResultReceiver.send(VmLauncherService.RESULT_IPADDR, b);