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