blob: 9e49826f70c3d4c148b14e1191c9d89a066f0993 [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 Hsueh64aa7822019-01-12 00:40:02 +080019import static android.os.AsyncTask.Status.FINISHED;
20import static android.os.AsyncTask.Status.PENDING;
21import static android.os.AsyncTask.Status.RUNNING;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080022import static android.os.image.DynamicSystemClient.ACTION_NOTIFY_IF_IN_USE;
23import static android.os.image.DynamicSystemClient.ACTION_START_INSTALL;
24import static android.os.image.DynamicSystemClient.CAUSE_ERROR_EXCEPTION;
25import static android.os.image.DynamicSystemClient.CAUSE_ERROR_INVALID_URL;
26import static android.os.image.DynamicSystemClient.CAUSE_ERROR_IO;
27import static android.os.image.DynamicSystemClient.CAUSE_INSTALL_CANCELLED;
28import static android.os.image.DynamicSystemClient.CAUSE_INSTALL_COMPLETED;
29import static android.os.image.DynamicSystemClient.CAUSE_NOT_SPECIFIED;
30import static android.os.image.DynamicSystemClient.STATUS_IN_PROGRESS;
31import static android.os.image.DynamicSystemClient.STATUS_IN_USE;
32import static android.os.image.DynamicSystemClient.STATUS_NOT_STARTED;
33import static android.os.image.DynamicSystemClient.STATUS_READY;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080034
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +080035import static com.android.dynsystem.InstallationAsyncTask.RESULT_CANCELLED;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080036import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_EXCEPTION;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080037import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_IO;
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +080038import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_UNSUPPORTED_FORMAT;
39import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_UNSUPPORTED_URL;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080040import static com.android.dynsystem.InstallationAsyncTask.RESULT_OK;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080041
42import android.app.Notification;
43import android.app.NotificationChannel;
44import android.app.NotificationManager;
45import android.app.PendingIntent;
46import android.app.Service;
47import android.content.Context;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080048import android.content.Intent;
49import android.os.Bundle;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080050import android.os.Handler;
51import android.os.IBinder;
52import android.os.Message;
53import android.os.Messenger;
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +080054import android.os.ParcelableException;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080055import android.os.PowerManager;
56import android.os.RemoteException;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080057import android.os.image.DynamicSystemClient;
58import android.os.image.DynamicSystemManager;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080059import android.util.Log;
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +080060import android.widget.Toast;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080061
62import java.lang.ref.WeakReference;
63import java.util.ArrayList;
64
65/**
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080066 * This class is the service in charge of DynamicSystem installation.
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080067 * It also posts status to notification bar and wait for user's
68 * cancel and confirm commnands.
69 */
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080070public class DynamicSystemInstallationService extends Service
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +080071 implements InstallationAsyncTask.ProgressListener {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080072
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080073 private static final String TAG = "DynSystemInstallationService";
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080074
Po-Chien Hsuehdfd69d72019-05-07 17:23:44 +080075 // TODO (b/131866826): This is currently for test only. Will move this to System API.
76 static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
77
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080078 /*
79 * Intent actions
80 */
81 private static final String ACTION_CANCEL_INSTALL =
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080082 "com.android.dynsystem.ACTION_CANCEL_INSTALL";
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +080083 private static final String ACTION_DISCARD_INSTALL =
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080084 "com.android.dynsystem.ACTION_DISCARD_INSTALL";
85 private static final String ACTION_REBOOT_TO_DYN_SYSTEM =
86 "com.android.dynsystem.ACTION_REBOOT_TO_DYN_SYSTEM";
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080087 private static final String ACTION_REBOOT_TO_NORMAL =
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080088 "com.android.dynsystem.ACTION_REBOOT_TO_NORMAL";
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080089
90 /*
91 * For notification
92 */
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080093 private static final String NOTIFICATION_CHANNEL_ID = "com.android.dynsystem";
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080094 private static final int NOTIFICATION_ID = 1;
95
96 /*
97 * IPC
98 */
99 /** Keeps track of all current registered clients. */
100 ArrayList<Messenger> mClients = new ArrayList<>();
101
102 /** Handler of incoming messages from clients. */
103 final Messenger mMessenger = new Messenger(new IncomingHandler(this));
104
105 static class IncomingHandler extends Handler {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800106 private final WeakReference<DynamicSystemInstallationService> mWeakService;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800107
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800108 IncomingHandler(DynamicSystemInstallationService service) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800109 mWeakService = new WeakReference<>(service);
110 }
111
112 @Override
113 public void handleMessage(Message msg) {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800114 DynamicSystemInstallationService service = mWeakService.get();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800115
116 if (service != null) {
117 service.handleMessage(msg);
118 }
119 }
120 }
121
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800122 private DynamicSystemManager mDynSystem;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800123 private NotificationManager mNM;
124
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800125 private int mNumInstalledPartitions;
126
127 private String mCurrentPartitionName;
128 private long mCurrentPartitionSize;
129 private long mCurrentPartitionInstalledSize;
130
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800131 private boolean mJustCancelledByUser;
132
Po-Chien Hsuehdfd69d72019-05-07 17:23:44 +0800133 // This is for testing only now
134 private boolean mEnableWhenCompleted;
135
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800136 private InstallationAsyncTask mInstallTask;
137
138
139 @Override
140 public void onCreate() {
141 super.onCreate();
142
143 prepareNotification();
144
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800145 mDynSystem = (DynamicSystemManager) getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800146 }
147
148 @Override
149 public void onDestroy() {
150 // Cancel the persistent notification.
151 mNM.cancel(NOTIFICATION_ID);
152 }
153
154 @Override
155 public IBinder onBind(Intent intent) {
156 return mMessenger.getBinder();
157 }
158
159 @Override
160 public int onStartCommand(Intent intent, int flags, int startId) {
161 String action = intent.getAction();
162
163 Log.d(TAG, "onStartCommand(): action=" + action);
164
165 if (ACTION_START_INSTALL.equals(action)) {
166 executeInstallCommand(intent);
167 } else if (ACTION_CANCEL_INSTALL.equals(action)) {
168 executeCancelCommand();
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800169 } else if (ACTION_DISCARD_INSTALL.equals(action)) {
170 executeDiscardCommand();
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800171 } else if (ACTION_REBOOT_TO_DYN_SYSTEM.equals(action)) {
172 executeRebootToDynSystemCommand();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800173 } else if (ACTION_REBOOT_TO_NORMAL.equals(action)) {
174 executeRebootToNormalCommand();
175 } else if (ACTION_NOTIFY_IF_IN_USE.equals(action)) {
176 executeNotifyIfInUseCommand();
177 }
178
179 return Service.START_NOT_STICKY;
180 }
181
182 @Override
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800183 public void onProgressUpdate(InstallationAsyncTask.Progress progress) {
184 mCurrentPartitionName = progress.mPartitionName;
185 mCurrentPartitionSize = progress.mPartitionSize;
186 mCurrentPartitionInstalledSize = progress.mInstalledSize;
187 mNumInstalledPartitions = progress.mNumInstalledPartitions;
188
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800189 postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED, null);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800190 }
191
192 @Override
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800193 public void onResult(int result, Throwable detail) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800194 if (result == RESULT_OK) {
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800195 postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED, null);
Po-Chien Hsuehdfd69d72019-05-07 17:23:44 +0800196
197 // For testing: enable DSU and restart the device when install completed
198 if (mEnableWhenCompleted) {
199 executeRebootToDynSystemCommand();
200 }
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800201 return;
202 }
203
204 // if it's not successful, reset the task and stop self.
205 resetTaskAndStop();
206
207 switch (result) {
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800208 case RESULT_CANCELLED:
209 postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
210 break;
211
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800212 case RESULT_ERROR_IO:
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800213 postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO, detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800214 break;
215
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800216 case RESULT_ERROR_UNSUPPORTED_URL:
217 case RESULT_ERROR_UNSUPPORTED_FORMAT:
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800218 postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL, detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800219 break;
220
221 case RESULT_ERROR_EXCEPTION:
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800222 postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION, detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800223 break;
224 }
225 }
226
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800227 private void executeInstallCommand(Intent intent) {
228 if (!verifyRequest(intent)) {
229 Log.e(TAG, "Verification failed. Did you use VerificationActivity?");
230 return;
231 }
232
233 if (mInstallTask != null) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800234 Log.e(TAG, "There is already an installation task running");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800235 return;
236 }
237
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800238 if (isInDynamicSystem()) {
239 Log.e(TAG, "We are already running in DynamicSystem");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800240 return;
241 }
242
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +0800243 String url = intent.getDataString();
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800244 long systemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0);
245 long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
Po-Chien Hsuehdfd69d72019-05-07 17:23:44 +0800246 mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800247
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800248 // TODO: better constructor or builder
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800249 mInstallTask = new InstallationAsyncTask(
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800250 url, systemSize, userdataSize, this, mDynSystem, this);
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800251
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800252 mInstallTask.execute();
253
254 // start fore ground
255 startForeground(NOTIFICATION_ID,
256 buildNotification(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED));
257 }
258
259 private void executeCancelCommand() {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800260 if (mInstallTask == null || mInstallTask.getStatus() != RUNNING) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800261 Log.e(TAG, "Cancel command triggered, but there is no task running");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800262 return;
263 }
264
265 mJustCancelledByUser = true;
266
267 if (mInstallTask.cancel(false)) {
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800268 // Will cleanup and post status in onResult()
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800269 Log.d(TAG, "Cancel request filed successfully");
270 } else {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800271 Log.e(TAG, "Trying to cancel installation while it's already completed.");
272 }
273 }
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800274
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800275 private void executeDiscardCommand() {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800276 if (isInDynamicSystem()) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800277 Log.e(TAG, "We are now running in AOT, please reboot to normal system first");
278 return;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800279 }
280
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800281 if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800282 Log.e(TAG, "Trying to discard AOT while there is no complete installation");
283 return;
284 }
285
286 Toast.makeText(this,
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800287 getString(R.string.toast_dynsystem_discarded),
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800288 Toast.LENGTH_LONG).show();
289
290 resetTaskAndStop();
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800291 postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800292
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800293 mDynSystem.remove();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800294 }
295
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800296 private void executeRebootToDynSystemCommand() {
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800297 boolean enabled = false;
298
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800299 if (mInstallTask != null && mInstallTask.isCompleted()) {
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800300 enabled = mInstallTask.commit();
301 } else if (isDynamicSystemInstalled()) {
Howard Chenc44ee152019-08-02 17:54:49 +0800302 enabled = mDynSystem.setEnable(true, true);
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800303 } else {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800304 Log.e(TAG, "Trying to reboot to AOT while there is no complete installation");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800305 return;
306 }
307
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800308 if (enabled) {
309 PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
310
311 if (powerManager != null) {
312 powerManager.reboot("dynsystem");
313 }
314 } else {
315 Log.e(TAG, "Failed to enable DynamicSystem because of native runtime error.");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800316 mNM.cancel(NOTIFICATION_ID);
317
Po-Chien Hsueh646d9f32019-03-06 14:20:22 +0800318 Toast.makeText(this,
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800319 getString(R.string.toast_failed_to_reboot_to_dynsystem),
Po-Chien Hsueh646d9f32019-03-06 14:20:22 +0800320 Toast.LENGTH_LONG).show();
321
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800322 mDynSystem.remove();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800323 }
324 }
325
326 private void executeRebootToNormalCommand() {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800327 if (!isInDynamicSystem()) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800328 Log.e(TAG, "It's already running in normal system.");
329 return;
330 }
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800331
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800332 // Per current design, we don't have disable() API. AOT is disabled on next reboot.
333 // TODO: Use better status query when b/125079548 is done.
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800334 PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
335
336 if (powerManager != null) {
337 powerManager.reboot(null);
338 }
339 }
340
341 private void executeNotifyIfInUseCommand() {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800342 int status = getStatus();
343
344 if (status == STATUS_IN_USE) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800345 startForeground(NOTIFICATION_ID,
346 buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800347 } else if (status == STATUS_READY) {
348 startForeground(NOTIFICATION_ID,
349 buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED));
Po-Chien Hsueh67697392019-04-10 15:30:16 +0800350 } else {
351 stopSelf();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800352 }
353 }
354
355 private void resetTaskAndStop() {
356 mInstallTask = null;
357
358 stopForeground(true);
359
360 // stop self, but this service is not destroyed yet if it's still bound
361 stopSelf();
362 }
363
364 private void prepareNotification() {
365 NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
366 getString(R.string.notification_channel_name),
367 NotificationManager.IMPORTANCE_LOW);
368
369 mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
370
371 if (mNM != null) {
372 mNM.createNotificationChannel(chan);
373 }
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800374 }
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800375
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800376 private PendingIntent createPendingIntent(String action) {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800377 Intent intent = new Intent(this, DynamicSystemInstallationService.class);
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800378 intent.setAction(action);
379 return PendingIntent.getService(this, 0, intent, 0);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800380 }
381
382 private Notification buildNotification(int status, int cause) {
383 Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
384 .setSmallIcon(R.drawable.ic_system_update_googblue_24dp)
385 .setProgress(0, 0, false);
386
387 switch (status) {
388 case STATUS_IN_PROGRESS:
389 builder.setContentText(getString(R.string.notification_install_inprogress));
390
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800391 int max = 1024;
392 int progress = 0;
393
394 int currentMax = max >> (mNumInstalledPartitions + 1);
395 progress = max - currentMax * 2;
396
397 long currentProgress = (mCurrentPartitionInstalledSize >> 20) * currentMax
398 / Math.max(mCurrentPartitionSize >> 20, 1);
399
400 progress += (int) currentProgress;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800401
402 builder.setProgress(max, progress, false);
403
404 builder.addAction(new Notification.Action.Builder(
405 null, getString(R.string.notification_action_cancel),
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800406 createPendingIntent(ACTION_CANCEL_INSTALL)).build());
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800407
408 break;
409
410 case STATUS_READY:
411 builder.setContentText(getString(R.string.notification_install_completed));
412
413 builder.addAction(new Notification.Action.Builder(
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800414 null, getString(R.string.notification_action_discard),
415 createPendingIntent(ACTION_DISCARD_INSTALL)).build());
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800416
Po-Chien Hsueh9a1627f2019-04-01 10:40:51 +0800417 builder.addAction(new Notification.Action.Builder(
418 null, getString(R.string.notification_action_reboot_to_dynsystem),
419 createPendingIntent(ACTION_REBOOT_TO_DYN_SYSTEM)).build());
420
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800421 break;
422
423 case STATUS_IN_USE:
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800424 builder.setContentText(getString(R.string.notification_dynsystem_in_use));
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800425
426 builder.addAction(new Notification.Action.Builder(
427 null, getString(R.string.notification_action_uninstall),
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800428 createPendingIntent(ACTION_REBOOT_TO_NORMAL)).build());
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800429
430 break;
431
432 case STATUS_NOT_STARTED:
433 if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) {
434 builder.setContentText(getString(R.string.notification_install_failed));
435 } else {
436 // no need to notify the user if the task is not started, or cancelled.
437 }
438 break;
439
440 default:
441 throw new IllegalStateException("status is invalid");
442 }
443
444 return builder.build();
445 }
446
447 private boolean verifyRequest(Intent intent) {
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +0800448 String url = intent.getDataString();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800449
450 return VerificationActivity.isVerified(url);
451 }
452
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800453 private void postStatus(int status, int cause, Throwable detail) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800454 Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause);
455
456 boolean notifyOnNotificationBar = true;
457
458 if (status == STATUS_NOT_STARTED
459 && cause == CAUSE_INSTALL_CANCELLED
460 && mJustCancelledByUser) {
461 // if task is cancelled by user, do not notify them
462 notifyOnNotificationBar = false;
463 mJustCancelledByUser = false;
464 }
465
466 if (notifyOnNotificationBar) {
467 mNM.notify(NOTIFICATION_ID, buildNotification(status, cause));
468 }
469
470 for (int i = mClients.size() - 1; i >= 0; i--) {
471 try {
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800472 notifyOneClient(mClients.get(i), status, cause, detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800473 } catch (RemoteException e) {
474 mClients.remove(i);
475 }
476 }
477 }
478
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800479 private void notifyOneClient(Messenger client, int status, int cause, Throwable detail)
480 throws RemoteException {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800481 Bundle bundle = new Bundle();
482
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800483 // TODO: send more info to the clients
484 bundle.putLong(DynamicSystemClient.KEY_INSTALLED_SIZE, mCurrentPartitionInstalledSize);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800485
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800486 if (detail != null) {
487 bundle.putSerializable(DynamicSystemClient.KEY_EXCEPTION_DETAIL,
488 new ParcelableException(detail));
489 }
490
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800491 client.send(Message.obtain(null,
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800492 DynamicSystemClient.MSG_POST_STATUS, status, cause, bundle));
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800493 }
494
495 private int getStatus() {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800496 if (isInDynamicSystem()) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800497 return STATUS_IN_USE;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800498 } else if (isDynamicSystemInstalled()) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800499 return STATUS_READY;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800500 } else if (mInstallTask == null) {
501 return STATUS_NOT_STARTED;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800502 }
503
504 switch (mInstallTask.getStatus()) {
505 case PENDING:
506 return STATUS_NOT_STARTED;
507
508 case RUNNING:
509 return STATUS_IN_PROGRESS;
510
511 case FINISHED:
Po-Chien Hsueh99d18dd2019-10-30 16:48:15 +0800512 if (mInstallTask.isCompleted()) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800513 return STATUS_READY;
514 } else {
515 throw new IllegalStateException("A failed InstallationTask is not reset");
516 }
517
518 default:
519 return STATUS_NOT_STARTED;
520 }
521 }
522
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800523 private boolean isInDynamicSystem() {
524 return mDynSystem.isInUse();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800525 }
526
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800527 private boolean isDynamicSystemInstalled() {
528 return mDynSystem.isInstalled();
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800529 }
530
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800531 void handleMessage(Message msg) {
532 switch (msg.what) {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800533 case DynamicSystemClient.MSG_REGISTER_LISTENER:
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800534 try {
535 Messenger client = msg.replyTo;
536
537 int status = getStatus();
538
539 // tell just registered client my status, but do not specify cause
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800540 notifyOneClient(client, status, CAUSE_NOT_SPECIFIED, null);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800541
542 mClients.add(client);
543 } catch (RemoteException e) {
544 // do nothing if we cannot send update to the client
545 e.printStackTrace();
546 }
547
548 break;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800549 case DynamicSystemClient.MSG_UNREGISTER_LISTENER:
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800550 mClients.remove(msg.replyTo);
551 break;
552 default:
553 // do nothing
554 }
555 }
556}