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
17package android.os;
18
19import android.annotation.SystemApi;
20import android.os.IUpdateEngine;
21import android.os.IUpdateEngineCallback;
22import android.os.RemoteException;
23
24/**
25 * UpdateEngine handles calls to the update engine which takes care of A/B OTA
26 * updates. It wraps up the update engine Binder APIs and exposes them as
27 * SystemApis, which will be called by the system app responsible for OTAs.
28 * On a Google device, this will be GmsCore.
29 *
30 * The minimal flow is:
31 * <ol>
32 * <li>Create a new UpdateEngine instance.
33 * <li>Call {@link #bind}, optionally providing callbacks.
34 * <li>Call {@link #applyPayload}.
35 * </ol>
36 *
37 * In addition, methods are provided to {@link #cancel} or
38 * {@link #suspend}/{@link #resume} application of an update.
39 *
40 * The APIs defined in this class and UpdateEngineCallback class must be in
41 * sync with the ones in
42 * system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl and
43 * system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl.
44 *
45 * {@hide}
46 */
47@SystemApi
48public class UpdateEngine {
49    private static final String TAG = "UpdateEngine";
50
51    private static final String UPDATE_ENGINE_SERVICE = "android.os.UpdateEngineService";
52
53    /**
54     * Error code from the update engine. Values must agree with the ones in
55     * system/update_engine/common/error_code.h.
56     */
57    @SystemApi
58    public static final class ErrorCodeConstants {
59        public static final int SUCCESS = 0;
60        public static final int ERROR = 1;
61        public static final int FILESYSTEM_COPIER_ERROR = 4;
62        public static final int POST_INSTALL_RUNNER_ERROR = 5;
63        public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6;
64        public static final int INSTALL_DEVICE_OPEN_ERROR = 7;
65        public static final int KERNEL_DEVICE_OPEN_ERROR = 8;
66        public static final int DOWNLOAD_TRANSFER_ERROR = 9;
67        public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10;
68        public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11;
69        public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12;
70        public static final int UPDATED_BUT_NOT_ACTIVE = 52;
71    }
72
73    /**
74     * Update status code from the update engine. Values must agree with the
75     * ones in system/update_engine/client_library/include/update_engine/update_status.h.
76     */
77    @SystemApi
78    public static final class UpdateStatusConstants {
79        public static final int IDLE = 0;
80        public static final int CHECKING_FOR_UPDATE = 1;
81        public static final int UPDATE_AVAILABLE = 2;
82        public static final int DOWNLOADING = 3;
83        public static final int VERIFYING = 4;
84        public static final int FINALIZING = 5;
85        public static final int UPDATED_NEED_REBOOT = 6;
86        public static final int REPORTING_ERROR_EVENT = 7;
87        public static final int ATTEMPTING_ROLLBACK = 8;
88        public static final int DISABLED = 9;
89    }
90
91    private IUpdateEngine mUpdateEngine;
92    private IUpdateEngineCallback mUpdateEngineCallback = null;
93    private final Object mUpdateEngineCallbackLock = new Object();
94
95    /**
96     * Creates a new instance.
97     */
98    @SystemApi
99    public UpdateEngine() {
100        mUpdateEngine = IUpdateEngine.Stub.asInterface(
101                ServiceManager.getService(UPDATE_ENGINE_SERVICE));
102    }
103
104    /**
105     * Prepares this instance for use. The callback will be notified on any
106     * status change, and when the update completes. A handler can be supplied
107     * to control which thread runs the callback, or null.
108     */
109    @SystemApi
110    public boolean bind(final UpdateEngineCallback callback, final Handler handler) {
111        synchronized (mUpdateEngineCallbackLock) {
112            mUpdateEngineCallback = new IUpdateEngineCallback.Stub() {
113                @Override
114                public void onStatusUpdate(final int status, final float percent) {
115                    if (handler != null) {
116                        handler.post(new Runnable() {
117                            @Override
118                            public void run() {
119                                callback.onStatusUpdate(status, percent);
120                            }
121                        });
122                    } else {
123                        callback.onStatusUpdate(status, percent);
124                    }
125                }
126
127                @Override
128                public void onPayloadApplicationComplete(final int errorCode) {
129                    if (handler != null) {
130                        handler.post(new Runnable() {
131                            @Override
132                            public void run() {
133                                callback.onPayloadApplicationComplete(errorCode);
134                            }
135                        });
136                    } else {
137                        callback.onPayloadApplicationComplete(errorCode);
138                    }
139                }
140            };
141
142            try {
143                return mUpdateEngine.bind(mUpdateEngineCallback);
144            } catch (RemoteException e) {
145                throw e.rethrowFromSystemServer();
146            }
147        }
148    }
149
150    /**
151     * Equivalent to {@code bind(callback, null)}.
152     */
153    @SystemApi
154    public boolean bind(final UpdateEngineCallback callback) {
155        return bind(callback, null);
156    }
157
158    /**
159     * Applies the payload found at the given {@code url}. For non-streaming
160     * updates, the URL can be a local file using the {@code file://} scheme.
161     *
162     * <p>The {@code offset} and {@code size} parameters specify the location
163     * of the payload within the file represented by the URL. This is useful
164     * if the downloadable package at the URL contains more than just the
165     * update_engine payload (such as extra metadata). This is true for
166     * Google's OTA system, where the URL points to a zip file in which the
167     * payload is stored uncompressed within the zip file alongside other
168     * data.
169     *
170     * <p>The {@code headerKeyValuePairs} parameter is used to pass metadata
171     * to update_engine. In Google's implementation, this is stored as
172     * {@code payload_properties.txt} in the zip file. It's generated by the
173     * script {@code system/update_engine/scripts/brillo_update_payload}.
174     * The complete list of keys and their documentation is in
175     * {@code system/update_engine/common/constants.cc}, but an example
176     * might be:
177     * <pre>
178     * String[] pairs = {
179     *   "FILE_HASH=lURPCIkIAjtMOyB/EjQcl8zDzqtD6Ta3tJef6G/+z2k=",
180     *   "FILE_SIZE=871903868",
181     *   "METADATA_HASH=tBvj43QOB0Jn++JojcpVdbRLz0qdAuL+uTkSy7hokaw=",
182     *   "METADATA_SIZE=70604"
183     * };
184     * </pre>
185     */
186    @SystemApi
187    public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) {
188        try {
189            mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs);
190        } catch (RemoteException e) {
191            throw e.rethrowFromSystemServer();
192        }
193    }
194
195    /**
196     * Permanently cancels an in-progress update.
197     *
198     * <p>See {@link #resetStatus} to undo a finshed update (only available
199     * before the updated system has been rebooted).
200     *
201     * <p>See {@link #suspend} for a way to temporarily stop an in-progress
202     * update with the ability to resume it later.
203     */
204    @SystemApi
205    public void cancel() {
206        try {
207            mUpdateEngine.cancel();
208        } catch (RemoteException e) {
209            throw e.rethrowFromSystemServer();
210        }
211    }
212
213    /**
214     * Suspends an in-progress update. This can be undone by calling
215     * {@link #resume}.
216     */
217    @SystemApi
218    public void suspend() {
219        try {
220            mUpdateEngine.suspend();
221        } catch (RemoteException e) {
222            throw e.rethrowFromSystemServer();
223        }
224    }
225
226    /**
227     * Resumes a suspended update.
228     */
229    @SystemApi
230    public void resume() {
231        try {
232            mUpdateEngine.resume();
233        } catch (RemoteException e) {
234            throw e.rethrowFromSystemServer();
235        }
236    }
237
238    /**
239     * Resets the bootable flag on the non-current partition and all internal
240     * update_engine state. This can be used after an unwanted payload has been
241     * successfully applied and the device has not yet been rebooted to signal
242     * that we no longer want to boot into that updated system. After this call
243     * completes, update_engine will no longer report
244     * {@code UPDATED_NEED_REBOOT}, so your callback can remove any outstanding
245     * notification that rebooting into the new system is possible.
246     */
247    @SystemApi
248    public void resetStatus() {
249        try {
250            mUpdateEngine.resetStatus();
251        } catch (RemoteException e) {
252            throw e.rethrowFromSystemServer();
253        }
254    }
255
256    /**
257     * Unbinds the last bound callback function.
258     */
259    @SystemApi
260    public boolean unbind() {
261        synchronized (mUpdateEngineCallbackLock) {
262            if (mUpdateEngineCallback == null) {
263                return true;
264            }
265            try {
266                boolean result = mUpdateEngine.unbind(mUpdateEngineCallback);
267                mUpdateEngineCallback = null;
268                return result;
269            } catch (RemoteException e) {
270                throw e.rethrowFromSystemServer();
271            }
272        }
273    }
274
275    /**
276     * Verifies that a payload associated with the given payload metadata
277     * {@code payloadMetadataFilename} can be safely applied to ths device.
278     * Returns {@code true} if the update can successfully be applied and
279     * returns {@code false} otherwise.
280     *
281     * @param payloadMetadataFilename the location of the metadata without the
282     * {@code file://} prefix.
283     */
284    @SystemApi
285    public boolean verifyPayloadMetadata(String payloadMetadataFilename) {
286        try {
287            return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename);
288        } catch (RemoteException e) {
289            throw e.rethrowFromSystemServer();
290        }
291    }
292}
293