1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.system.virtualmachine; 18 19 import android.annotation.Nullable; 20 import android.os.PersistableBundle; 21 22 import java.util.ArrayList; 23 import java.util.List; 24 import java.util.Optional; 25 26 /** @hide */ 27 public class VirtualMachineCustomImageConfig { 28 private static final String KEY_NAME = "name"; 29 private static final String KEY_KERNEL = "kernel"; 30 private static final String KEY_INITRD = "initrd"; 31 private static final String KEY_BOOTLOADER = "bootloader"; 32 private static final String KEY_PARAMS = "params"; 33 private static final String KEY_DISK_WRITABLES = "disk_writables"; 34 private static final String KEY_DISK_IMAGES = "disk_images"; 35 private static final String KEY_DISPLAY_CONFIG = "display_config"; 36 private static final String KEY_TOUCH = "touch"; 37 private static final String KEY_KEYBOARD = "keyboard"; 38 private static final String KEY_MOUSE = "mouse"; 39 private static final String KEY_GPU = "gpu"; 40 41 @Nullable private final String name; 42 @Nullable private final String kernelPath; 43 @Nullable private final String initrdPath; 44 @Nullable private final String bootloaderPath; 45 @Nullable private final String[] params; 46 @Nullable private final Disk[] disks; 47 @Nullable private final DisplayConfig displayConfig; 48 private final boolean touch; 49 private final boolean keyboard; 50 private final boolean mouse; 51 @Nullable private final GpuConfig gpuConfig; 52 53 @Nullable getDisks()54 public Disk[] getDisks() { 55 return disks; 56 } 57 58 @Nullable getBootloaderPath()59 public String getBootloaderPath() { 60 return bootloaderPath; 61 } 62 63 @Nullable getInitrdPath()64 public String getInitrdPath() { 65 return initrdPath; 66 } 67 68 @Nullable getKernelPath()69 public String getKernelPath() { 70 return kernelPath; 71 } 72 73 @Nullable getName()74 public String getName() { 75 return name; 76 } 77 78 @Nullable getParams()79 public String[] getParams() { 80 return params; 81 } 82 useTouch()83 public boolean useTouch() { 84 return touch; 85 } 86 useKeyboard()87 public boolean useKeyboard() { 88 return keyboard; 89 } 90 useMouse()91 public boolean useMouse() { 92 return mouse; 93 } 94 95 /** @hide */ VirtualMachineCustomImageConfig( String name, String kernelPath, String initrdPath, String bootloaderPath, String[] params, Disk[] disks, DisplayConfig displayConfig, boolean touch, boolean keyboard, boolean mouse, GpuConfig gpuConfig)96 public VirtualMachineCustomImageConfig( 97 String name, 98 String kernelPath, 99 String initrdPath, 100 String bootloaderPath, 101 String[] params, 102 Disk[] disks, 103 DisplayConfig displayConfig, 104 boolean touch, 105 boolean keyboard, 106 boolean mouse, 107 GpuConfig gpuConfig) { 108 this.name = name; 109 this.kernelPath = kernelPath; 110 this.initrdPath = initrdPath; 111 this.bootloaderPath = bootloaderPath; 112 this.params = params; 113 this.disks = disks; 114 this.displayConfig = displayConfig; 115 this.touch = touch; 116 this.keyboard = keyboard; 117 this.mouse = mouse; 118 this.gpuConfig = gpuConfig; 119 } 120 from(PersistableBundle customImageConfigBundle)121 static VirtualMachineCustomImageConfig from(PersistableBundle customImageConfigBundle) { 122 Builder builder = new Builder(); 123 builder.setName(customImageConfigBundle.getString(KEY_NAME)); 124 builder.setKernelPath(customImageConfigBundle.getString(KEY_KERNEL)); 125 builder.setInitrdPath(customImageConfigBundle.getString(KEY_INITRD)); 126 builder.setBootloaderPath(customImageConfigBundle.getString(KEY_BOOTLOADER)); 127 String[] params = customImageConfigBundle.getStringArray(KEY_PARAMS); 128 if (params != null) { 129 for (String param : params) { 130 builder.addParam(param); 131 } 132 } 133 boolean[] writables = customImageConfigBundle.getBooleanArray(KEY_DISK_WRITABLES); 134 String[] diskImages = customImageConfigBundle.getStringArray(KEY_DISK_IMAGES); 135 if (writables != null && diskImages != null) { 136 if (writables.length == diskImages.length) { 137 for (int i = 0; i < writables.length; i++) { 138 builder.addDisk( 139 writables[i] ? Disk.RWDisk(diskImages[i]) : Disk.RODisk(diskImages[i])); 140 } 141 } 142 } 143 PersistableBundle displayConfigPb = 144 customImageConfigBundle.getPersistableBundle(KEY_DISPLAY_CONFIG); 145 builder.setDisplayConfig(DisplayConfig.from(displayConfigPb)); 146 builder.useTouch(customImageConfigBundle.getBoolean(KEY_TOUCH)); 147 builder.useKeyboard(customImageConfigBundle.getBoolean(KEY_KEYBOARD)); 148 builder.useMouse(customImageConfigBundle.getBoolean(KEY_MOUSE)); 149 builder.setGpuConfig(GpuConfig.from(customImageConfigBundle.getPersistableBundle(KEY_GPU))); 150 return builder.build(); 151 } 152 153 154 toPersistableBundle()155 PersistableBundle toPersistableBundle() { 156 PersistableBundle pb = new PersistableBundle(); 157 pb.putString(KEY_NAME, this.name); 158 pb.putString(KEY_KERNEL, this.kernelPath); 159 pb.putString(KEY_BOOTLOADER, this.bootloaderPath); 160 pb.putString(KEY_INITRD, this.initrdPath); 161 pb.putStringArray(KEY_PARAMS, this.params); 162 163 if (disks != null) { 164 boolean[] writables = new boolean[disks.length]; 165 String[] images = new String[disks.length]; 166 for (int i = 0; i < disks.length; i++) { 167 writables[i] = disks[i].writable; 168 images[i] = disks[i].imagePath; 169 } 170 pb.putBooleanArray(KEY_DISK_WRITABLES, writables); 171 pb.putStringArray(KEY_DISK_IMAGES, images); 172 } 173 pb.putPersistableBundle( 174 KEY_DISPLAY_CONFIG, 175 Optional.ofNullable(displayConfig) 176 .map(dc -> dc.toPersistableBundle()) 177 .orElse(null)); 178 pb.putBoolean(KEY_TOUCH, touch); 179 pb.putBoolean(KEY_KEYBOARD, keyboard); 180 pb.putBoolean(KEY_MOUSE, mouse); 181 pb.putPersistableBundle( 182 KEY_GPU, 183 Optional.ofNullable(gpuConfig).map(gc -> gc.toPersistableBundle()).orElse(null)); 184 return pb; 185 } 186 187 @Nullable getDisplayConfig()188 public DisplayConfig getDisplayConfig() { 189 return displayConfig; 190 } 191 192 @Nullable getGpuConfig()193 public GpuConfig getGpuConfig() { 194 return gpuConfig; 195 } 196 197 /** @hide */ 198 public static final class Disk { 199 private final boolean writable; 200 private final String imagePath; 201 Disk(boolean writable, String imagePath)202 private Disk(boolean writable, String imagePath) { 203 this.writable = writable; 204 this.imagePath = imagePath; 205 } 206 207 /** @hide */ RWDisk(String imagePath)208 public static Disk RWDisk(String imagePath) { 209 return new Disk(true, imagePath); 210 } 211 212 /** @hide */ RODisk(String imagePath)213 public static Disk RODisk(String imagePath) { 214 return new Disk(false, imagePath); 215 } 216 217 /** @hide */ isWritable()218 public boolean isWritable() { 219 return writable; 220 } 221 222 /** @hide */ getImagePath()223 public String getImagePath() { 224 return imagePath; 225 } 226 } 227 228 /** @hide */ 229 public static final class Builder { 230 private String name; 231 private String kernelPath; 232 private String initrdPath; 233 private String bootloaderPath; 234 private List<String> params = new ArrayList<>(); 235 private List<Disk> disks = new ArrayList<>(); 236 private DisplayConfig displayConfig; 237 private boolean touch; 238 private boolean keyboard; 239 private boolean mouse; 240 private GpuConfig gpuConfig; 241 242 /** @hide */ Builder()243 public Builder() {} 244 245 /** @hide */ setName(String name)246 public Builder setName(String name) { 247 this.name = name; 248 return this; 249 } 250 251 /** @hide */ setKernelPath(String kernelPath)252 public Builder setKernelPath(String kernelPath) { 253 this.kernelPath = kernelPath; 254 return this; 255 } 256 257 /** @hide */ setBootloaderPath(String bootloaderPath)258 public Builder setBootloaderPath(String bootloaderPath) { 259 this.bootloaderPath = bootloaderPath; 260 return this; 261 } 262 263 /** @hide */ setInitrdPath(String initrdPath)264 public Builder setInitrdPath(String initrdPath) { 265 this.initrdPath = initrdPath; 266 return this; 267 } 268 269 /** @hide */ addDisk(Disk disk)270 public Builder addDisk(Disk disk) { 271 this.disks.add(disk); 272 return this; 273 } 274 275 /** @hide */ addParam(String param)276 public Builder addParam(String param) { 277 this.params.add(param); 278 return this; 279 } 280 281 /** @hide */ setDisplayConfig(DisplayConfig displayConfig)282 public Builder setDisplayConfig(DisplayConfig displayConfig) { 283 this.displayConfig = displayConfig; 284 return this; 285 } 286 287 /** @hide */ setGpuConfig(GpuConfig gpuConfig)288 public Builder setGpuConfig(GpuConfig gpuConfig) { 289 this.gpuConfig = gpuConfig; 290 return this; 291 } 292 293 /** @hide */ useTouch(boolean touch)294 public Builder useTouch(boolean touch) { 295 this.touch = touch; 296 return this; 297 } 298 299 /** @hide */ useKeyboard(boolean keyboard)300 public Builder useKeyboard(boolean keyboard) { 301 this.keyboard = keyboard; 302 return this; 303 } 304 305 /** @hide */ useMouse(boolean mouse)306 public Builder useMouse(boolean mouse) { 307 this.mouse = mouse; 308 return this; 309 } 310 311 /** @hide */ build()312 public VirtualMachineCustomImageConfig build() { 313 return new VirtualMachineCustomImageConfig( 314 this.name, 315 this.kernelPath, 316 this.initrdPath, 317 this.bootloaderPath, 318 this.params.toArray(new String[0]), 319 this.disks.toArray(new Disk[0]), 320 displayConfig, 321 touch, 322 keyboard, 323 mouse, 324 gpuConfig); 325 } 326 } 327 328 /** @hide */ 329 public static final class DisplayConfig { 330 private static final String KEY_WIDTH = "width"; 331 private static final String KEY_HEIGHT = "height"; 332 private static final String KEY_HORIZONTAL_DPI = "horizontal_dpi"; 333 private static final String KEY_VERTICAL_DPI = "vertical_dpi"; 334 private static final String KEY_REFRESH_RATE = "refresh_rate"; 335 private final int width; 336 private final int height; 337 private final int horizontalDpi; 338 private final int verticalDpi; 339 private final int refreshRate; 340 DisplayConfig( int width, int height, int horizontalDpi, int verticalDpi, int refreshRate)341 private DisplayConfig( 342 int width, int height, int horizontalDpi, int verticalDpi, int refreshRate) { 343 this.width = width; 344 this.height = height; 345 this.horizontalDpi = horizontalDpi; 346 this.verticalDpi = verticalDpi; 347 this.refreshRate = refreshRate; 348 } 349 350 /** @hide */ getWidth()351 public int getWidth() { 352 return width; 353 } 354 355 /** @hide */ getHeight()356 public int getHeight() { 357 return height; 358 } 359 360 /** @hide */ getHorizontalDpi()361 public int getHorizontalDpi() { 362 return horizontalDpi; 363 } 364 365 /** @hide */ getVerticalDpi()366 public int getVerticalDpi() { 367 return verticalDpi; 368 } 369 370 /** @hide */ getRefreshRate()371 public int getRefreshRate() { 372 return refreshRate; 373 } 374 toParcelable()375 android.system.virtualizationservice.DisplayConfig toParcelable() { 376 android.system.virtualizationservice.DisplayConfig parcelable = 377 new android.system.virtualizationservice.DisplayConfig(); 378 parcelable.width = this.width; 379 parcelable.height = this.height; 380 parcelable.horizontalDpi = this.horizontalDpi; 381 parcelable.verticalDpi = this.verticalDpi; 382 parcelable.refreshRate = this.refreshRate; 383 384 return parcelable; 385 } 386 from(PersistableBundle pb)387 private static DisplayConfig from(PersistableBundle pb) { 388 if (pb == null) { 389 return null; 390 } 391 Builder builder = new Builder(); 392 builder.setWidth(pb.getInt(KEY_WIDTH)); 393 builder.setHeight(pb.getInt(KEY_HEIGHT)); 394 builder.setHorizontalDpi(pb.getInt(KEY_HORIZONTAL_DPI)); 395 builder.setVerticalDpi(pb.getInt(KEY_VERTICAL_DPI)); 396 builder.setRefreshRate(pb.getInt(KEY_REFRESH_RATE)); 397 return builder.build(); 398 } 399 toPersistableBundle()400 private PersistableBundle toPersistableBundle() { 401 PersistableBundle pb = new PersistableBundle(); 402 pb.putInt(KEY_WIDTH, this.width); 403 pb.putInt(KEY_HEIGHT, this.height); 404 pb.putInt(KEY_HORIZONTAL_DPI, this.horizontalDpi); 405 pb.putInt(KEY_VERTICAL_DPI, this.verticalDpi); 406 pb.putInt(KEY_REFRESH_RATE, this.refreshRate); 407 return pb; 408 } 409 410 /** @hide */ 411 public static class Builder { 412 // Default values come from external/crosvm/vm_control/src/gpu.rs 413 private int width; 414 private int height; 415 private int horizontalDpi = 320; 416 private int verticalDpi = 320; 417 private int refreshRate = 60; 418 419 /** @hide */ Builder()420 public Builder() {} 421 422 /** @hide */ setWidth(int width)423 public Builder setWidth(int width) { 424 this.width = width; 425 return this; 426 } 427 428 /** @hide */ setHeight(int height)429 public Builder setHeight(int height) { 430 this.height = height; 431 return this; 432 } 433 434 /** @hide */ setHorizontalDpi(int horizontalDpi)435 public Builder setHorizontalDpi(int horizontalDpi) { 436 this.horizontalDpi = horizontalDpi; 437 return this; 438 } 439 440 /** @hide */ setVerticalDpi(int verticalDpi)441 public Builder setVerticalDpi(int verticalDpi) { 442 this.verticalDpi = verticalDpi; 443 return this; 444 } 445 446 /** @hide */ setRefreshRate(int refreshRate)447 public Builder setRefreshRate(int refreshRate) { 448 this.refreshRate = refreshRate; 449 return this; 450 } 451 452 /** @hide */ build()453 public DisplayConfig build() { 454 if (this.width == 0 || this.height == 0) { 455 throw new IllegalStateException("width and height must be specified"); 456 } 457 return new DisplayConfig(width, height, horizontalDpi, verticalDpi, refreshRate); 458 } 459 } 460 } 461 462 /** @hide */ 463 public static final class GpuConfig { 464 private static final String KEY_BACKEND = "backend"; 465 private static final String KEY_CONTEXT_TYPES = "context_types"; 466 private static final String KEY_PCI_ADDRESS = "pci_address"; 467 private static final String KEY_RENDERER_FEATURES = "renderer_features"; 468 private static final String KEY_RENDERER_USE_EGL = "renderer_use_egl"; 469 private static final String KEY_RENDERER_USE_GLES = "renderer_use_gles"; 470 private static final String KEY_RENDERER_USE_GLX = "renderer_use_glx"; 471 private static final String KEY_RENDERER_USE_SURFACELESS = "renderer_use_surfaceless"; 472 private static final String KEY_RENDERER_USE_VULKAN = "renderer_use_vulkan"; 473 474 private final String backend; 475 private final String[] contextTypes; 476 private final String pciAddress; 477 private final String rendererFeatures; 478 private final boolean rendererUseEgl; 479 private final boolean rendererUseGles; 480 private final boolean rendererUseGlx; 481 private final boolean rendererUseSurfaceless; 482 private final boolean rendererUseVulkan; 483 GpuConfig( String backend, String[] contextTypes, String pciAddress, String rendererFeatures, boolean rendererUseEgl, boolean rendererUseGles, boolean rendererUseGlx, boolean rendererUseSurfaceless, boolean rendererUseVulkan)484 private GpuConfig( 485 String backend, 486 String[] contextTypes, 487 String pciAddress, 488 String rendererFeatures, 489 boolean rendererUseEgl, 490 boolean rendererUseGles, 491 boolean rendererUseGlx, 492 boolean rendererUseSurfaceless, 493 boolean rendererUseVulkan) { 494 this.backend = backend; 495 this.contextTypes = contextTypes; 496 this.pciAddress = pciAddress; 497 this.rendererFeatures = rendererFeatures; 498 this.rendererUseEgl = rendererUseEgl; 499 this.rendererUseGles = rendererUseGles; 500 this.rendererUseGlx = rendererUseGlx; 501 this.rendererUseSurfaceless = rendererUseSurfaceless; 502 this.rendererUseVulkan = rendererUseVulkan; 503 } 504 505 /** @hide */ getBackend()506 public String getBackend() { 507 return backend; 508 } 509 510 /** @hide */ getContextTypes()511 public String[] getContextTypes() { 512 return contextTypes; 513 } 514 515 /** @hide */ getPciAddress()516 public String getPciAddress() { 517 return pciAddress; 518 } 519 520 /** @hide */ getRendererFeatures()521 public String getRendererFeatures() { 522 return rendererFeatures; 523 } 524 525 /** @hide */ getRendererUseEgl()526 public boolean getRendererUseEgl() { 527 return rendererUseEgl; 528 } 529 530 /** @hide */ getRendererUseGles()531 public boolean getRendererUseGles() { 532 return rendererUseGles; 533 } 534 535 /** @hide */ getRendererUseGlx()536 public boolean getRendererUseGlx() { 537 return rendererUseGlx; 538 } 539 540 /** @hide */ getRendererUseSurfaceless()541 public boolean getRendererUseSurfaceless() { 542 return rendererUseSurfaceless; 543 } 544 545 /** @hide */ getRendererUseVulkan()546 public boolean getRendererUseVulkan() { 547 return rendererUseVulkan; 548 } 549 toParcelable()550 android.system.virtualizationservice.GpuConfig toParcelable() { 551 android.system.virtualizationservice.GpuConfig parcelable = 552 new android.system.virtualizationservice.GpuConfig(); 553 parcelable.backend = this.backend; 554 parcelable.contextTypes = this.contextTypes; 555 parcelable.pciAddress = this.pciAddress; 556 parcelable.rendererFeatures = this.rendererFeatures; 557 parcelable.rendererUseEgl = this.rendererUseEgl; 558 parcelable.rendererUseGles = this.rendererUseGles; 559 parcelable.rendererUseGlx = this.rendererUseGlx; 560 parcelable.rendererUseSurfaceless = this.rendererUseSurfaceless; 561 parcelable.rendererUseVulkan = this.rendererUseVulkan; 562 return parcelable; 563 } 564 from(PersistableBundle pb)565 private static GpuConfig from(PersistableBundle pb) { 566 if (pb == null) { 567 return null; 568 } 569 Builder builder = new Builder(); 570 builder.setBackend(pb.getString(KEY_BACKEND)); 571 builder.setContextTypes(pb.getStringArray(KEY_CONTEXT_TYPES)); 572 builder.setPciAddress(pb.getString(KEY_PCI_ADDRESS)); 573 builder.setRendererFeatures(pb.getString(KEY_RENDERER_FEATURES)); 574 builder.setRendererUseEgl(pb.getBoolean(KEY_RENDERER_USE_EGL)); 575 builder.setRendererUseGles(pb.getBoolean(KEY_RENDERER_USE_GLES)); 576 builder.setRendererUseGlx(pb.getBoolean(KEY_RENDERER_USE_GLX)); 577 builder.setRendererUseSurfaceless(pb.getBoolean(KEY_RENDERER_USE_SURFACELESS)); 578 builder.setRendererUseVulkan(pb.getBoolean(KEY_RENDERER_USE_VULKAN)); 579 return builder.build(); 580 } 581 toPersistableBundle()582 private PersistableBundle toPersistableBundle() { 583 PersistableBundle pb = new PersistableBundle(); 584 pb.putString(KEY_BACKEND, this.backend); 585 pb.putStringArray(KEY_CONTEXT_TYPES, this.contextTypes); 586 pb.putString(KEY_PCI_ADDRESS, this.pciAddress); 587 pb.putString(KEY_RENDERER_FEATURES, this.rendererFeatures); 588 pb.putBoolean(KEY_RENDERER_USE_EGL, this.rendererUseEgl); 589 pb.putBoolean(KEY_RENDERER_USE_GLES, this.rendererUseGles); 590 pb.putBoolean(KEY_RENDERER_USE_GLX, this.rendererUseGlx); 591 pb.putBoolean(KEY_RENDERER_USE_SURFACELESS, this.rendererUseSurfaceless); 592 pb.putBoolean(KEY_RENDERER_USE_VULKAN, this.rendererUseVulkan); 593 return pb; 594 } 595 596 /** @hide */ 597 public static class Builder { 598 private String backend; 599 private String[] contextTypes; 600 private String pciAddress; 601 private String rendererFeatures; 602 private boolean rendererUseEgl = true; 603 private boolean rendererUseGles = true; 604 private boolean rendererUseGlx = false; 605 private boolean rendererUseSurfaceless = true; 606 private boolean rendererUseVulkan = false; 607 608 /** @hide */ Builder()609 public Builder() {} 610 611 /** @hide */ setBackend(String backend)612 public Builder setBackend(String backend) { 613 this.backend = backend; 614 return this; 615 } 616 617 /** @hide */ setContextTypes(String[] contextTypes)618 public Builder setContextTypes(String[] contextTypes) { 619 this.contextTypes = contextTypes; 620 return this; 621 } 622 623 /** @hide */ setPciAddress(String pciAddress)624 public Builder setPciAddress(String pciAddress) { 625 this.pciAddress = pciAddress; 626 return this; 627 } 628 629 /** @hide */ setRendererFeatures(String rendererFeatures)630 public Builder setRendererFeatures(String rendererFeatures) { 631 this.rendererFeatures = rendererFeatures; 632 return this; 633 } 634 635 /** @hide */ setRendererUseEgl(Boolean rendererUseEgl)636 public Builder setRendererUseEgl(Boolean rendererUseEgl) { 637 this.rendererUseEgl = rendererUseEgl; 638 return this; 639 } 640 641 /** @hide */ setRendererUseGles(Boolean rendererUseGles)642 public Builder setRendererUseGles(Boolean rendererUseGles) { 643 this.rendererUseGles = rendererUseGles; 644 return this; 645 } 646 647 /** @hide */ setRendererUseGlx(Boolean rendererUseGlx)648 public Builder setRendererUseGlx(Boolean rendererUseGlx) { 649 this.rendererUseGlx = rendererUseGlx; 650 return this; 651 } 652 653 /** @hide */ setRendererUseSurfaceless(Boolean rendererUseSurfaceless)654 public Builder setRendererUseSurfaceless(Boolean rendererUseSurfaceless) { 655 this.rendererUseSurfaceless = rendererUseSurfaceless; 656 return this; 657 } 658 659 /** @hide */ setRendererUseVulkan(Boolean rendererUseVulkan)660 public Builder setRendererUseVulkan(Boolean rendererUseVulkan) { 661 this.rendererUseVulkan = rendererUseVulkan; 662 return this; 663 } 664 665 /** @hide */ build()666 public GpuConfig build() { 667 return new GpuConfig( 668 backend, 669 contextTypes, 670 pciAddress, 671 rendererFeatures, 672 rendererUseEgl, 673 rendererUseGles, 674 rendererUseGlx, 675 rendererUseSurfaceless, 676 rendererUseVulkan); 677 } 678 } 679 } 680 } 681