1 /*
2  * Copyright (C) 2019 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.os.image;
18 
19 import android.annotation.RequiresPermission;
20 import android.annotation.SystemService;
21 import android.content.Context;
22 import android.gsi.AvbPublicKey;
23 import android.gsi.GsiProgress;
24 import android.os.ParcelFileDescriptor;
25 import android.os.RemoteException;
26 
27 /**
28  * The DynamicSystemManager offers a mechanism to use a new system image temporarily. After the
29  * installation, the device can reboot into this image with a new created /data. This image will
30  * last until the next reboot and then the device will go back to the original image. However the
31  * installed image and the new created /data are not deleted but disabled. Thus the application can
32  * either re-enable the installed image by calling {@link #toggle} or use the {@link #remove} to
33  * delete it completely. In other words, there are three device states: no installation, installed
34  * and running. The procedure to install a DynamicSystem starts with a {@link #startInstallation},
35  * followed by a series of {@link #write} and ends with a {@link commit}. Once the installation is
36  * complete, the device state changes from no installation to the installed state and a followed
37  * reboot will change its state to running. Note one instance of DynamicSystem can exist on a given
38  * device thus the {@link #startInstallation} will fail if the device is currently running a
39  * DynamicSystem.
40  *
41  * @hide
42  */
43 @SystemService(Context.DYNAMIC_SYSTEM_SERVICE)
44 public class DynamicSystemManager {
45     private static final String TAG = "DynamicSystemManager";
46 
47     private final IDynamicSystemService mService;
48 
49     /** {@hide} */
DynamicSystemManager(IDynamicSystemService service)50     public DynamicSystemManager(IDynamicSystemService service) {
51         mService = service;
52     }
53 
54     /** The DynamicSystemManager.Session represents a started session for the installation. */
55     public class Session {
Session()56         private Session() {}
57 
58         /**
59          * Set the file descriptor that points to a ashmem which will be used
60          * to fetch data during the submitFromAshmem.
61          *
62          * @param ashmem fd that points to a ashmem
63          * @param size size of the ashmem file
64          */
65         @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
setAshmem(ParcelFileDescriptor ashmem, long size)66         public boolean setAshmem(ParcelFileDescriptor ashmem, long size) {
67             try {
68                 return mService.setAshmem(ashmem, size);
69             } catch (RemoteException e) {
70                 throw new RuntimeException(e.toString());
71             }
72         }
73 
74         /**
75          * Submit bytes to the DSU partition from the ashmem previously set with
76          * setAshmem.
77          *
78          * @param size Number of bytes
79          * @return true on success, false otherwise.
80          */
81         @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
submitFromAshmem(int size)82         public boolean submitFromAshmem(int size) {
83             try {
84                 return mService.submitFromAshmem(size);
85             } catch (RemoteException e) {
86                 throw new RuntimeException(e.toString());
87             }
88         }
89 
90         /**
91          * Retrieve AVB public key from installing partition.
92          *
93          * @param dst           Output the AVB public key.
94          * @return              true on success, false if partition doesn't have a
95          *                      valid VBMeta block to retrieve the AVB key from.
96          */
97         @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
getAvbPublicKey(AvbPublicKey dst)98         public boolean getAvbPublicKey(AvbPublicKey dst) {
99             try {
100                 return mService.getAvbPublicKey(dst);
101             } catch (RemoteException e) {
102                 throw new RuntimeException(e.toString());
103             }
104         }
105 
106         /**
107          * Finish write and make device to boot into the it after reboot.
108          *
109          * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
110          *     error.
111          */
112         @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
commit()113         public boolean commit() {
114             try {
115                 return mService.setEnable(true, true);
116             } catch (RemoteException e) {
117                 throw new RuntimeException(e.toString());
118             }
119         }
120     }
121     /**
122      * Start DynamicSystem installation.
123      *
124      * @return true if the call succeeds
125      */
126     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
startInstallation(String dsuSlot)127     public boolean startInstallation(String dsuSlot) {
128         try {
129             return mService.startInstallation(dsuSlot);
130         } catch (RemoteException e) {
131             throw new RuntimeException(e.toString());
132         }
133     }
134     /**
135      * Start DynamicSystem installation. This call may take an unbounded amount of time. The caller
136      * may use another thread to call the getStartProgress() to get the progress.
137      *
138      * @param name The DSU partition name
139      * @param size Size of the DSU image in bytes
140      * @param readOnly True if the partition is read only, e.g. system.
141      * @return {@code true} if the call succeeds. {@code false} either the device does not contain
142      *     enough space or a DynamicSystem is currently in use where the {@link #isInUse} would be
143      *     true.
144      */
145     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
createPartition(String name, long size, boolean readOnly)146     public Session createPartition(String name, long size, boolean readOnly) {
147         try {
148             if (mService.createPartition(name, size, readOnly)) {
149                 return new Session();
150             } else {
151                 return null;
152             }
153         } catch (RemoteException e) {
154             throw new RuntimeException(e.toString());
155         }
156     }
157     /**
158      * Complete the current partition installation.
159      *
160      * @return true if the partition installation completes without error.
161      */
162     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
closePartition()163     public boolean closePartition() {
164         try {
165             return mService.closePartition();
166         } catch (RemoteException e) {
167             throw new RuntimeException(e.toString());
168         }
169     }
170     /**
171      * Finish a previously started installation. Installations without a cooresponding
172      * finishInstallation() will be cleaned up during device boot.
173      */
174     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
finishInstallation()175     public boolean finishInstallation() {
176         try {
177             return mService.finishInstallation();
178         } catch (RemoteException e) {
179             throw new RuntimeException(e.toString());
180         }
181     }
182     /**
183      * Query the progress of the current installation operation. This can be called while the
184      * installation is in progress.
185      *
186      * @return GsiProgress GsiProgress { int status; long bytes_processed; long total_bytes; } The
187      *     status field can be IGsiService.STATUS_NO_OPERATION, IGsiService.STATUS_WORKING or
188      *     IGsiService.STATUS_COMPLETE.
189      */
190     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
getInstallationProgress()191     public GsiProgress getInstallationProgress() {
192         try {
193             return mService.getInstallationProgress();
194         } catch (RemoteException e) {
195             throw new RuntimeException(e.toString());
196         }
197     }
198 
199     /**
200      * Abort the installation process. Note this method must be called in a thread other than the
201      * one calling the startInstallation method as the startInstallation method will not return
202      * until it is finished.
203      *
204      * @return {@code true} if the call succeeds. {@code false} if there is no installation
205      *     currently.
206      */
207     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
abort()208     public boolean abort() {
209         try {
210             return mService.abort();
211         } catch (RemoteException e) {
212             throw new RuntimeException(e.toString());
213         }
214     }
215 
216     /** @return {@code true} if the device is running a dynamic system */
217     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
isInUse()218     public boolean isInUse() {
219         try {
220             return mService.isInUse();
221         } catch (RemoteException e) {
222             throw new RuntimeException(e.toString());
223         }
224     }
225 
226     /** @return {@code true} if the device has a dynamic system installed */
227     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
isInstalled()228     public boolean isInstalled() {
229         try {
230             return mService.isInstalled();
231         } catch (RemoteException e) {
232             throw new RuntimeException(e.toString());
233         }
234     }
235 
236     /** @return {@code true} if the device has a dynamic system enabled */
237     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
isEnabled()238     public boolean isEnabled() {
239         try {
240             return mService.isEnabled();
241         } catch (RemoteException e) {
242             throw new RuntimeException(e.toString());
243         }
244     }
245 
246     /**
247      * Remove DynamicSystem installation if present
248      *
249      * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
250      */
251     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
remove()252     public boolean remove() {
253         try {
254             return mService.remove();
255         } catch (RemoteException e) {
256             throw new RuntimeException(e.toString());
257         }
258     }
259 
260     /**
261      * Enable or disable DynamicSystem.
262      * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
263      */
264     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
setEnable(boolean enable, boolean oneShot)265     public boolean setEnable(boolean enable, boolean oneShot) {
266         try {
267             return mService.setEnable(enable, oneShot);
268         } catch (RemoteException e) {
269             throw new RuntimeException(e.toString());
270         }
271     }
272 
273     /**
274      * Returns the suggested scratch partition size for overlayFS.
275      */
276     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
suggestScratchSize()277     public long suggestScratchSize() {
278         try {
279             return mService.suggestScratchSize();
280         } catch (RemoteException e) {
281             throw new RuntimeException(e.toString());
282         }
283     }
284 }
285