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