1 /*
2  * Copyright 2022 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.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.os.Parcel;
25 import android.os.ParcelFileDescriptor;
26 import android.os.Parcelable;
27 
28 import java.io.IOException;
29 
30 /**
31  * A VM descriptor that captures the state of a Virtual Machine.
32  *
33  * <p>You can capture the current state of VM by creating an instance of this class with {@link
34  * VirtualMachine#toDescriptor}, optionally pass it to another App, and then build an identical VM
35  * with the descriptor received.
36  *
37  * @hide
38  */
39 @SystemApi
40 public final class VirtualMachineDescriptor implements Parcelable, AutoCloseable {
41     private volatile boolean mClosed = false;
42     @NonNull private final ParcelFileDescriptor mConfigFd;
43     // File descriptor of the file containing the instance id - will be null iff
44     // FEATURE_LLPVM_CHANGES is disabled.
45     @Nullable private final ParcelFileDescriptor mInstanceIdFd;
46     @NonNull private final ParcelFileDescriptor mInstanceImgFd;
47     // File descriptor of the image backing the encrypted storage - Will be null if encrypted
48     // storage is not enabled. */
49     @Nullable private final ParcelFileDescriptor mEncryptedStoreFd;
50 
51     @Override
describeContents()52     public int describeContents() {
53         return CONTENTS_FILE_DESCRIPTOR;
54     }
55 
56     @Override
writeToParcel(@onNull Parcel out, int flags)57     public void writeToParcel(@NonNull Parcel out, int flags) {
58         checkNotClosed();
59         out.writeParcelable(mConfigFd, flags);
60         out.writeParcelable(mInstanceIdFd, flags);
61         out.writeParcelable(mInstanceImgFd, flags);
62         out.writeParcelable(mEncryptedStoreFd, flags);
63     }
64 
65     @NonNull
66     public static final Parcelable.Creator<VirtualMachineDescriptor> CREATOR =
67             new Parcelable.Creator<>() {
68                 public VirtualMachineDescriptor createFromParcel(Parcel in) {
69                     return new VirtualMachineDescriptor(in);
70                 }
71 
72                 public VirtualMachineDescriptor[] newArray(int size) {
73                     return new VirtualMachineDescriptor[size];
74                 }
75             };
76 
77     /**
78      * @return File descriptor of the VM configuration file config.xml.
79      */
80     @NonNull
getConfigFd()81     ParcelFileDescriptor getConfigFd() {
82         checkNotClosed();
83         return mConfigFd;
84     }
85 
86     /**
87      * @return File descriptor of the file containing instance_id of the VM.
88      */
89     @Nullable
getInstanceIdFd()90     ParcelFileDescriptor getInstanceIdFd() {
91         checkNotClosed();
92         return mInstanceIdFd;
93     }
94 
95     /**
96      * @return File descriptor of the instance.img of the VM.
97      */
98     @NonNull
getInstanceImgFd()99     ParcelFileDescriptor getInstanceImgFd() {
100         checkNotClosed();
101         return mInstanceImgFd;
102     }
103 
104     /**
105      * @return File descriptor of image backing the encrypted storage.
106      *     <p>This method will return null if encrypted storage is not enabled.
107      */
108     @Nullable
getEncryptedStoreFd()109     ParcelFileDescriptor getEncryptedStoreFd() {
110         checkNotClosed();
111         return mEncryptedStoreFd;
112     }
113 
VirtualMachineDescriptor( @onNull ParcelFileDescriptor configFd, @Nullable ParcelFileDescriptor instanceIdFd, @NonNull ParcelFileDescriptor instanceImgFd, @Nullable ParcelFileDescriptor encryptedStoreFd)114     VirtualMachineDescriptor(
115             @NonNull ParcelFileDescriptor configFd,
116             @Nullable ParcelFileDescriptor instanceIdFd,
117             @NonNull ParcelFileDescriptor instanceImgFd,
118             @Nullable ParcelFileDescriptor encryptedStoreFd) {
119         mConfigFd = requireNonNull(configFd);
120         mInstanceIdFd = instanceIdFd;
121         mInstanceImgFd = requireNonNull(instanceImgFd);
122         mEncryptedStoreFd = encryptedStoreFd;
123     }
124 
VirtualMachineDescriptor(Parcel in)125     private VirtualMachineDescriptor(Parcel in) {
126         mConfigFd = requireNonNull(readParcelFileDescriptor(in));
127         mInstanceIdFd = readParcelFileDescriptor(in);
128         mInstanceImgFd = requireNonNull(readParcelFileDescriptor(in));
129         mEncryptedStoreFd = readParcelFileDescriptor(in);
130     }
131 
readParcelFileDescriptor(Parcel in)132     private ParcelFileDescriptor readParcelFileDescriptor(Parcel in) {
133         return in.readParcelable(
134                 ParcelFileDescriptor.class.getClassLoader(), ParcelFileDescriptor.class);
135     }
136 
137     /**
138      * Release any resources held by this descriptor. Calling {@code close} on an already-closed
139      * descriptor has no effect.
140      */
141     @Override
close()142     public void close() {
143         mClosed = true;
144         // Let the compiler do the work: close everything, throw if any of them fail, skipping null.
145         try (mConfigFd;
146                 mInstanceIdFd;
147                 mInstanceImgFd;
148                 mEncryptedStoreFd) {
149         } catch (IOException ignored) {
150             // PFD already swallows exceptions from closing the fd. There's no reason to propagate
151             // this to the caller.
152         }
153     }
154 
checkNotClosed()155     private void checkNotClosed() {
156         if (mClosed) {
157             throw new IllegalStateException("Descriptor has been closed");
158         }
159     }
160 }
161