1 /* 2 * Copyright (C) 2016 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; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.SystemApi; 23 import android.annotation.WorkerThread; 24 import android.content.res.AssetFileDescriptor; 25 import android.os.IUpdateEngine; 26 import android.os.IUpdateEngineCallback; 27 import android.os.RemoteException; 28 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 32 /** 33 * UpdateEngine handles calls to the update engine which takes care of A/B OTA 34 * updates. It wraps up the update engine Binder APIs and exposes them as 35 * SystemApis, which will be called by the system app responsible for OTAs. 36 * On a Google device, this will be GmsCore. 37 * 38 * The minimal flow is: 39 * <ol> 40 * <li>Create a new UpdateEngine instance. 41 * <li>Call {@link #bind}, optionally providing callbacks. 42 * <li>Call {@link #applyPayload}. 43 * </ol> 44 * 45 * In addition, methods are provided to {@link #cancel} or 46 * {@link #suspend}/{@link #resume} application of an update. 47 * 48 * The APIs defined in this class and UpdateEngineCallback class must be in 49 * sync with the ones in 50 * {@code system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl} 51 * and 52 * {@code system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl}. 53 * 54 * {@hide} 55 */ 56 @SystemApi 57 public class UpdateEngine { 58 private static final String TAG = "UpdateEngine"; 59 60 private static final String UPDATE_ENGINE_SERVICE = "android.os.UpdateEngineService"; 61 62 /** 63 * Error codes from update engine upon finishing a call to 64 * {@link applyPayload}. Values will be passed via the callback function 65 * {@link UpdateEngineCallback#onPayloadApplicationComplete}. Values must 66 * agree with the ones in {@code system/update_engine/common/error_code.h}. 67 */ 68 public static final class ErrorCodeConstants { 69 /** 70 * Error code: a request finished successfully. 71 */ 72 public static final int SUCCESS = 0; 73 /** 74 * Error code: a request failed due to a generic error. 75 */ 76 public static final int ERROR = 1; 77 /** 78 * Error code: an update failed to apply due to filesystem copier 79 * error. 80 */ 81 public static final int FILESYSTEM_COPIER_ERROR = 4; 82 /** 83 * Error code: an update failed to apply due to an error in running 84 * post-install hooks. 85 */ 86 public static final int POST_INSTALL_RUNNER_ERROR = 5; 87 /** 88 * Error code: an update failed to apply due to a mismatching payload. 89 * 90 * <p>For example, the given payload uses a feature that's not 91 * supported by the current update engine. 92 */ 93 public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; 94 /** 95 * Error code: an update failed to apply due to an error in opening 96 * devices. 97 */ 98 public static final int INSTALL_DEVICE_OPEN_ERROR = 7; 99 /** 100 * Error code: an update failed to apply due to an error in opening 101 * kernel device. 102 */ 103 public static final int KERNEL_DEVICE_OPEN_ERROR = 8; 104 /** 105 * Error code: an update failed to apply due to an error in fetching 106 * the payload. 107 * 108 * <p>For example, this could be a result of bad network connection 109 * when streaming an update. 110 */ 111 public static final int DOWNLOAD_TRANSFER_ERROR = 9; 112 /** 113 * Error code: an update failed to apply due to a mismatch in payload 114 * hash. 115 * 116 * <p>Update engine does validity checks for the given payload and its 117 * metadata. 118 */ 119 public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; 120 121 /** 122 * Error code: an update failed to apply due to a mismatch in payload 123 * size. 124 */ 125 public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; 126 127 /** 128 * Error code: an update failed to apply due to failing to verify 129 * payload signatures. 130 */ 131 public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; 132 133 /** 134 * Error code: an update failed to apply due to a downgrade in payload 135 * timestamp. 136 * 137 * <p>The timestamp of a build is encoded into the payload, which will 138 * be enforced during install to prevent downgrading a device. 139 */ 140 public static final int PAYLOAD_TIMESTAMP_ERROR = 51; 141 142 /** 143 * Error code: an update has been applied successfully but the new slot 144 * hasn't been set to active. 145 * 146 * <p>It indicates a successful finish of calling {@link #applyPayload} with 147 * {@code SWITCH_SLOT_ON_REBOOT=0}. See {@link #applyPayload}. 148 */ 149 public static final int UPDATED_BUT_NOT_ACTIVE = 52; 150 151 /** 152 * Error code: there is not enough space on the device to apply the update. User should 153 * be prompted to free up space and re-try the update. 154 * 155 * <p>See {@link UpdateEngine#allocateSpace}. 156 */ 157 public static final int NOT_ENOUGH_SPACE = 60; 158 159 /** 160 * Error code: the device is corrupted and no further updates may be applied. 161 * 162 * <p>See {@link UpdateEngine#cleanupAppliedPayload}. 163 */ 164 public static final int DEVICE_CORRUPTED = 61; 165 } 166 167 /** @hide */ 168 @IntDef(value = { 169 ErrorCodeConstants.SUCCESS, 170 ErrorCodeConstants.ERROR, 171 ErrorCodeConstants.FILESYSTEM_COPIER_ERROR, 172 ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR, 173 ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR, 174 ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR, 175 ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR, 176 ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR, 177 ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, 178 ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR, 179 ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR, 180 ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR, 181 ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE, 182 ErrorCodeConstants.NOT_ENOUGH_SPACE, 183 ErrorCodeConstants.DEVICE_CORRUPTED, 184 }) 185 @Retention(RetentionPolicy.SOURCE) 186 public @interface ErrorCode {} 187 188 /** 189 * Status codes for update engine. Values must agree with the ones in 190 * {@code system/update_engine/client_library/include/update_engine/update_status.h}. 191 */ 192 public static final class UpdateStatusConstants { 193 /** 194 * Update status code: update engine is in idle state. 195 */ 196 public static final int IDLE = 0; 197 198 /** 199 * Update status code: update engine is checking for update. 200 */ 201 public static final int CHECKING_FOR_UPDATE = 1; 202 203 /** 204 * Update status code: an update is available. 205 */ 206 public static final int UPDATE_AVAILABLE = 2; 207 208 /** 209 * Update status code: update engine is downloading an update. 210 */ 211 public static final int DOWNLOADING = 3; 212 213 /** 214 * Update status code: update engine is verifying an update. 215 */ 216 public static final int VERIFYING = 4; 217 218 /** 219 * Update status code: update engine is finalizing an update. 220 */ 221 public static final int FINALIZING = 5; 222 223 /** 224 * Update status code: an update has been applied and is pending for 225 * reboot. 226 */ 227 public static final int UPDATED_NEED_REBOOT = 6; 228 229 /** 230 * Update status code: update engine is reporting an error event. 231 */ 232 public static final int REPORTING_ERROR_EVENT = 7; 233 234 /** 235 * Update status code: update engine is attempting to rollback an 236 * update. 237 */ 238 public static final int ATTEMPTING_ROLLBACK = 8; 239 240 /** 241 * Update status code: update engine is in disabled state. 242 */ 243 public static final int DISABLED = 9; 244 } 245 246 private final IUpdateEngine mUpdateEngine; 247 private IUpdateEngineCallback mUpdateEngineCallback = null; 248 private final Object mUpdateEngineCallbackLock = new Object(); 249 250 /** 251 * Creates a new instance. 252 */ UpdateEngine()253 public UpdateEngine() { 254 mUpdateEngine = IUpdateEngine.Stub.asInterface( 255 ServiceManager.getService(UPDATE_ENGINE_SERVICE)); 256 if (mUpdateEngine == null) { 257 throw new IllegalStateException("Failed to find update_engine"); 258 } 259 } 260 261 /** 262 * Prepares this instance for use. The callback will be notified on any 263 * status change, and when the update completes. A handler can be supplied 264 * to control which thread runs the callback, or null. 265 */ bind(final UpdateEngineCallback callback, final Handler handler)266 public boolean bind(final UpdateEngineCallback callback, final Handler handler) { 267 synchronized (mUpdateEngineCallbackLock) { 268 mUpdateEngineCallback = new IUpdateEngineCallback.Stub() { 269 @Override 270 public void onStatusUpdate(final int status, final float percent) { 271 if (handler != null) { 272 handler.post(new Runnable() { 273 @Override 274 public void run() { 275 callback.onStatusUpdate(status, percent); 276 } 277 }); 278 } else { 279 callback.onStatusUpdate(status, percent); 280 } 281 } 282 283 @Override 284 public void onPayloadApplicationComplete(final int errorCode) { 285 if (handler != null) { 286 handler.post(new Runnable() { 287 @Override 288 public void run() { 289 callback.onPayloadApplicationComplete(errorCode); 290 } 291 }); 292 } else { 293 callback.onPayloadApplicationComplete(errorCode); 294 } 295 } 296 }; 297 298 try { 299 return mUpdateEngine.bind(mUpdateEngineCallback); 300 } catch (RemoteException e) { 301 throw e.rethrowFromSystemServer(); 302 } 303 } 304 } 305 306 /** 307 * Equivalent to {@code bind(callback, null)}. 308 */ bind(final UpdateEngineCallback callback)309 public boolean bind(final UpdateEngineCallback callback) { 310 return bind(callback, null); 311 } 312 313 /** 314 * Applies the payload found at the given {@code url}. For non-streaming 315 * updates, the URL can be a local file using the {@code file://} scheme. 316 * 317 * <p>The {@code offset} and {@code size} parameters specify the location 318 * of the payload within the file represented by the URL. This is useful 319 * if the downloadable package at the URL contains more than just the 320 * update_engine payload (such as extra metadata). This is true for 321 * Google's OTA system, where the URL points to a zip file in which the 322 * payload is stored uncompressed within the zip file alongside other 323 * data. 324 * 325 * <p>The {@code headerKeyValuePairs} parameter is used to pass metadata 326 * to update_engine. In Google's implementation, this is stored as 327 * {@code payload_properties.txt} in the zip file. It's generated by the 328 * script {@code system/update_engine/scripts/brillo_update_payload}. 329 * The complete list of keys and their documentation is in 330 * {@code system/update_engine/common/constants.cc}, but an example 331 * might be: 332 * <pre> 333 * String[] pairs = { 334 * "FILE_HASH=lURPCIkIAjtMOyB/EjQcl8zDzqtD6Ta3tJef6G/+z2k=", 335 * "FILE_SIZE=871903868", 336 * "METADATA_HASH=tBvj43QOB0Jn++JojcpVdbRLz0qdAuL+uTkSy7hokaw=", 337 * "METADATA_SIZE=70604" 338 * }; 339 * </pre> 340 * 341 * <p>The callback functions registered via {@code #bind} will be called 342 * during and at the end of the payload application. 343 * 344 * <p>By default the newly updated slot will be set active upon 345 * successfully finishing an update. Device will attempt to boot into the 346 * new slot on next reboot. This behavior can be customized by specifying 347 * {@code SWITCH_SLOT_ON_REBOOT=0} in {@code headerKeyValuePairs}, which 348 * allows the caller to later determine a good time to boot into the new 349 * slot. Calling {@code applyPayload} again with the same payload but with 350 * {@code SWITCH_SLOT_ON_REBOOT=1} will do the minimal work to set the new 351 * slot active, after verifying its integrity. 352 */ applyPayload(String url, long offset, long size, String[] headerKeyValuePairs)353 public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) { 354 try { 355 mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs); 356 } catch (RemoteException e) { 357 throw e.rethrowFromSystemServer(); 358 } 359 } 360 361 /** 362 * Applies the payload passed as AssetFileDescriptor {@code assetFd} 363 * instead of using the {@code file://} scheme. 364 * 365 * <p>See {@link #applyPayload(String)} for {@code offset}, {@code size} and 366 * {@code headerKeyValuePairs} parameters. 367 */ applyPayload(@onNull AssetFileDescriptor assetFd, @NonNull String[] headerKeyValuePairs)368 public void applyPayload(@NonNull AssetFileDescriptor assetFd, 369 @NonNull String[] headerKeyValuePairs) { 370 try { 371 mUpdateEngine.applyPayloadFd(assetFd.getParcelFileDescriptor(), 372 assetFd.getStartOffset(), assetFd.getLength(), headerKeyValuePairs); 373 } catch (RemoteException e) { 374 throw e.rethrowFromSystemServer(); 375 } 376 } 377 378 /** 379 * Permanently cancels an in-progress update. 380 * 381 * <p>See {@link #resetStatus} to undo a finshed update (only available 382 * before the updated system has been rebooted). 383 * 384 * <p>See {@link #suspend} for a way to temporarily stop an in-progress 385 * update with the ability to resume it later. 386 */ cancel()387 public void cancel() { 388 try { 389 mUpdateEngine.cancel(); 390 } catch (RemoteException e) { 391 throw e.rethrowFromSystemServer(); 392 } 393 } 394 395 /** 396 * Suspends an in-progress update. This can be undone by calling 397 * {@link #resume}. 398 */ suspend()399 public void suspend() { 400 try { 401 mUpdateEngine.suspend(); 402 } catch (RemoteException e) { 403 throw e.rethrowFromSystemServer(); 404 } 405 } 406 407 /** 408 * Resumes a suspended update. 409 */ resume()410 public void resume() { 411 try { 412 mUpdateEngine.resume(); 413 } catch (RemoteException e) { 414 throw e.rethrowFromSystemServer(); 415 } 416 } 417 418 /** 419 * Resets the bootable flag on the non-current partition and all internal 420 * update_engine state. Note this call will clear the entire update 421 * progress. So a subsequent {@link #applyPayload} will apply the update 422 * from scratch. 423 * 424 * <p>After this call completes, update_engine will no longer report 425 * {@code UPDATED_NEED_REBOOT}, so your callback can remove any outstanding 426 * notification that rebooting into the new system is possible. 427 */ resetStatus()428 public void resetStatus() { 429 try { 430 mUpdateEngine.resetStatus(); 431 } catch (RemoteException e) { 432 throw e.rethrowFromSystemServer(); 433 } 434 } 435 436 /** 437 * Sets the A/B slot switch for the next boot after applying an ota update. If 438 * {@link #applyPayload} hasn't switched the slot, the updater APP can call 439 * this API to switch the slot and apply the update on next boot. 440 * 441 * @param payloadMetadataFilename the location of the metadata without the 442 * {@code file://} prefix. 443 */ setShouldSwitchSlotOnReboot(@onNull String payloadMetadataFilename)444 public void setShouldSwitchSlotOnReboot(@NonNull String payloadMetadataFilename) { 445 try { 446 mUpdateEngine.setShouldSwitchSlotOnReboot(payloadMetadataFilename); 447 } catch (RemoteException e) { 448 throw e.rethrowFromSystemServer(); 449 } 450 } 451 452 /** 453 * Resets the boot slot to the source/current slot, without cancelling the 454 * update progress. This can be called after the update is installed, and to 455 * prevent the device from accidentally taking the update when it reboots. 456 * 457 * This is useful when users don't want to take the update immediately; or 458 * the updater determines some condition hasn't met, e.g. insufficient space 459 * for boot. 460 */ resetShouldSwitchSlotOnReboot()461 public void resetShouldSwitchSlotOnReboot() { 462 try { 463 mUpdateEngine.resetShouldSwitchSlotOnReboot(); 464 } catch (RemoteException e) { 465 throw e.rethrowFromSystemServer(); 466 } 467 } 468 469 /** 470 * Unbinds the last bound callback function. 471 */ unbind()472 public boolean unbind() { 473 synchronized (mUpdateEngineCallbackLock) { 474 if (mUpdateEngineCallback == null) { 475 return true; 476 } 477 try { 478 boolean result = mUpdateEngine.unbind(mUpdateEngineCallback); 479 mUpdateEngineCallback = null; 480 return result; 481 } catch (RemoteException e) { 482 throw e.rethrowFromSystemServer(); 483 } 484 } 485 } 486 487 /** 488 * Verifies that a payload associated with the given payload metadata 489 * {@code payloadMetadataFilename} can be safely applied to ths device. 490 * Returns {@code true} if the update can successfully be applied and 491 * returns {@code false} otherwise. 492 * 493 * @param payloadMetadataFilename the location of the metadata without the 494 * {@code file://} prefix. 495 */ verifyPayloadMetadata(String payloadMetadataFilename)496 public boolean verifyPayloadMetadata(String payloadMetadataFilename) { 497 try { 498 return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename); 499 } catch (RemoteException e) { 500 throw e.rethrowFromSystemServer(); 501 } 502 } 503 504 /** 505 * Return value of {@link #allocateSpace.} 506 */ 507 public static final class AllocateSpaceResult { 508 private @ErrorCode int mErrorCode = ErrorCodeConstants.SUCCESS; 509 private long mFreeSpaceRequired = 0; AllocateSpaceResult()510 private AllocateSpaceResult() {} 511 /** 512 * Error code. 513 * 514 * @return The following error codes: 515 * <ul> 516 * <li>{@link ErrorCodeConstants#SUCCESS} if space has been allocated 517 * successfully.</li> 518 * <li>{@link ErrorCodeConstants#NOT_ENOUGH_SPACE} if insufficient 519 * space.</li> 520 * <li>Other {@link ErrorCodeConstants} for other errors.</li> 521 * </ul> 522 */ 523 @ErrorCode getErrorCode()524 public int getErrorCode() { 525 return mErrorCode; 526 } 527 528 /** 529 * Estimated total space that needs to be available on the userdata partition to apply the 530 * payload (in bytes). 531 * 532 * <p> 533 * Note that in practice, more space needs to be made available before applying the payload 534 * to keep the device working. 535 * 536 * @return The following values: 537 * <ul> 538 * <li>zero if {@link #getErrorCode} returns {@link ErrorCodeConstants#SUCCESS}</li> 539 * <li>non-zero if {@link #getErrorCode} returns 540 * {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}. 541 * Value is the estimated total space required on userdata partition.</li> 542 * </ul> 543 * @throws IllegalStateException if {@link #getErrorCode} is not one of the above. 544 * 545 */ getFreeSpaceRequired()546 public long getFreeSpaceRequired() { 547 if (mErrorCode == ErrorCodeConstants.SUCCESS) { 548 return 0; 549 } 550 if (mErrorCode == ErrorCodeConstants.NOT_ENOUGH_SPACE) { 551 return mFreeSpaceRequired; 552 } 553 throw new IllegalStateException(String.format( 554 "getFreeSpaceRequired() is not available when error code is %d", mErrorCode)); 555 } 556 } 557 558 /** 559 * Initialize partitions for a payload associated with the given payload 560 * metadata {@code payloadMetadataFilename} by preallocating required space. 561 * 562 * <p>This function should be called after payload has been verified after 563 * {@link #verifyPayloadMetadata}. This function does not verify whether 564 * the given payload is applicable or not. 565 * 566 * <p>Implementation of {@code allocateSpace} uses 567 * {@code headerKeyValuePairs} to determine whether space has been allocated 568 * for a different or same payload previously. If space has been allocated 569 * for a different payload before, space will be reallocated for the given 570 * payload. If space has been allocated for the same payload, no actions to 571 * storage devices are taken. 572 * 573 * <p>This function is synchronous and may take a non-trivial amount of 574 * time. Callers should call this function in a background thread. 575 * 576 * @param payloadMetadataFilename See {@link #verifyPayloadMetadata}. 577 * @param headerKeyValuePairs See {@link #applyPayload}. 578 * @return See {@link AllocateSpaceResult#getErrorCode} and 579 * {@link AllocateSpaceResult#getFreeSpaceRequired}. 580 */ 581 @WorkerThread 582 @NonNull allocateSpace( @onNull String payloadMetadataFilename, @NonNull String[] headerKeyValuePairs)583 public AllocateSpaceResult allocateSpace( 584 @NonNull String payloadMetadataFilename, 585 @NonNull String[] headerKeyValuePairs) { 586 AllocateSpaceResult result = new AllocateSpaceResult(); 587 try { 588 result.mFreeSpaceRequired = mUpdateEngine.allocateSpaceForPayload( 589 payloadMetadataFilename, 590 headerKeyValuePairs); 591 result.mErrorCode = result.mFreeSpaceRequired == 0 592 ? ErrorCodeConstants.SUCCESS 593 : ErrorCodeConstants.NOT_ENOUGH_SPACE; 594 return result; 595 } catch (ServiceSpecificException e) { 596 result.mErrorCode = e.errorCode; 597 result.mFreeSpaceRequired = 0; 598 return result; 599 } catch (RemoteException e) { 600 throw e.rethrowFromSystemServer(); 601 } 602 } 603 604 private static class CleanupAppliedPayloadCallback extends IUpdateEngineCallback.Stub { 605 private int mErrorCode = ErrorCodeConstants.ERROR; 606 private boolean mCompleted = false; 607 private Object mLock = new Object(); getResult()608 private int getResult() { 609 synchronized (mLock) { 610 while (!mCompleted) { 611 try { 612 mLock.wait(); 613 } catch (InterruptedException ex) { 614 // do nothing, just wait again. 615 } 616 } 617 return mErrorCode; 618 } 619 } 620 621 @Override onStatusUpdate(int status, float percent)622 public void onStatusUpdate(int status, float percent) { 623 } 624 625 @Override onPayloadApplicationComplete(int errorCode)626 public void onPayloadApplicationComplete(int errorCode) { 627 synchronized (mLock) { 628 mErrorCode = errorCode; 629 mCompleted = true; 630 mLock.notifyAll(); 631 } 632 } 633 } 634 635 /** 636 * Cleanup files used by the previous update and free up space after the 637 * device has been booted successfully into the new build. 638 * 639 * <p>In particular, this function waits until delta files for snapshots for 640 * Virtual A/B update are merged to OS partitions, then delete these delta 641 * files. 642 * 643 * <p>This function is synchronous and may take a non-trivial amount of 644 * time. Callers should call this function in a background thread. 645 * 646 * <p>This function does not delete payload binaries downloaded for a 647 * non-streaming OTA update. 648 * 649 * @return One of the following: 650 * <ul> 651 * <li>{@link ErrorCodeConstants#SUCCESS} if execution is successful.</li> 652 * <li>{@link ErrorCodeConstants#ERROR} if a transient error has occurred. 653 * The device should be able to recover after a reboot. The function should 654 * be retried after the reboot.</li> 655 * <li>{@link ErrorCodeConstants#DEVICE_CORRUPTED} if a permanent error is 656 * encountered. Device is corrupted, and future updates must not be applied. 657 * The device cannot recover without flashing and factory resets. 658 * </ul> 659 */ 660 @WorkerThread 661 @ErrorCode cleanupAppliedPayload()662 public int cleanupAppliedPayload() { 663 CleanupAppliedPayloadCallback callback = new CleanupAppliedPayloadCallback(); 664 try { 665 mUpdateEngine.cleanupSuccessfulUpdate(callback); 666 return callback.getResult(); 667 } catch (RemoteException e) { 668 throw e.rethrowFromSystemServer(); 669 } 670 } 671 672 /** 673 * Run postinstall script for specified partition |partition| 674 * 675 * @param partition The partition to trigger postinstall runs 676 * 677 * @throws ServiceSpecificException error code of this exception would be one of 678 * https://cs.android.com/android/platform/superproject/main/+/main:system/update_engine/common/error_code.h 679 * @hide 680 */ 681 @FlaggedApi(Flags.FLAG_UPDATE_ENGINE_API) 682 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) triggerPostinstall(@onNull String partition)683 public void triggerPostinstall(@NonNull String partition) { 684 try { 685 mUpdateEngine.triggerPostinstall(partition); 686 } catch (RemoteException e) { 687 throw e.rethrowFromSystemServer(); 688 } 689 } 690 } 691