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