Introduce VirtualMachineCustomImageConfig instead of rawConfigPath
```
VirtualMachineCustomImageConfig.Builder builder = ...;
builder.setKernel(path);
builder.addParam("console=...");
builder.addDisk(Disk.RODisk(diskPath));
config.setCustomImageConfig(builder.build());
```
Bug: 330256602
Test: run vmlauncher
Change-Id: I14dedf15edc2be8ae047ebbc37f10d5c74adba76
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachine.java b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
index f9ca2f2..dd2719c 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachine.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
@@ -813,7 +813,7 @@
private android.system.virtualizationservice.VirtualMachineConfig
createVirtualMachineConfigForRawFrom(VirtualMachineConfig vmConfig)
- throws IllegalStateException {
+ throws IllegalStateException, IOException {
VirtualMachineRawConfig rawConfig = vmConfig.toVsRawConfig();
return android.system.virtualizationservice.VirtualMachineConfig.rawConfig(rawConfig);
}
@@ -904,7 +904,7 @@
VirtualMachineConfig vmConfig = getConfig();
android.system.virtualizationservice.VirtualMachineConfig vmConfigParcel =
- vmConfig.getRawConfigPath() != null
+ vmConfig.getCustomImageConfig() != null
? createVirtualMachineConfigForRawFrom(vmConfig)
: createVirtualMachineConfigForAppFrom(vmConfig, service);
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
index 99c3d05..be80db8 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -43,13 +43,11 @@
import android.system.virtualizationservice.VirtualMachineAppConfig;
import android.system.virtualizationservice.VirtualMachinePayloadConfig;
import android.system.virtualizationservice.VirtualMachineRawConfig;
+import android.text.TextUtils;
import android.util.Log;
import com.android.system.virtualmachine.flags.Flags;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
@@ -60,13 +58,12 @@
import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.zip.ZipFile;
/**
@@ -88,7 +85,7 @@
private static final String KEY_PACKAGENAME = "packageName";
private static final String KEY_APKPATH = "apkPath";
private static final String KEY_PAYLOADCONFIGPATH = "payloadConfigPath";
- private static final String KEY_RAWCONFIGPATH = "rawConfigPath";
+ private static final String KEY_CUSTOMIMAGECONFIG = "customImageConfig";
private static final String KEY_PAYLOADBINARYNAME = "payloadBinaryPath";
private static final String KEY_DEBUGLEVEL = "debugLevel";
private static final String KEY_PROTECTED_VM = "protectedVm";
@@ -184,8 +181,8 @@
/** Name of the payload binary file within the APK that will be executed within the VM. */
@Nullable private final String mPayloadBinaryName;
- /** Path within the raw config file to launch the VM. */
- @Nullable private final String mRawConfigPath;
+ /** The custom image config file to launch the custom VM. */
+ @Nullable private final VirtualMachineCustomImageConfig mCustomImageConfig;
/** The size of storage in bytes. 0 indicates that encryptedStorage is not required */
private final long mEncryptedStorageBytes;
@@ -224,7 +221,7 @@
List<String> extraApks,
@Nullable String payloadConfigPath,
@Nullable String payloadBinaryName,
- @Nullable String rawConfigPath,
+ @Nullable VirtualMachineCustomImageConfig customImageConfig,
@DebugLevel int debugLevel,
boolean protectedVm,
long memoryBytes,
@@ -244,7 +241,7 @@
Arrays.asList(extraApks.toArray(new String[0])));
mPayloadConfigPath = payloadConfigPath;
mPayloadBinaryName = payloadBinaryName;
- mRawConfigPath = rawConfigPath;
+ mCustomImageConfig = customImageConfig;
mDebugLevel = debugLevel;
mProtectedVm = protectedVm;
mMemoryBytes = memoryBytes;
@@ -307,9 +304,10 @@
String payloadConfigPath = b.getString(KEY_PAYLOADCONFIGPATH);
String payloadBinaryName = b.getString(KEY_PAYLOADBINARYNAME);
- String rawConfigPath = b.getString(KEY_RAWCONFIGPATH);
- if (rawConfigPath != null) {
- builder.setRawConfigPath(rawConfigPath);
+ PersistableBundle customImageConfigBundle = b.getPersistableBundle(KEY_CUSTOMIMAGECONFIG);
+ if (customImageConfigBundle != null) {
+ builder.setCustomImageConfig(
+ VirtualMachineCustomImageConfig.from(customImageConfigBundle));
} else if (payloadConfigPath != null) {
builder.setPayloadConfigPath(payloadConfigPath);
} else {
@@ -372,7 +370,9 @@
}
b.putString(KEY_PAYLOADCONFIGPATH, mPayloadConfigPath);
b.putString(KEY_PAYLOADBINARYNAME, mPayloadBinaryName);
- b.putString(KEY_RAWCONFIGPATH, mRawConfigPath);
+ if (mCustomImageConfig != null) {
+ b.putPersistableBundle(KEY_CUSTOMIMAGECONFIG, mCustomImageConfig.toPersistableBundle());
+ }
b.putInt(KEY_DEBUGLEVEL, mDebugLevel);
b.putBoolean(KEY_PROTECTED_VM, mProtectedVm);
b.putInt(KEY_CPU_TOPOLOGY, mCpuTopology);
@@ -433,13 +433,13 @@
}
/**
- * Returns the path within the raw config file to launch the VM.
+ * Returns the custom image config to launch the custom VM.
*
* @hide
*/
@Nullable
- public String getRawConfigPath() {
- return mRawConfigPath;
+ public VirtualMachineCustomImageConfig getCustomImageConfig() {
+ return mCustomImageConfig;
}
/**
@@ -585,96 +585,67 @@
&& Objects.equals(this.mExtraApks, other.mExtraApks);
}
- // Needs to sync with packages/modules/Virtualization/libs/vmconfig/src/lib.rs
- VirtualMachineRawConfig toVsRawConfig() throws IllegalStateException {
- VirtualMachineRawConfig config = new VirtualMachineRawConfig();
-
+ private ParcelFileDescriptor openOrNull(File file, int mode) {
try {
- String rawJson = new String(Files.readAllBytes(Path.of(mRawConfigPath)));
- JSONObject json = new JSONObject(rawJson);
- config.name = json.optString("name", "");
- config.instanceId = new byte[64];
- if (json.has("kernel")) {
- config.kernel =
- ParcelFileDescriptor.open(
- new File(json.getString("kernel")), MODE_READ_ONLY);
- }
- if (json.has("initrd")) {
- config.initrd =
- ParcelFileDescriptor.open(
- new File(json.getString("initrd")), MODE_READ_ONLY);
- }
- if (json.has("params")) {
- config.params = json.getString("params");
- }
- if (json.has("bootloader")) {
- config.bootloader =
- ParcelFileDescriptor.open(
- new File(json.getString("bootloader")), MODE_READ_ONLY);
- }
- if (json.has("disks")) {
- JSONArray diskArr = json.getJSONArray("disks");
- config.disks = new DiskImage[diskArr.length()];
- for (int i = 0; i < diskArr.length(); i++) {
- config.disks[i] = new DiskImage();
- JSONObject item = diskArr.getJSONObject(i);
- config.disks[i].writable = item.optBoolean("writable", false);
- if (item.has("image")) {
- config.disks[i].image =
- ParcelFileDescriptor.open(
- new File(item.getString("image")),
- config.disks[i].writable
- ? MODE_READ_WRITE
- : MODE_READ_ONLY);
- }
- if (item.has("partition")) {
- JSONArray partitionArr = json.getJSONArray("partition");
- config.disks[i].partitions = new Partition[partitionArr.length()];
- for (int j = 0; j < partitionArr.length(); j++) {
- config.disks[i].partitions[j] = new Partition();
- JSONObject partitionItem = partitionArr.getJSONObject(j);
- config.disks[i].partitions[j].writable =
- partitionItem.optBoolean("writable", false);
- config.disks[i].partitions[j].label = partitionItem.getString("label");
- config.disks[i].partitions[j].image =
- ParcelFileDescriptor.open(
- new File(partitionItem.getString("image")),
- config.disks[i].partitions[j].writable
- ? MODE_READ_WRITE
- : MODE_READ_ONLY);
- }
- } else {
- config.disks[i].partitions = new Partition[0];
- }
- }
- } else {
- config.disks = new DiskImage[0];
- }
- // The value which is set by setProtectedVm is used.
- if (json.has("protected")) {
- Log.d(TAG, "'protected' field is ignored, the value from setProtectedVm is used");
- }
- config.protectedVm = this.mProtectedVm;
- config.memoryMib = json.optInt("memory_mib", 0);
- if (json.optString("cpu_topology", "one_cpu").equals("match_host")) {
- config.cpuTopology = CPU_TOPOLOGY_MATCH_HOST;
- } else {
- config.cpuTopology = CPU_TOPOLOGY_ONE_CPU;
- }
- config.platformVersion = json.getString("platform_version");
- if (json.has("devices")) {
- JSONArray arr = json.getJSONArray("devices");
- config.devices = new String[arr.length()];
- for (int i = 0; i < arr.length(); i++) {
- config.devices[i] = arr.getString(i);
- }
- } else {
- config.devices = new String[0];
- }
-
- } catch (JSONException | IOException e) {
- throw new IllegalStateException("malformed input", e);
+ return ParcelFileDescriptor.open(file, mode);
+ } catch (FileNotFoundException e) {
+ Log.d(TAG, "cannot open", e);
+ return null;
}
+ }
+
+ VirtualMachineRawConfig toVsRawConfig() throws IllegalStateException, IOException {
+ VirtualMachineRawConfig config = new VirtualMachineRawConfig();
+ VirtualMachineCustomImageConfig customImageConfig = getCustomImageConfig();
+ requireNonNull(customImageConfig);
+ config.name = Optional.ofNullable(customImageConfig.getName()).orElse("");
+ config.instanceId = new byte[64];
+ config.kernel =
+ Optional.of(customImageConfig.getKernelPath())
+ .map(
+ (path) -> {
+ try {
+ return ParcelFileDescriptor.open(
+ new File(path), MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .orElse(null);
+
+ config.initrd =
+ Optional.ofNullable(customImageConfig.getInitrdPath())
+ .map((path) -> openOrNull(new File(path), MODE_READ_ONLY))
+ .orElse(null);
+ config.bootloader =
+ Optional.ofNullable(customImageConfig.getBootloaderPath())
+ .map((path) -> openOrNull(new File(path), MODE_READ_ONLY))
+ .orElse(null);
+ config.params =
+ Optional.ofNullable(customImageConfig.getParams())
+ .map((params) -> TextUtils.join(" ", params))
+ .orElse("");
+ config.disks =
+ new DiskImage
+ [Optional.ofNullable(customImageConfig.getDisks())
+ .map(arr -> arr.length)
+ .orElse(0)];
+ for (int i = 0; i < config.disks.length; i++) {
+ config.disks[i] = new DiskImage();
+ config.disks[i].writable = customImageConfig.getDisks()[i].isWritable();
+
+ config.disks[i].image =
+ ParcelFileDescriptor.open(
+ new File(customImageConfig.getDisks()[i].getImagePath()),
+ config.disks[i].writable ? MODE_READ_WRITE : MODE_READ_ONLY);
+ config.disks[i].partitions = new Partition[0];
+ }
+
+ config.protectedVm = this.mProtectedVm;
+ config.memoryMib = bytesToMebiBytes(mMemoryBytes);
+ config.cpuTopology = (byte) this.mCpuTopology;
+ config.devices = EMPTY_STRING_ARRAY;
+ config.platformVersion = "~1.0";
return config;
}
@@ -804,7 +775,7 @@
@Nullable private String mApkPath;
private final List<String> mExtraApks = new ArrayList<>();
@Nullable private String mPayloadConfigPath;
- @Nullable private String mRawConfigPath;
+ @Nullable private VirtualMachineCustomImageConfig mCustomImageConfig;
@Nullable private String mPayloadBinaryName;
@DebugLevel private int mDebugLevel = DEBUG_LEVEL_NONE;
private boolean mProtectedVm;
@@ -854,11 +825,11 @@
// This should never happen, unless we're deserializing a bad config
throw new IllegalStateException("apkPath or packageName must be specified");
}
- if (mRawConfigPath != null) {
+ if (mCustomImageConfig != null) {
if (mPayloadBinaryName != null || mPayloadConfigPath != null) {
throw new IllegalStateException(
- "setRawConfigPath and (setPayloadBinaryName or setPayloadConfigPath)"
- + " may not both be called");
+ "setCustomImageConfig and (setPayloadBinaryName or"
+ + " setPayloadConfigPath) may not both be called");
}
} else if (mPayloadBinaryName == null) {
if (mPayloadConfigPath == null) {
@@ -893,7 +864,7 @@
mExtraApks,
mPayloadConfigPath,
mPayloadBinaryName,
- mRawConfigPath,
+ mCustomImageConfig,
mDebugLevel,
mProtectedVm,
mMemoryBytes,
@@ -955,25 +926,15 @@
}
/**
- * Sets the path within the raw config file to launch the VM. The file is a JSON file; see
- * packages/modules/Virtualization/libs/vmconfig/src/lib.rs for the format.
+ * Sets the custom config file to launch the custom VM.
*
* @hide
*/
@RequiresPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION)
@NonNull
- public Builder setRawConfigPath(@NonNull String rawConfigPath) {
- // TODO: This method will be removed when the builder support more structured methods
- // for that like below.
- // builder
- // .setLinuxKernelConfig(new LinuxKernelConfig()
- // .setKernelPath(...)
- // .setInitRamdiskPath(...)
- // .setKernelCommandLine(...))
- // .setDiskConfig(new DiskConfig()
- // .addDisk(...)
- // .addDisk(...))
- mRawConfigPath = requireNonNull(rawConfigPath, "rawConfigPath must not be null");
+ public Builder setCustomImageConfig(
+ @NonNull VirtualMachineCustomImageConfig customImageConfig) {
+ this.mCustomImageConfig = customImageConfig;
return this;
}
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
new file mode 100644
index 0000000..219fdca
--- /dev/null
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
@@ -0,0 +1,224 @@
+/*
+ * 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 android.system.virtualmachine;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide */
+public class VirtualMachineCustomImageConfig {
+ private static final String KEY_NAME = "name";
+ private static final String KEY_KERNEL = "kernel";
+ private static final String KEY_INITRD = "initrd";
+ private static final String KEY_BOOTLOADER = "bootloader";
+ private static final String KEY_PARAMS = "params";
+ private static final String KEY_DISK_WRITABLES = "disk_writables";
+ private static final String KEY_DISK_IMAGES = "disk_images";
+ @Nullable private final String name;
+ @NonNull private final String kernelPath;
+ @Nullable private final String initrdPath;
+ @Nullable private final String bootloaderPath;
+ @Nullable private final String[] params;
+ @Nullable private final Disk[] disks;
+
+ @Nullable
+ public Disk[] getDisks() {
+ return disks;
+ }
+
+ @Nullable
+ public String getBootloaderPath() {
+ return bootloaderPath;
+ }
+
+ @Nullable
+ public String getInitrdPath() {
+ return initrdPath;
+ }
+
+ @NonNull
+ public String getKernelPath() {
+ return kernelPath;
+ }
+
+ @Nullable
+ public String getName() {
+ return name;
+ }
+
+ @Nullable
+ public String[] getParams() {
+ return params;
+ }
+
+ /** @hide */
+ public VirtualMachineCustomImageConfig(
+ String name,
+ String kernelPath,
+ String initrdPath,
+ String bootloaderPath,
+ String[] params,
+ Disk[] disks) {
+ this.name = name;
+ this.kernelPath = kernelPath;
+ this.initrdPath = initrdPath;
+ this.bootloaderPath = bootloaderPath;
+ this.params = params;
+ this.disks = disks;
+ }
+
+ static VirtualMachineCustomImageConfig from(PersistableBundle customImageConfigBundle) {
+ Builder builder = new Builder();
+ builder.setName(customImageConfigBundle.getString(KEY_NAME));
+ builder.setKernelPath(customImageConfigBundle.getString(KEY_KERNEL));
+ builder.setInitrdPath(customImageConfigBundle.getString(KEY_INITRD));
+ builder.setBootloaderPath(customImageConfigBundle.getString(KEY_BOOTLOADER));
+ String[] params = customImageConfigBundle.getStringArray(KEY_PARAMS);
+ if (params != null) {
+ for (String param : params) {
+ builder.addParam(param);
+ }
+ }
+ boolean[] writables = customImageConfigBundle.getBooleanArray(KEY_DISK_WRITABLES);
+ String[] diskImages = customImageConfigBundle.getStringArray(KEY_DISK_IMAGES);
+ if (writables != null && diskImages != null) {
+ if (writables.length == diskImages.length) {
+ for (int i = 0; i < writables.length; i++) {
+ builder.addDisk(
+ writables[i] ? Disk.RWDisk(diskImages[i]) : Disk.RODisk(diskImages[i]));
+ }
+ }
+ }
+ return null;
+ }
+
+ PersistableBundle toPersistableBundle() {
+ PersistableBundle pb = new PersistableBundle();
+ pb.putString(KEY_NAME, this.name);
+ pb.putString(KEY_KERNEL, this.kernelPath);
+ pb.putString(KEY_BOOTLOADER, this.bootloaderPath);
+ pb.putString(KEY_INITRD, this.initrdPath);
+ pb.putStringArray(KEY_PARAMS, this.params);
+
+ if (disks != null) {
+ boolean[] writables = new boolean[disks.length];
+ String[] images = new String[disks.length];
+ for (int i = 0; i < disks.length; i++) {
+ writables[i] = disks[i].writable;
+ images[i] = disks[i].imagePath;
+ }
+ pb.putBooleanArray(KEY_DISK_WRITABLES, writables);
+ pb.putStringArray(KEY_DISK_IMAGES, images);
+ }
+ return pb;
+ }
+
+ /** @hide */
+ public static final class Disk {
+ private final boolean writable;
+ private final String imagePath;
+
+ private Disk(boolean writable, String imagePath) {
+ this.writable = writable;
+ this.imagePath = imagePath;
+ }
+
+ /** @hide */
+ public static Disk RWDisk(String imagePath) {
+ return new Disk(true, imagePath);
+ }
+
+ /** @hide */
+ public static Disk RODisk(String imagePath) {
+ return new Disk(false, imagePath);
+ }
+
+ /** @hide */
+ public boolean isWritable() {
+ return writable;
+ }
+
+ /** @hide */
+ public String getImagePath() {
+ return imagePath;
+ }
+ }
+
+ /** @hide */
+ public static final class Builder {
+ private String name;
+ private String kernelPath;
+ private String initrdPath;
+ private String bootloaderPath;
+ private List<String> params = new ArrayList<>();
+ private List<Disk> disks = new ArrayList<>();
+
+ /** @hide */
+ public Builder() {}
+
+ /** @hide */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setKernelPath(String kernelPath) {
+ this.kernelPath = kernelPath;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setBootloaderPath(String bootloaderPath) {
+ this.bootloaderPath = bootloaderPath;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setInitrdPath(String initrdPath) {
+ this.initrdPath = initrdPath;
+ return this;
+ }
+
+ /** @hide */
+ public Builder addDisk(Disk disk) {
+ this.disks.add(disk);
+ return this;
+ }
+
+ /** @hide */
+ public Builder addParam(String param) {
+ this.params.add(param);
+ return this;
+ }
+
+ /** @hide */
+ public VirtualMachineCustomImageConfig build() {
+ return new VirtualMachineCustomImageConfig(
+ this.name,
+ this.kernelPath,
+ this.initrdPath,
+ this.bootloaderPath,
+ this.params.toArray(new String[0]),
+ this.disks.toArray(new Disk[0]));
+ }
+ }
+}
diff --git a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
index 90d7fcc..7c927c9 100644
--- a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
@@ -16,8 +16,11 @@
package com.android.virtualization.vmlauncher;
+import static android.system.virtualmachine.VirtualMachineConfig.CPU_TOPOLOGY_MATCH_HOST;
+
import android.app.Activity;
import android.os.Bundle;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig;
import android.util.Log;
import android.system.virtualmachine.VirtualMachine;
import android.system.virtualmachine.VirtualMachineCallback;
@@ -25,10 +28,17 @@
import android.system.virtualmachine.VirtualMachineException;
import android.system.virtualmachine.VirtualMachineManager;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -39,6 +49,62 @@
private final ExecutorService mExecutorService = Executors.newFixedThreadPool(4);
private VirtualMachine mVirtualMachine;
+ private VirtualMachineConfig createVirtualMachineConfig(String jsonPath) {
+ VirtualMachineConfig.Builder configBuilder =
+ new VirtualMachineConfig.Builder(getApplication());
+ configBuilder.setCpuTopology(CPU_TOPOLOGY_MATCH_HOST);
+
+ configBuilder.setProtectedVm(false);
+ if (DEBUG) {
+ configBuilder.setDebugLevel(VirtualMachineConfig.DEBUG_LEVEL_FULL);
+ configBuilder.setVmOutputCaptured(true);
+ }
+ VirtualMachineCustomImageConfig.Builder customImageConfigBuilder =
+ new VirtualMachineCustomImageConfig.Builder();
+ try {
+ String rawJson = new String(Files.readAllBytes(Path.of(jsonPath)));
+ JSONObject json = new JSONObject(rawJson);
+ customImageConfigBuilder.setName(json.optString("name", ""));
+ if (json.has("kernel")) {
+ customImageConfigBuilder.setKernelPath(json.getString("kernel"));
+ }
+ if (json.has("initrd")) {
+ customImageConfigBuilder.setInitrdPath(json.getString("initrd"));
+ }
+ if (json.has("params")) {
+ Arrays.stream(json.getString("params").split(" "))
+ .forEach(customImageConfigBuilder::addParam);
+ }
+ if (json.has("bootloader")) {
+ customImageConfigBuilder.setInitrdPath(json.getString("bootloader"));
+ }
+ if (json.has("disks")) {
+ JSONArray diskArr = json.getJSONArray("disks");
+ for (int i = 0; i < diskArr.length(); i++) {
+ JSONObject item = diskArr.getJSONObject(i);
+ if (item.has("image")) {
+ if (item.optBoolean("writable", false)) {
+ customImageConfigBuilder.addDisk(
+ VirtualMachineCustomImageConfig.Disk.RWDisk(
+ item.getString("image")));
+ } else {
+ customImageConfigBuilder.addDisk(
+ VirtualMachineCustomImageConfig.Disk.RODisk(
+ item.getString("image")));
+ }
+ }
+ }
+ }
+
+ configBuilder.setMemoryBytes(8L * 1024 * 1024 * 1024 /* 8 GB */);
+ configBuilder.setCustomImageConfig(customImageConfigBuilder.build());
+
+ } catch (JSONException | IOException e) {
+ throw new IllegalStateException("malformed input", e);
+ }
+ return configBuilder.build();
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -89,15 +155,8 @@
};
try {
- VirtualMachineConfig.Builder builder =
- new VirtualMachineConfig.Builder(getApplication());
- builder.setRawConfigPath("/data/local/tmp/vm_config.json");
- builder.setProtectedVm(false);
- if (DEBUG) {
- builder.setDebugLevel(VirtualMachineConfig.DEBUG_LEVEL_FULL);
- builder.setVmOutputCaptured(true);
- }
- VirtualMachineConfig config = builder.build();
+ VirtualMachineConfig config =
+ createVirtualMachineConfig("/data/local/tmp/vm_config.json");
VirtualMachineManager vmm =
getApplication().getSystemService(VirtualMachineManager.class);
if (vmm == null) {