• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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