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.SystemApi; 27 import android.annotation.WorkerThread; 28 import android.content.Context; 29 import android.content.pm.PackageManager; 30 import android.sysprop.HypervisorProperties; 31 import android.util.ArrayMap; 32 33 import com.android.internal.annotations.GuardedBy; 34 35 import java.lang.annotation.Retention; 36 import java.lang.annotation.RetentionPolicy; 37 import java.lang.ref.WeakReference; 38 import java.util.Map; 39 40 /** 41 * Manages {@linkplain VirtualMachine virtual machine} instances created by an app. Each instance is 42 * created from a {@linkplain VirtualMachineConfig configuration} that defines the shape of the VM 43 * (RAM, CPUs), the code to execute within it, etc. 44 * 45 * <p>Each virtual machine instance is named; the configuration and related state of each is 46 * persisted in the app's private data directory and an instance can be retrieved given the name. 47 * The name must be a valid directory name and must not contain '/'. 48 * 49 * <p>The app can then start, stop and otherwise interact with the VM. 50 * 51 * <p>An instance of {@link VirtualMachineManager} can be obtained by calling {@link 52 * Context#getSystemService(Class)}. 53 * 54 * @hide 55 */ 56 @SystemApi 57 @RequiresFeature(PackageManager.FEATURE_VIRTUALIZATION_FRAMEWORK) 58 public class VirtualMachineManager { 59 /** 60 * A lock used to synchronize the creation of virtual machines. It protects {@link #mVmsByName}, 61 * but is also held throughout VM creation / retrieval / deletion, to prevent these actions 62 * racing with each other. 63 */ 64 private static final Object sCreateLock = new Object(); 65 66 @NonNull private final Context mContext; 67 68 /** @hide */ VirtualMachineManager(@onNull Context context)69 public VirtualMachineManager(@NonNull Context context) { 70 mContext = requireNonNull(context); 71 } 72 73 @GuardedBy("sCreateLock") 74 private final Map<String, WeakReference<VirtualMachine>> mVmsByName = new ArrayMap<>(); 75 76 /** 77 * Capabilities of the virtual machine implementation. 78 * 79 * @hide 80 */ 81 @Retention(RetentionPolicy.SOURCE) 82 @IntDef(prefix = "CAPABILITY_", flag = true, value = { 83 CAPABILITY_PROTECTED_VM, 84 CAPABILITY_NON_PROTECTED_VM 85 }) 86 public @interface Capability {} 87 88 /* The implementation supports creating protected VMs, whose memory is inaccessible to the 89 * host OS. 90 */ 91 public static final int CAPABILITY_PROTECTED_VM = 1; 92 93 /* The implementation supports creating non-protected VMs, whose memory is accessible to the 94 * host OS. 95 */ 96 public static final int CAPABILITY_NON_PROTECTED_VM = 2; 97 98 /** 99 * Returns a set of flags indicating what this implementation of virtualization is capable of. 100 * 101 * @see #CAPABILITY_PROTECTED_VM 102 * @see #CAPABILITY_NON_PROTECTED_VM 103 * @hide 104 */ 105 @SystemApi 106 @Capability getCapabilities()107 public int getCapabilities() { 108 @Capability int result = 0; 109 if (HypervisorProperties.hypervisor_protected_vm_supported().orElse(false)) { 110 result |= CAPABILITY_PROTECTED_VM; 111 } 112 if (HypervisorProperties.hypervisor_vm_supported().orElse(false)) { 113 result |= CAPABILITY_NON_PROTECTED_VM; 114 } 115 return result; 116 } 117 118 /** 119 * Creates a new {@link VirtualMachine} with the given name and config. Creating a virtual 120 * machine with the same name as an existing virtual machine is an error. The existing virtual 121 * machine has to be deleted before its name can be reused. 122 * 123 * <p>Each successful call to this method creates a new (and different) virtual machine even if 124 * the name and the config are the same as a deleted one. The new virtual machine will initially 125 * be stopped. 126 * 127 * <p>NOTE: This method may block and should not be called on the main thread. 128 * 129 * @throws VirtualMachineException if the VM cannot be created, or there is an existing VM with 130 * the given name. 131 * @hide 132 */ 133 @SystemApi 134 @NonNull 135 @WorkerThread 136 @RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) create(@onNull String name, @NonNull VirtualMachineConfig config)137 public VirtualMachine create(@NonNull String name, @NonNull VirtualMachineConfig config) 138 throws VirtualMachineException { 139 synchronized (sCreateLock) { 140 return createLocked(name, config); 141 } 142 } 143 144 @NonNull 145 @GuardedBy("sCreateLock") createLocked(@onNull String name, @NonNull VirtualMachineConfig config)146 private VirtualMachine createLocked(@NonNull String name, @NonNull VirtualMachineConfig config) 147 throws VirtualMachineException { 148 VirtualMachine vm = VirtualMachine.create(mContext, name, config); 149 mVmsByName.put(name, new WeakReference<>(vm)); 150 return vm; 151 } 152 153 /** 154 * Returns an existing {@link VirtualMachine} with the given name. Returns null if there is no 155 * such virtual machine. 156 * 157 * <p>There is at most one {@code VirtualMachine} object corresponding to a given virtual 158 * machine instance. Multiple calls to get() passing the same name will get the same object 159 * returned, until the virtual machine is deleted (via {@link #delete}) and then recreated. 160 * 161 * <p>NOTE: This method may block and should not be called on the main thread. 162 * 163 * @see #getOrCreate 164 * @throws VirtualMachineException if the virtual machine exists but could not be successfully 165 * retrieved. 166 * @hide 167 */ 168 @SystemApi 169 @WorkerThread 170 @Nullable get(@onNull String name)171 public VirtualMachine get(@NonNull String name) throws VirtualMachineException { 172 synchronized (sCreateLock) { 173 return getLocked(name); 174 } 175 } 176 177 @Nullable 178 @GuardedBy("sCreateLock") getLocked(@onNull String name)179 private VirtualMachine getLocked(@NonNull String name) throws VirtualMachineException { 180 VirtualMachine vm = getVmByName(name); 181 if (vm != null) return vm; 182 183 vm = VirtualMachine.load(mContext, name); 184 if (vm != null) { 185 mVmsByName.put(name, new WeakReference<>(vm)); 186 } 187 return vm; 188 } 189 190 /** 191 * Imports a virtual machine from an {@link VirtualMachineDescriptor} object and associates it 192 * with the given name. 193 * 194 * <p>The new virtual machine will be in the same state as the descriptor indicates. The 195 * descriptor is automatically closed and cannot be used again. 196 * 197 * <p>NOTE: This method may block and should not be called on the main thread. 198 * 199 * @throws VirtualMachineException if the VM cannot be imported or the {@code 200 * VirtualMachineDescriptor} has already been closed. 201 * @hide 202 */ 203 @NonNull 204 @SystemApi 205 @WorkerThread importFromDescriptor( @onNull String name, @NonNull VirtualMachineDescriptor vmDescriptor)206 public VirtualMachine importFromDescriptor( 207 @NonNull String name, @NonNull VirtualMachineDescriptor vmDescriptor) 208 throws VirtualMachineException { 209 synchronized (sCreateLock) { 210 VirtualMachine vm = VirtualMachine.fromDescriptor(mContext, name, vmDescriptor); 211 mVmsByName.put(name, new WeakReference<>(vm)); 212 return vm; 213 } 214 } 215 216 /** 217 * Returns an existing {@link VirtualMachine} if it exists, or create a new one. The config 218 * parameter is used only when a new virtual machine is created. 219 * 220 * <p>NOTE: This method may block and should not be called on the main thread. 221 * 222 * @throws VirtualMachineException if the virtual machine could not be created or retrieved. 223 * @hide 224 */ 225 @SystemApi 226 @WorkerThread 227 @NonNull getOrCreate(@onNull String name, @NonNull VirtualMachineConfig config)228 public VirtualMachine getOrCreate(@NonNull String name, @NonNull VirtualMachineConfig config) 229 throws VirtualMachineException { 230 synchronized (sCreateLock) { 231 VirtualMachine vm = getLocked(name); 232 if (vm != null) { 233 return vm; 234 } else { 235 return createLocked(name, config); 236 } 237 } 238 } 239 240 /** 241 * Deletes an existing {@link VirtualMachine}. Deleting a virtual machine means deleting any 242 * persisted data associated with it including the per-VM secret. This is an irreversible 243 * action. A virtual machine once deleted can never be restored. A new virtual machine created 244 * with the same name is different from an already deleted virtual machine even if it has the 245 * same config. 246 * 247 * <p>NOTE: This method may block and should not be called on the main thread. 248 * 249 * @throws VirtualMachineException if the virtual machine does not exist, is not stopped, or 250 * cannot be deleted. 251 * @hide 252 */ 253 @SystemApi 254 @WorkerThread delete(@onNull String name)255 public void delete(@NonNull String name) throws VirtualMachineException { 256 synchronized (sCreateLock) { 257 VirtualMachine vm = getVmByName(name); 258 if (vm == null) { 259 VirtualMachine.deleteVmDirectory(mContext, name); 260 } else { 261 vm.delete(mContext, name); 262 } 263 mVmsByName.remove(name); 264 } 265 } 266 267 @Nullable 268 @GuardedBy("sCreateLock") getVmByName(@onNull String name)269 private VirtualMachine getVmByName(@NonNull String name) { 270 requireNonNull(name); 271 WeakReference<VirtualMachine> weakReference = mVmsByName.get(name); 272 if (weakReference != null) { 273 VirtualMachine vm = weakReference.get(); 274 if (vm != null && vm.getStatus() != VirtualMachine.STATUS_DELETED) { 275 return vm; 276 } 277 } 278 return null; 279 } 280 } 281