1 /* 2 * Copyright (C) 2021 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 static java.util.Objects.requireNonNull; 20 21 import android.annotation.FlaggedApi; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresFeature; 26 import android.annotation.RequiresPermission; 27 import android.annotation.StringDef; 28 import android.annotation.SystemApi; 29 import android.annotation.TestApi; 30 import android.annotation.WorkerThread; 31 import android.content.Context; 32 import android.content.pm.PackageManager; 33 import android.os.RemoteException; 34 import android.sysprop.HypervisorProperties; 35 import android.system.virtualizationservice.IVirtualizationService; 36 import android.util.ArrayMap; 37 38 import com.android.internal.annotations.GuardedBy; 39 import com.android.system.virtualmachine.flags.Flags; 40 41 import java.io.File; 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.lang.ref.WeakReference; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.List; 48 import java.util.Map; 49 50 /** 51 * Manages {@linkplain VirtualMachine virtual machine} instances created by an app. Each instance is 52 * created from a {@linkplain VirtualMachineConfig configuration} that defines the shape of the VM 53 * (RAM, CPUs), the code to execute within it, etc. 54 * 55 * <p>Each virtual machine instance is named; the configuration and related state of each is 56 * persisted in the app's private data directory and an instance can be retrieved given the name. 57 * The name must be a valid directory name and must not contain '/'. 58 * 59 * <p>The app can then start, stop and otherwise interact with the VM. 60 * 61 * <p>An instance of {@link VirtualMachineManager} can be obtained by calling {@link 62 * Context#getSystemService(Class)}. 63 * 64 * @hide 65 */ 66 @SystemApi 67 @RequiresFeature(PackageManager.FEATURE_VIRTUALIZATION_FRAMEWORK) 68 public class VirtualMachineManager { 69 /** 70 * A lock used to synchronize the creation of virtual machines. It protects {@link #mVmsByName}, 71 * but is also held throughout VM creation / retrieval / deletion, to prevent these actions 72 * racing with each other. 73 */ 74 private static final Object sCreateLock = new Object(); 75 76 @NonNull private final Context mContext; 77 78 /** @hide */ VirtualMachineManager(@onNull Context context)79 public VirtualMachineManager(@NonNull Context context) { 80 mContext = requireNonNull(context); 81 } 82 83 @GuardedBy("sCreateLock") 84 private final Map<String, WeakReference<VirtualMachine>> mVmsByName = new ArrayMap<>(); 85 86 /** 87 * Capabilities of the virtual machine implementation. 88 * 89 * @hide 90 */ 91 @Retention(RetentionPolicy.SOURCE) 92 @IntDef(prefix = "CAPABILITY_", flag = true, value = { 93 CAPABILITY_PROTECTED_VM, 94 CAPABILITY_NON_PROTECTED_VM 95 }) 96 public @interface Capability {} 97 98 /** 99 * The implementation supports creating protected VMs, whose memory is inaccessible to the host 100 * OS. 101 * 102 * @see VirtualMachineConfig.Builder#setProtectedVm 103 */ 104 public static final int CAPABILITY_PROTECTED_VM = 1; 105 106 /** 107 * The implementation supports creating non-protected VMs, whose memory is accessible to the 108 * host OS. 109 * 110 * @see VirtualMachineConfig.Builder#setProtectedVm 111 */ 112 public static final int CAPABILITY_NON_PROTECTED_VM = 2; 113 114 /** 115 * Features provided by {@link VirtualMachineManager}. 116 * 117 * @hide 118 */ 119 @Retention(RetentionPolicy.SOURCE) 120 @StringDef( 121 prefix = "FEATURE_", 122 value = { 123 FEATURE_DICE_CHANGES, 124 FEATURE_LLPVM_CHANGES, 125 FEATURE_MULTI_TENANT, 126 FEATURE_NETWORK, 127 FEATURE_REMOTE_ATTESTATION, 128 FEATURE_VENDOR_MODULES, 129 }) 130 public @interface Features {} 131 132 /** 133 * Feature to include new data in the VM DICE chain. 134 * 135 * @hide 136 */ 137 @TestApi 138 @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS) 139 public static final String FEATURE_DICE_CHANGES = IVirtualizationService.FEATURE_DICE_CHANGES; 140 141 /** 142 * Feature to run payload as non-root user. 143 * 144 * @hide 145 */ 146 @TestApi 147 @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS) 148 public static final String FEATURE_MULTI_TENANT = IVirtualizationService.FEATURE_MULTI_TENANT; 149 150 /** 151 * Feature to allow network features in VM. 152 * 153 * @hide 154 */ 155 @TestApi public static final String FEATURE_NETWORK = IVirtualizationService.FEATURE_NETWORK; 156 157 /** 158 * Feature to allow remote attestation in Microdroid. 159 * 160 * @hide 161 */ 162 @TestApi 163 @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS) 164 public static final String FEATURE_REMOTE_ATTESTATION = 165 IVirtualizationService.FEATURE_REMOTE_ATTESTATION; 166 167 /** 168 * Feature to allow vendor modules in Microdroid. 169 * 170 * @hide 171 */ 172 @TestApi 173 @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS) 174 public static final String FEATURE_VENDOR_MODULES = 175 IVirtualizationService.FEATURE_VENDOR_MODULES; 176 177 /** 178 * Feature to enable Secretkeeper protected secrets in Microdroid based pVMs. 179 * 180 * @hide 181 */ 182 @TestApi 183 @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS) 184 public static final String FEATURE_LLPVM_CHANGES = IVirtualizationService.FEATURE_LLPVM_CHANGES; 185 186 /** 187 * Returns a set of flags indicating what this implementation of virtualization is capable of. 188 * 189 * @see #CAPABILITY_PROTECTED_VM 190 * @see #CAPABILITY_NON_PROTECTED_VM 191 * @hide 192 */ 193 @SystemApi 194 @Capability getCapabilities()195 public int getCapabilities() { 196 @Capability int result = 0; 197 if (HypervisorProperties.hypervisor_protected_vm_supported().orElse(false)) { 198 result |= CAPABILITY_PROTECTED_VM; 199 } 200 if (HypervisorProperties.hypervisor_vm_supported().orElse(false)) { 201 result |= CAPABILITY_NON_PROTECTED_VM; 202 } 203 return result; 204 } 205 206 /** 207 * Creates a new {@link VirtualMachine} with the given name and config. Creating a virtual 208 * machine with the same name as an existing virtual machine is an error. The existing virtual 209 * machine has to be deleted before its name can be reused. 210 * 211 * <p>Each successful call to this method creates a new (and different) virtual machine even if 212 * the name and the config are the same as a deleted one. The new virtual machine will initially 213 * be stopped. 214 * 215 * <p>NOTE: This method may block and should not be called on the main thread. 216 * 217 * @throws VirtualMachineException if the VM cannot be created, or there is an existing VM with 218 * the given name. 219 * @hide 220 */ 221 @SystemApi 222 @NonNull 223 @WorkerThread 224 @RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) create(@onNull String name, @NonNull VirtualMachineConfig config)225 public VirtualMachine create(@NonNull String name, @NonNull VirtualMachineConfig config) 226 throws VirtualMachineException { 227 synchronized (sCreateLock) { 228 return createLocked(name, config); 229 } 230 } 231 232 @NonNull 233 @GuardedBy("sCreateLock") createLocked(@onNull String name, @NonNull VirtualMachineConfig config)234 private VirtualMachine createLocked(@NonNull String name, @NonNull VirtualMachineConfig config) 235 throws VirtualMachineException { 236 VirtualMachine vm = VirtualMachine.create(mContext, name, config); 237 mVmsByName.put(name, new WeakReference<>(vm)); 238 return vm; 239 } 240 241 /** 242 * Returns an existing {@link VirtualMachine} with the given name. Returns null if there is no 243 * such virtual machine. 244 * 245 * <p>There is at most one {@code VirtualMachine} object corresponding to a given virtual 246 * machine instance. Multiple calls to get() passing the same name will get the same object 247 * returned, until the virtual machine is deleted (via {@link #delete}) and then recreated. 248 * 249 * <p>NOTE: This method may block and should not be called on the main thread. 250 * 251 * @see #getOrCreate 252 * @throws VirtualMachineException if the virtual machine exists but could not be successfully 253 * retrieved. This can be resolved by calling {@link #delete} on the VM. 254 * @hide 255 */ 256 @SystemApi 257 @WorkerThread 258 @Nullable get(@onNull String name)259 public VirtualMachine get(@NonNull String name) throws VirtualMachineException { 260 synchronized (sCreateLock) { 261 return getLocked(name); 262 } 263 } 264 265 @Nullable 266 @GuardedBy("sCreateLock") getLocked(@onNull String name)267 private VirtualMachine getLocked(@NonNull String name) throws VirtualMachineException { 268 VirtualMachine vm = getVmByName(name); 269 if (vm != null) return vm; 270 271 vm = VirtualMachine.load(mContext, name); 272 if (vm != null) { 273 mVmsByName.put(name, new WeakReference<>(vm)); 274 } 275 return vm; 276 } 277 278 /** 279 * Imports a virtual machine from an {@link VirtualMachineDescriptor} object and associates it 280 * with the given name. 281 * 282 * <p>The new virtual machine will be in the same state as the descriptor indicates. The 283 * descriptor is automatically closed and cannot be used again. 284 * 285 * <p>NOTE: This method may block and should not be called on the main thread. 286 * 287 * @throws VirtualMachineException if the VM cannot be imported or the {@code 288 * VirtualMachineDescriptor} has already been closed. 289 * @hide 290 */ 291 @NonNull 292 @SystemApi 293 @WorkerThread importFromDescriptor( @onNull String name, @NonNull VirtualMachineDescriptor vmDescriptor)294 public VirtualMachine importFromDescriptor( 295 @NonNull String name, @NonNull VirtualMachineDescriptor vmDescriptor) 296 throws VirtualMachineException { 297 VirtualMachine vm; 298 synchronized (sCreateLock) { 299 vm = VirtualMachine.fromDescriptor(mContext, name, vmDescriptor); 300 mVmsByName.put(name, new WeakReference<>(vm)); 301 } 302 return vm; 303 } 304 305 /** 306 * Returns an existing {@link VirtualMachine} if it exists, or create a new one. The config 307 * parameter is used only when a new virtual machine is created. 308 * 309 * <p>NOTE: This method may block and should not be called on the main thread. 310 * 311 * @throws VirtualMachineException if the virtual machine could not be created or retrieved. 312 * @hide 313 */ 314 @SystemApi 315 @WorkerThread 316 @NonNull getOrCreate(@onNull String name, @NonNull VirtualMachineConfig config)317 public VirtualMachine getOrCreate(@NonNull String name, @NonNull VirtualMachineConfig config) 318 throws VirtualMachineException { 319 synchronized (sCreateLock) { 320 VirtualMachine vm = getLocked(name); 321 if (vm != null) { 322 return vm; 323 } else { 324 return createLocked(name, config); 325 } 326 } 327 } 328 329 /** 330 * Deletes an existing {@link VirtualMachine}. Deleting a virtual machine means deleting any 331 * persisted data associated with it including the per-VM secret. This is an irreversible 332 * action. A virtual machine once deleted can never be restored. A new virtual machine created 333 * with the same name is different from an already deleted virtual machine even if it has the 334 * same config. 335 * 336 * <p>NOTE: This method may block and should not be called on the main thread. 337 * 338 * @throws VirtualMachineException if the virtual machine does not exist, is not stopped, or 339 * cannot be deleted. 340 * @hide 341 */ 342 @SystemApi 343 @WorkerThread delete(@onNull String name)344 public void delete(@NonNull String name) throws VirtualMachineException { 345 synchronized (sCreateLock) { 346 VirtualMachine vm = getVmByName(name); 347 if (vm == null) { 348 VirtualMachine.vmInstanceCleanup(mContext, name); 349 } else { 350 vm.delete(mContext, name); 351 } 352 mVmsByName.remove(name); 353 } 354 } 355 356 @Nullable 357 @GuardedBy("sCreateLock") getVmByName(@onNull String name)358 private VirtualMachine getVmByName(@NonNull String name) { 359 requireNonNull(name); 360 WeakReference<VirtualMachine> weakReference = mVmsByName.get(name); 361 if (weakReference != null) { 362 VirtualMachine vm = weakReference.get(); 363 if (vm != null && vm.getStatus() != VirtualMachine.STATUS_DELETED) { 364 return vm; 365 } 366 } 367 return null; 368 } 369 370 private static final String JSON_SUFFIX = ".json"; 371 private static final List<String> SUPPORTED_OS_LIST_FROM_CFG = 372 extractSupportedOSListFromConfig(); 373 isVendorModuleEnabled()374 private boolean isVendorModuleEnabled() { 375 return VirtualizationService.nativeIsVendorModulesFlagEnabled(); 376 } 377 extractSupportedOSListFromConfig()378 private static List<String> extractSupportedOSListFromConfig() { 379 List<String> supportedOsList = new ArrayList<>(); 380 File directory = new File("/apex/com.android.virt/etc"); 381 File[] files = directory.listFiles(); 382 if (files != null) { 383 for (File file : files) { 384 String fileName = file.getName(); 385 if (fileName.endsWith(JSON_SUFFIX)) { 386 supportedOsList.add( 387 fileName.substring(0, fileName.length() - JSON_SUFFIX.length())); 388 } 389 } 390 } 391 return supportedOsList; 392 } 393 394 /** 395 * Returns a list of supported OS names. 396 * 397 * @hide 398 */ 399 @TestApi 400 @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS) 401 @NonNull getSupportedOSList()402 public List<String> getSupportedOSList() throws VirtualMachineException { 403 if (isVendorModuleEnabled()) { 404 return SUPPORTED_OS_LIST_FROM_CFG; 405 } else { 406 return Arrays.asList("microdroid"); 407 } 408 } 409 410 /** 411 * Returns {@code true} if given {@code featureName} is enabled. 412 * 413 * @hide 414 */ 415 @TestApi 416 @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS) 417 @RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) isFeatureEnabled(@eatures String featureName)418 public boolean isFeatureEnabled(@Features String featureName) throws VirtualMachineException { 419 synchronized (sCreateLock) { 420 VirtualizationService service = VirtualizationService.getInstance(); 421 try { 422 return service.getBinder().isFeatureEnabled(featureName); 423 } catch (RemoteException e) { 424 throw e.rethrowAsRuntimeException(); 425 } 426 } 427 } 428 429 /** 430 * Returns {@code true} if the pVM remote attestation feature is supported. Remote attestation 431 * allows a protected VM to attest its authenticity to a remote server. 432 * 433 * @hide 434 */ 435 @TestApi 436 @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS) 437 @RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) isRemoteAttestationSupported()438 public boolean isRemoteAttestationSupported() throws VirtualMachineException { 439 synchronized (sCreateLock) { 440 VirtualizationService service = VirtualizationService.getInstance(); 441 try { 442 return service.getBinder().isRemoteAttestationSupported(); 443 } catch (RemoteException e) { 444 throw e.rethrowAsRuntimeException(); 445 } 446 } 447 } 448 449 /** 450 * Returns {@code true} if Updatable VM feature is supported by AVF. Updatable VM allow secrets 451 * and data to be accessible even after updates of boot images and apks. For more info see 452 * packages/modules/Virtualization/docs/updatable_vm.md 453 * 454 * @hide 455 */ 456 @TestApi 457 @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS) 458 @RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) isUpdatableVmSupported()459 public boolean isUpdatableVmSupported() throws VirtualMachineException { 460 synchronized (sCreateLock) { 461 VirtualizationService service = VirtualizationService.getInstance(); 462 try { 463 return service.getBinder().isUpdatableVmSupported(); 464 } catch (RemoteException e) { 465 throw e.rethrowAsRuntimeException(); 466 } 467 } 468 } 469 } 470