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