423 lines
13 KiB
Java
423 lines
13 KiB
Java
![]() |
/*
|
||
|
* 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.Nullable;
|
||
|
import android.os.PersistableBundle;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.List;
|
||
|
import java.util.Optional;
|
||
|
|
||
|
/** @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";
|
||
|
private static final String KEY_DISPLAY_CONFIG = "display_config";
|
||
|
private static final String KEY_TOUCH = "touch";
|
||
|
private static final String KEY_KEYBOARD = "keyboard";
|
||
|
|
||
|
@Nullable private final String name;
|
||
|
@Nullable 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 private final DisplayConfig displayConfig;
|
||
|
private final boolean touch;
|
||
|
private final boolean keyboard;
|
||
|
|
||
|
@Nullable
|
||
|
public Disk[] getDisks() {
|
||
|
return disks;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
public String getBootloaderPath() {
|
||
|
return bootloaderPath;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
public String getInitrdPath() {
|
||
|
return initrdPath;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
public String getKernelPath() {
|
||
|
return kernelPath;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
public String getName() {
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
public String[] getParams() {
|
||
|
return params;
|
||
|
}
|
||
|
|
||
|
public boolean useTouch() {
|
||
|
return touch;
|
||
|
}
|
||
|
|
||
|
public boolean useKeyboard() {
|
||
|
return keyboard;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public VirtualMachineCustomImageConfig(
|
||
|
String name,
|
||
|
String kernelPath,
|
||
|
String initrdPath,
|
||
|
String bootloaderPath,
|
||
|
String[] params,
|
||
|
Disk[] disks,
|
||
|
DisplayConfig displayConfig,
|
||
|
boolean touch,
|
||
|
boolean keyboard) {
|
||
|
this.name = name;
|
||
|
this.kernelPath = kernelPath;
|
||
|
this.initrdPath = initrdPath;
|
||
|
this.bootloaderPath = bootloaderPath;
|
||
|
this.params = params;
|
||
|
this.disks = disks;
|
||
|
this.displayConfig = displayConfig;
|
||
|
this.touch = touch;
|
||
|
this.keyboard = keyboard;
|
||
|
}
|
||
|
|
||
|
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]));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
PersistableBundle displayConfigPb =
|
||
|
customImageConfigBundle.getPersistableBundle(KEY_DISPLAY_CONFIG);
|
||
|
builder.setDisplayConfig(DisplayConfig.from(displayConfigPb));
|
||
|
builder.useTouch(customImageConfigBundle.getBoolean(KEY_TOUCH));
|
||
|
builder.useKeyboard(customImageConfigBundle.getBoolean(KEY_KEYBOARD));
|
||
|
return builder.build();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
pb.putPersistableBundle(
|
||
|
KEY_DISPLAY_CONFIG,
|
||
|
Optional.ofNullable(displayConfig)
|
||
|
.map(dc -> dc.toPersistableBundle())
|
||
|
.orElse(null));
|
||
|
pb.putBoolean(KEY_TOUCH, touch);
|
||
|
pb.putBoolean(KEY_KEYBOARD, keyboard);
|
||
|
return pb;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
public DisplayConfig getDisplayConfig() {
|
||
|
return displayConfig;
|
||
|
}
|
||
|
|
||
|
/** @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<>();
|
||
|
private DisplayConfig displayConfig;
|
||
|
private boolean touch;
|
||
|
private boolean keyboard;
|
||
|
|
||
|
/** @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 Builder setDisplayConfig(DisplayConfig displayConfig) {
|
||
|
this.displayConfig = displayConfig;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public Builder useTouch(boolean touch) {
|
||
|
this.touch = touch;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public Builder useKeyboard(boolean keyboard) {
|
||
|
this.keyboard = keyboard;
|
||
|
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]),
|
||
|
displayConfig,
|
||
|
touch,
|
||
|
keyboard);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public static final class DisplayConfig {
|
||
|
private static final String KEY_WIDTH = "width";
|
||
|
private static final String KEY_HEIGHT = "height";
|
||
|
private static final String KEY_HORIZONTAL_DPI = "horizontal_dpi";
|
||
|
private static final String KEY_VERTICAL_DPI = "vertical_dpi";
|
||
|
private static final String KEY_REFRESH_RATE = "refresh_rate";
|
||
|
private final int width;
|
||
|
private final int height;
|
||
|
private final int horizontalDpi;
|
||
|
private final int verticalDpi;
|
||
|
private final int refreshRate;
|
||
|
|
||
|
private DisplayConfig(
|
||
|
int width, int height, int horizontalDpi, int verticalDpi, int refreshRate) {
|
||
|
this.width = width;
|
||
|
this.height = height;
|
||
|
this.horizontalDpi = horizontalDpi;
|
||
|
this.verticalDpi = verticalDpi;
|
||
|
this.refreshRate = refreshRate;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public int getWidth() {
|
||
|
return width;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public int getHeight() {
|
||
|
return height;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public int getHorizontalDpi() {
|
||
|
return horizontalDpi;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public int getVerticalDpi() {
|
||
|
return verticalDpi;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public int getRefreshRate() {
|
||
|
return refreshRate;
|
||
|
}
|
||
|
|
||
|
android.system.virtualizationservice.DisplayConfig toParcelable() {
|
||
|
android.system.virtualizationservice.DisplayConfig parcelable =
|
||
|
new android.system.virtualizationservice.DisplayConfig();
|
||
|
parcelable.width = this.width;
|
||
|
parcelable.height = this.height;
|
||
|
parcelable.horizontalDpi = this.horizontalDpi;
|
||
|
parcelable.verticalDpi = this.verticalDpi;
|
||
|
parcelable.refreshRate = this.refreshRate;
|
||
|
|
||
|
return parcelable;
|
||
|
}
|
||
|
|
||
|
private static DisplayConfig from(PersistableBundle pb) {
|
||
|
if (pb == null) {
|
||
|
return null;
|
||
|
}
|
||
|
Builder builder = new Builder();
|
||
|
builder.setWidth(pb.getInt(KEY_WIDTH));
|
||
|
builder.setHeight(pb.getInt(KEY_HEIGHT));
|
||
|
builder.setHorizontalDpi(pb.getInt(KEY_HORIZONTAL_DPI));
|
||
|
builder.setVerticalDpi(pb.getInt(KEY_VERTICAL_DPI));
|
||
|
builder.setRefreshRate(pb.getInt(KEY_REFRESH_RATE));
|
||
|
return builder.build();
|
||
|
}
|
||
|
|
||
|
private PersistableBundle toPersistableBundle() {
|
||
|
PersistableBundle pb = new PersistableBundle();
|
||
|
pb.putInt(KEY_WIDTH, this.width);
|
||
|
pb.putInt(KEY_HEIGHT, this.height);
|
||
|
pb.putInt(KEY_HORIZONTAL_DPI, this.horizontalDpi);
|
||
|
pb.putInt(KEY_VERTICAL_DPI, this.verticalDpi);
|
||
|
pb.putInt(KEY_REFRESH_RATE, this.refreshRate);
|
||
|
return pb;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public static class Builder {
|
||
|
// Default values come from external/crosvm/vm_control/src/gpu.rs
|
||
|
private int width;
|
||
|
private int height;
|
||
|
private int horizontalDpi = 320;
|
||
|
private int verticalDpi = 320;
|
||
|
private int refreshRate = 60;
|
||
|
|
||
|
/** @hide */
|
||
|
public Builder() {}
|
||
|
|
||
|
/** @hide */
|
||
|
public Builder setWidth(int width) {
|
||
|
this.width = width;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public Builder setHeight(int height) {
|
||
|
this.height = height;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public Builder setHorizontalDpi(int horizontalDpi) {
|
||
|
this.horizontalDpi = horizontalDpi;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public Builder setVerticalDpi(int verticalDpi) {
|
||
|
this.verticalDpi = verticalDpi;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public Builder setRefreshRate(int refreshRate) {
|
||
|
this.refreshRate = refreshRate;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public DisplayConfig build() {
|
||
|
if (this.width == 0 || this.height == 0) {
|
||
|
throw new IllegalStateException("width and height must be specified");
|
||
|
}
|
||
|
return new DisplayConfig(width, height, horizontalDpi, verticalDpi, refreshRate);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|