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      * Finish a previously started installation. Installations without a cooresponding
159      * finishInstallation() will be cleaned up during device boot.
160      */
161     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
finishInstallation()162     public boolean finishInstallation() {
163         try {
164             return mService.finishInstallation();
165         } catch (RemoteException e) {
166             throw new RuntimeException(e.toString());
167         }
168     }
169     /**
170      * Query the progress of the current installation operation. This can be called while the
171      * installation is in progress.
172      *
173      * @return GsiProgress GsiProgress { int status; long bytes_processed; long total_bytes; } The
174      *     status field can be IGsiService.STATUS_NO_OPERATION, IGsiService.STATUS_WORKING or
175      *     IGsiService.STATUS_COMPLETE.
176      */
177     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
getInstallationProgress()178     public GsiProgress getInstallationProgress() {
179         try {
180             return mService.getInstallationProgress();
181         } catch (RemoteException e) {
182             throw new RuntimeException(e.toString());
183         }
184     }
185 
186     /**
187      * Abort the installation process. Note this method must be called in a thread other than the
188      * one calling the startInstallation method as the startInstallation method will not return
189      * until it is finished.
190      *
191      * @return {@code true} if the call succeeds. {@code false} if there is no installation
192      *     currently.
193      */
194     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
abort()195     public boolean abort() {
196         try {
197             return mService.abort();
198         } catch (RemoteException e) {
199             throw new RuntimeException(e.toString());
200         }
201     }
202 
203     /** @return {@code true} if the device is running a dynamic system */
204     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
isInUse()205     public boolean isInUse() {
206         try {
207             return mService.isInUse();
208         } catch (RemoteException e) {
209             throw new RuntimeException(e.toString());
210         }
211     }
212 
213     /** @return {@code true} if the device has a dynamic system installed */
214     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
isInstalled()215     public boolean isInstalled() {
216         try {
217             return mService.isInstalled();
218         } catch (RemoteException e) {
219             throw new RuntimeException(e.toString());
220         }
221     }
222 
223     /** @return {@code true} if the device has a dynamic system enabled */
224     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
isEnabled()225     public boolean isEnabled() {
226         try {
227             return mService.isEnabled();
228         } catch (RemoteException e) {
229             throw new RuntimeException(e.toString());
230         }
231     }
232 
233     /**
234      * Remove DynamicSystem installation if present
235      *
236      * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
237      */
238     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
remove()239     public boolean remove() {
240         try {
241             return mService.remove();
242         } catch (RemoteException e) {
243             throw new RuntimeException(e.toString());
244         }
245     }
246 
247     /**
248      * Enable or disable DynamicSystem.
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)
setEnable(boolean enable, boolean oneShot)252     public boolean setEnable(boolean enable, boolean oneShot) {
253         try {
254             return mService.setEnable(enable, oneShot);
255         } catch (RemoteException e) {
256             throw new RuntimeException(e.toString());
257         }
258     }
259 }
260