blob: cf286bdbde968d7a552e65907899fb44b6ffd788 [file] [log] [blame]
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +08001/*
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
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080017package com.android.dynsystem;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080018
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +080019import android.content.Context;
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +080020import android.gsi.GsiProgress;
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +080021import android.net.Uri;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080022import android.os.AsyncTask;
Howard Chenae615b32019-08-05 16:56:12 +080023import android.os.MemoryFile;
24import android.os.ParcelFileDescriptor;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080025import android.os.image.DynamicSystemManager;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080026import android.util.Log;
27import android.webkit.URLUtil;
28
29import java.io.BufferedInputStream;
30import java.io.IOException;
31import java.io.InputStream;
32import java.net.URL;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080033import java.util.Locale;
34import java.util.zip.GZIPInputStream;
35
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +080036class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080037
38 private static final String TAG = "InstallationAsyncTask";
39
Po-Chien Hsueh22c1f9a2019-05-17 15:08:20 +080040 private static final int READ_BUFFER_SIZE = 1 << 13;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080041
42 private class InvalidImageUrlException extends RuntimeException {
43 private InvalidImageUrlException(String message) {
44 super(message);
45 }
46 }
47
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080048 /** Not completed, including being cancelled */
49 static final int NO_RESULT = 0;
50 static final int RESULT_OK = 1;
51 static final int RESULT_ERROR_IO = 2;
52 static final int RESULT_ERROR_INVALID_URL = 3;
53 static final int RESULT_ERROR_EXCEPTION = 6;
54
55 interface InstallStatusListener {
56 void onProgressUpdate(long installedSize);
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +080057 void onResult(int resultCode, Throwable detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080058 void onCancelled();
59 }
60
61 private final String mUrl;
62 private final long mSystemSize;
63 private final long mUserdataSize;
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +080064 private final Context mContext;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080065 private final DynamicSystemManager mDynSystem;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080066 private final InstallStatusListener mListener;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080067 private DynamicSystemManager.Session mInstallationSession;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080068
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080069 private int mResult = NO_RESULT;
70
71 private InputStream mStream;
72
73
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +080074 InstallationAsyncTask(String url, long systemSize, long userdataSize, Context context,
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080075 DynamicSystemManager dynSystem, InstallStatusListener listener) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080076 mUrl = url;
77 mSystemSize = systemSize;
78 mUserdataSize = userdataSize;
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +080079 mContext = context;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080080 mDynSystem = dynSystem;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080081 mListener = listener;
82 }
83
84 @Override
85 protected void onPreExecute() {
86 mListener.onProgressUpdate(0);
87 }
88
89 @Override
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +080090 protected Throwable doInBackground(String... voids) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080091 Log.d(TAG, "Start doInBackground(), URL: " + mUrl);
92
93 try {
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +080094 long installedSize = 0;
95 long reportedInstalledSize = 0;
96
97 long minStepToReport = (mSystemSize + mUserdataSize) / 100;
98
99 // init input stream before calling startInstallation(), which takes 90 seconds.
100 initInputStream();
101
102 Thread thread = new Thread(() -> {
103 mInstallationSession =
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800104 mDynSystem.startInstallation(mSystemSize, mUserdataSize);
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800105 });
106
107
108 thread.start();
109
110 while (thread.isAlive()) {
111 if (isCancelled()) {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800112 boolean aborted = mDynSystem.abort();
113 Log.d(TAG, "Called DynamicSystemManager.abort(), result = " + aborted);
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800114 return null;
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800115 }
116
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800117 GsiProgress progress = mDynSystem.getInstallationProgress();
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800118 installedSize = progress.bytes_processed;
119
120 if (installedSize > reportedInstalledSize + minStepToReport) {
121 publishProgress(installedSize);
122 reportedInstalledSize = installedSize;
123 }
124
125 Thread.sleep(10);
126 }
127
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800128 if (mInstallationSession == null) {
Howard Chenae615b32019-08-05 16:56:12 +0800129 throw new IOException(
130 "Failed to start installation with requested size: "
131 + (mSystemSize + mUserdataSize));
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800132 }
133
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800134 installedSize = mUserdataSize;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800135
Howard Chenae615b32019-08-05 16:56:12 +0800136 MemoryFile memoryFile = new MemoryFile("dsu", READ_BUFFER_SIZE);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800137 byte[] bytes = new byte[READ_BUFFER_SIZE];
Howard Chenae615b32019-08-05 16:56:12 +0800138 mInstallationSession.setAshmem(
139 new ParcelFileDescriptor(memoryFile.getFileDescriptor()), READ_BUFFER_SIZE);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800140 int numBytesRead;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800141 Log.d(TAG, "Start installation loop");
142 while ((numBytesRead = mStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
Howard Chenae615b32019-08-05 16:56:12 +0800143 memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800144 if (isCancelled()) {
145 break;
146 }
Howard Chenae615b32019-08-05 16:56:12 +0800147 if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800148 throw new IOException("Failed write() to DynamicSystem");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800149 }
150
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800151 installedSize += numBytesRead;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800152
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800153 if (installedSize > reportedInstalledSize + minStepToReport) {
154 publishProgress(installedSize);
155 reportedInstalledSize = installedSize;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800156 }
157 }
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800158 return null;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800159
160 } catch (Exception e) {
161 e.printStackTrace();
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800162 return e;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800163 } finally {
164 close();
165 }
166 }
167
168 @Override
169 protected void onCancelled() {
170 Log.d(TAG, "onCancelled(), URL: " + mUrl);
171
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800172 mListener.onCancelled();
173 }
174
175 @Override
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800176 protected void onPostExecute(Throwable detail) {
177 if (detail == null) {
178 mResult = RESULT_OK;
179 } else if (detail instanceof IOException) {
180 mResult = RESULT_ERROR_IO;
181 } else if (detail instanceof InvalidImageUrlException) {
182 mResult = RESULT_ERROR_INVALID_URL;
183 } else {
184 mResult = RESULT_ERROR_EXCEPTION;
185 }
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800186
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800187 Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + mResult);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800188
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800189 mListener.onResult(mResult, detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800190 }
191
192 @Override
193 protected void onProgressUpdate(Long... values) {
194 long progress = values[0];
195 mListener.onProgressUpdate(progress);
196 }
197
198 private void initInputStream() throws IOException, InvalidImageUrlException {
199 if (URLUtil.isNetworkUrl(mUrl) || URLUtil.isFileUrl(mUrl)) {
200 mStream = new BufferedInputStream(new GZIPInputStream(new URL(mUrl).openStream()));
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +0800201 } else if (URLUtil.isContentUrl(mUrl)) {
202 Uri uri = Uri.parse(mUrl);
203 mStream = new BufferedInputStream(new GZIPInputStream(
204 mContext.getContentResolver().openInputStream(uri)));
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800205 } else {
206 throw new InvalidImageUrlException(
207 String.format(Locale.US, "Unsupported file source: %s", mUrl));
208 }
209 }
210
211 private void close() {
212 try {
213 if (mStream != null) {
214 mStream.close();
215 mStream = null;
216 }
217 } catch (IOException e) {
218 // ignore
219 }
220 }
221
222 int getResult() {
223 return mResult;
224 }
225
226 boolean commit() {
227 if (mInstallationSession == null) {
228 return false;
229 }
230
231 return mInstallationSession.commit();
232 }
233}