blob: 142078e1b77cce337a0ea14e51e88e8ecbf5a2b6 [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 Hsueh4e908c22019-03-07 11:57:17 +080035import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_EXCEPTION;
36import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_INVALID_URL;
37import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_IO;
38import static com.android.dynsystem.InstallationAsyncTask.RESULT_OK;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080039
40import android.app.Notification;
41import android.app.NotificationChannel;
42import android.app.NotificationManager;
43import android.app.PendingIntent;
44import android.app.Service;
45import android.content.Context;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080046import android.content.Intent;
47import android.os.Bundle;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080048import android.os.Handler;
49import android.os.IBinder;
50import android.os.Message;
51import android.os.Messenger;
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +080052import android.os.ParcelableException;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080053import android.os.PowerManager;
54import android.os.RemoteException;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080055import android.os.image.DynamicSystemClient;
56import android.os.image.DynamicSystemManager;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080057import android.util.Log;
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +080058import android.widget.Toast;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080059
60import java.lang.ref.WeakReference;
61import java.util.ArrayList;
62
63/**
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080064 * This class is the service in charge of DynamicSystem installation.
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080065 * It also posts status to notification bar and wait for user's
66 * cancel and confirm commnands.
67 */
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080068public class DynamicSystemInstallationService extends Service
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080069 implements InstallationAsyncTask.InstallStatusListener {
70
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080071 private static final String TAG = "DynSystemInstallationService";
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080072
Po-Chien Hsuehdfd69d72019-05-07 17:23:44 +080073
74 // TODO (b/131866826): This is currently for test only. Will move this to System API.
75 static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
76
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080077 /*
78 * Intent actions
79 */
80 private static final String ACTION_CANCEL_INSTALL =
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080081 "com.android.dynsystem.ACTION_CANCEL_INSTALL";
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +080082 private static final String ACTION_DISCARD_INSTALL =
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080083 "com.android.dynsystem.ACTION_DISCARD_INSTALL";
84 private static final String ACTION_REBOOT_TO_DYN_SYSTEM =
85 "com.android.dynsystem.ACTION_REBOOT_TO_DYN_SYSTEM";
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080086 private static final String ACTION_REBOOT_TO_NORMAL =
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080087 "com.android.dynsystem.ACTION_REBOOT_TO_NORMAL";
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080088
89 /*
90 * For notification
91 */
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +080092 private static final String NOTIFICATION_CHANNEL_ID = "com.android.dynsystem";
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +080093 private static final int NOTIFICATION_ID = 1;
94
95 /*
96 * IPC
97 */
98 /** Keeps track of all current registered clients. */
99 ArrayList<Messenger> mClients = new ArrayList<>();
100
101 /** Handler of incoming messages from clients. */
102 final Messenger mMessenger = new Messenger(new IncomingHandler(this));
103
104 static class IncomingHandler extends Handler {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800105 private final WeakReference<DynamicSystemInstallationService> mWeakService;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800106
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800107 IncomingHandler(DynamicSystemInstallationService service) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800108 mWeakService = new WeakReference<>(service);
109 }
110
111 @Override
112 public void handleMessage(Message msg) {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800113 DynamicSystemInstallationService service = mWeakService.get();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800114
115 if (service != null) {
116 service.handleMessage(msg);
117 }
118 }
119 }
120
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800121 private DynamicSystemManager mDynSystem;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800122 private NotificationManager mNM;
123
124 private long mSystemSize;
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800125 private long mUserdataSize;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800126 private long mInstalledSize;
127 private boolean mJustCancelledByUser;
128
Po-Chien Hsuehdfd69d72019-05-07 17:23:44 +0800129 // This is for testing only now
130 private boolean mEnableWhenCompleted;
131
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800132 private InstallationAsyncTask mInstallTask;
133
134
135 @Override
136 public void onCreate() {
137 super.onCreate();
138
139 prepareNotification();
140
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800141 mDynSystem = (DynamicSystemManager) getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800142 }
143
144 @Override
145 public void onDestroy() {
146 // Cancel the persistent notification.
147 mNM.cancel(NOTIFICATION_ID);
148 }
149
150 @Override
151 public IBinder onBind(Intent intent) {
152 return mMessenger.getBinder();
153 }
154
155 @Override
156 public int onStartCommand(Intent intent, int flags, int startId) {
157 String action = intent.getAction();
158
159 Log.d(TAG, "onStartCommand(): action=" + action);
160
161 if (ACTION_START_INSTALL.equals(action)) {
162 executeInstallCommand(intent);
163 } else if (ACTION_CANCEL_INSTALL.equals(action)) {
164 executeCancelCommand();
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800165 } else if (ACTION_DISCARD_INSTALL.equals(action)) {
166 executeDiscardCommand();
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800167 } else if (ACTION_REBOOT_TO_DYN_SYSTEM.equals(action)) {
168 executeRebootToDynSystemCommand();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800169 } else if (ACTION_REBOOT_TO_NORMAL.equals(action)) {
170 executeRebootToNormalCommand();
171 } else if (ACTION_NOTIFY_IF_IN_USE.equals(action)) {
172 executeNotifyIfInUseCommand();
173 }
174
175 return Service.START_NOT_STICKY;
176 }
177
178 @Override
179 public void onProgressUpdate(long installedSize) {
180 mInstalledSize = installedSize;
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800181 postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED, null);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800182 }
183
184 @Override
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800185 public void onResult(int result, Throwable detail) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800186 if (result == RESULT_OK) {
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800187 postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED, null);
Po-Chien Hsuehdfd69d72019-05-07 17:23:44 +0800188
189 // For testing: enable DSU and restart the device when install completed
190 if (mEnableWhenCompleted) {
191 executeRebootToDynSystemCommand();
192 }
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800193 return;
194 }
195
196 // if it's not successful, reset the task and stop self.
197 resetTaskAndStop();
198
199 switch (result) {
200 case RESULT_ERROR_IO:
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800201 postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO, detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800202 break;
203
204 case RESULT_ERROR_INVALID_URL:
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800205 postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL, detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800206 break;
207
208 case RESULT_ERROR_EXCEPTION:
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800209 postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION, detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800210 break;
211 }
212 }
213
214 @Override
215 public void onCancelled() {
216 resetTaskAndStop();
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800217 postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800218 }
219
220 private void executeInstallCommand(Intent intent) {
221 if (!verifyRequest(intent)) {
222 Log.e(TAG, "Verification failed. Did you use VerificationActivity?");
223 return;
224 }
225
226 if (mInstallTask != null) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800227 Log.e(TAG, "There is already an installation task running");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800228 return;
229 }
230
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800231 if (isInDynamicSystem()) {
232 Log.e(TAG, "We are already running in DynamicSystem");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800233 return;
234 }
235
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +0800236 String url = intent.getDataString();
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800237 mSystemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0);
238 mUserdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
Po-Chien Hsuehdfd69d72019-05-07 17:23:44 +0800239 mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800240
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800241 mInstallTask = new InstallationAsyncTask(
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +0800242 url, mSystemSize, mUserdataSize, this, mDynSystem, this);
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800243
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800244 mInstallTask.execute();
245
246 // start fore ground
247 startForeground(NOTIFICATION_ID,
248 buildNotification(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED));
249 }
250
251 private void executeCancelCommand() {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800252 if (mInstallTask == null || mInstallTask.getStatus() != RUNNING) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800253 Log.e(TAG, "Cancel command triggered, but there is no task running");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800254 return;
255 }
256
257 mJustCancelledByUser = true;
258
259 if (mInstallTask.cancel(false)) {
260 // Will cleanup and post status in onCancelled()
261 Log.d(TAG, "Cancel request filed successfully");
262 } else {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800263 Log.e(TAG, "Trying to cancel installation while it's already completed.");
264 }
265 }
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800266
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800267 private void executeDiscardCommand() {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800268 if (isInDynamicSystem()) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800269 Log.e(TAG, "We are now running in AOT, please reboot to normal system first");
270 return;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800271 }
272
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800273 if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800274 Log.e(TAG, "Trying to discard AOT while there is no complete installation");
275 return;
276 }
277
278 Toast.makeText(this,
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800279 getString(R.string.toast_dynsystem_discarded),
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800280 Toast.LENGTH_LONG).show();
281
282 resetTaskAndStop();
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800283 postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800284
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800285 mDynSystem.remove();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800286 }
287
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800288 private void executeRebootToDynSystemCommand() {
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800289 boolean enabled = false;
290
Po-Chien Hsuehdfd69d72019-05-07 17:23:44 +0800291 if (mInstallTask != null && mInstallTask.getResult() == RESULT_OK) {
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800292 enabled = mInstallTask.commit();
293 } else if (isDynamicSystemInstalled()) {
Howard Chen295d7492019-08-02 17:54:49 +0800294 enabled = mDynSystem.setEnable(true, true);
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800295 } else {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800296 Log.e(TAG, "Trying to reboot to AOT while there is no complete installation");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800297 return;
298 }
299
Po-Chien Hsueh91508ea2019-04-01 16:51:47 +0800300 if (enabled) {
301 PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
302
303 if (powerManager != null) {
304 powerManager.reboot("dynsystem");
305 }
306 } else {
307 Log.e(TAG, "Failed to enable DynamicSystem because of native runtime error.");
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800308 mNM.cancel(NOTIFICATION_ID);
309
Po-Chien Hsueh646d9f32019-03-06 14:20:22 +0800310 Toast.makeText(this,
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800311 getString(R.string.toast_failed_to_reboot_to_dynsystem),
Po-Chien Hsueh646d9f32019-03-06 14:20:22 +0800312 Toast.LENGTH_LONG).show();
313
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800314 mDynSystem.remove();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800315 }
316 }
317
318 private void executeRebootToNormalCommand() {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800319 if (!isInDynamicSystem()) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800320 Log.e(TAG, "It's already running in normal system.");
321 return;
322 }
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800323
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800324 // Per current design, we don't have disable() API. AOT is disabled on next reboot.
325 // TODO: Use better status query when b/125079548 is done.
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800326 PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
327
328 if (powerManager != null) {
329 powerManager.reboot(null);
330 }
331 }
332
333 private void executeNotifyIfInUseCommand() {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800334 int status = getStatus();
335
336 if (status == STATUS_IN_USE) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800337 startForeground(NOTIFICATION_ID,
338 buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800339 } else if (status == STATUS_READY) {
340 startForeground(NOTIFICATION_ID,
341 buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED));
Po-Chien Hsueh67697392019-04-10 15:30:16 +0800342 } else {
343 stopSelf();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800344 }
345 }
346
347 private void resetTaskAndStop() {
348 mInstallTask = null;
349
350 stopForeground(true);
351
352 // stop self, but this service is not destroyed yet if it's still bound
353 stopSelf();
354 }
355
356 private void prepareNotification() {
357 NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
358 getString(R.string.notification_channel_name),
359 NotificationManager.IMPORTANCE_LOW);
360
361 mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
362
363 if (mNM != null) {
364 mNM.createNotificationChannel(chan);
365 }
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800366 }
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800367
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800368 private PendingIntent createPendingIntent(String action) {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800369 Intent intent = new Intent(this, DynamicSystemInstallationService.class);
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800370 intent.setAction(action);
371 return PendingIntent.getService(this, 0, intent, 0);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800372 }
373
374 private Notification buildNotification(int status, int cause) {
375 Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
376 .setSmallIcon(R.drawable.ic_system_update_googblue_24dp)
377 .setProgress(0, 0, false);
378
379 switch (status) {
380 case STATUS_IN_PROGRESS:
381 builder.setContentText(getString(R.string.notification_install_inprogress));
382
Po-Chien Hsueh9c1500f2019-03-04 14:47:46 +0800383 int max = (int) Math.max((mSystemSize + mUserdataSize) >> 20, 1);
384 int progress = (int) (mInstalledSize >> 20);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800385
386 builder.setProgress(max, progress, false);
387
388 builder.addAction(new Notification.Action.Builder(
389 null, getString(R.string.notification_action_cancel),
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800390 createPendingIntent(ACTION_CANCEL_INSTALL)).build());
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800391
392 break;
393
394 case STATUS_READY:
395 builder.setContentText(getString(R.string.notification_install_completed));
396
397 builder.addAction(new Notification.Action.Builder(
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800398 null, getString(R.string.notification_action_discard),
399 createPendingIntent(ACTION_DISCARD_INSTALL)).build());
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800400
Po-Chien Hsueh9a1627f2019-04-01 10:40:51 +0800401 builder.addAction(new Notification.Action.Builder(
402 null, getString(R.string.notification_action_reboot_to_dynsystem),
403 createPendingIntent(ACTION_REBOOT_TO_DYN_SYSTEM)).build());
404
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800405 break;
406
407 case STATUS_IN_USE:
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800408 builder.setContentText(getString(R.string.notification_dynsystem_in_use));
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800409
410 builder.addAction(new Notification.Action.Builder(
411 null, getString(R.string.notification_action_uninstall),
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800412 createPendingIntent(ACTION_REBOOT_TO_NORMAL)).build());
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800413
414 break;
415
416 case STATUS_NOT_STARTED:
417 if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) {
418 builder.setContentText(getString(R.string.notification_install_failed));
419 } else {
420 // no need to notify the user if the task is not started, or cancelled.
421 }
422 break;
423
424 default:
425 throw new IllegalStateException("status is invalid");
426 }
427
428 return builder.build();
429 }
430
431 private boolean verifyRequest(Intent intent) {
Po-Chien Hsuehc51cf0f2019-03-21 17:16:06 +0800432 String url = intent.getDataString();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800433
434 return VerificationActivity.isVerified(url);
435 }
436
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800437 private void postStatus(int status, int cause, Throwable detail) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800438 Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause);
439
440 boolean notifyOnNotificationBar = true;
441
442 if (status == STATUS_NOT_STARTED
443 && cause == CAUSE_INSTALL_CANCELLED
444 && mJustCancelledByUser) {
445 // if task is cancelled by user, do not notify them
446 notifyOnNotificationBar = false;
447 mJustCancelledByUser = false;
448 }
449
450 if (notifyOnNotificationBar) {
451 mNM.notify(NOTIFICATION_ID, buildNotification(status, cause));
452 }
453
454 for (int i = mClients.size() - 1; i >= 0; i--) {
455 try {
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800456 notifyOneClient(mClients.get(i), status, cause, detail);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800457 } catch (RemoteException e) {
458 mClients.remove(i);
459 }
460 }
461 }
462
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800463 private void notifyOneClient(Messenger client, int status, int cause, Throwable detail)
464 throws RemoteException {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800465 Bundle bundle = new Bundle();
466
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800467 bundle.putLong(DynamicSystemClient.KEY_INSTALLED_SIZE, mInstalledSize);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800468
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800469 if (detail != null) {
470 bundle.putSerializable(DynamicSystemClient.KEY_EXCEPTION_DETAIL,
471 new ParcelableException(detail));
472 }
473
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800474 client.send(Message.obtain(null,
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800475 DynamicSystemClient.MSG_POST_STATUS, status, cause, bundle));
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800476 }
477
478 private int getStatus() {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800479 if (isInDynamicSystem()) {
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800480 return STATUS_IN_USE;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800481 } else if (isDynamicSystemInstalled()) {
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800482 return STATUS_READY;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800483 } else if (mInstallTask == null) {
484 return STATUS_NOT_STARTED;
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800485 }
486
487 switch (mInstallTask.getStatus()) {
488 case PENDING:
489 return STATUS_NOT_STARTED;
490
491 case RUNNING:
492 return STATUS_IN_PROGRESS;
493
494 case FINISHED:
495 int result = mInstallTask.getResult();
496
497 if (result == RESULT_OK) {
498 return STATUS_READY;
499 } else {
500 throw new IllegalStateException("A failed InstallationTask is not reset");
501 }
502
503 default:
504 return STATUS_NOT_STARTED;
505 }
506 }
507
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800508 private boolean isInDynamicSystem() {
509 return mDynSystem.isInUse();
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800510 }
511
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800512 private boolean isDynamicSystemInstalled() {
513 return mDynSystem.isInstalled();
Po-Chien Hsueh34de5af2019-03-05 16:00:45 +0800514 }
515
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800516 void handleMessage(Message msg) {
517 switch (msg.what) {
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800518 case DynamicSystemClient.MSG_REGISTER_LISTENER:
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800519 try {
520 Messenger client = msg.replyTo;
521
522 int status = getStatus();
523
524 // tell just registered client my status, but do not specify cause
Po-Chien Hsueha5bd0842019-03-19 12:56:19 +0800525 notifyOneClient(client, status, CAUSE_NOT_SPECIFIED, null);
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800526
527 mClients.add(client);
528 } catch (RemoteException e) {
529 // do nothing if we cannot send update to the client
530 e.printStackTrace();
531 }
532
533 break;
Po-Chien Hsueh4e908c22019-03-07 11:57:17 +0800534 case DynamicSystemClient.MSG_UNREGISTER_LISTENER:
Po-Chien Hsueh64aa7822019-01-12 00:40:02 +0800535 mClients.remove(msg.replyTo);
536 break;
537 default:
538 // do nothing
539 }
540 }
541}