1 /* 2 * Copyright (C) 2017 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 com.android.server.backup.internal; 18 19 import static com.android.server.backup.BackupManagerService.DEBUG; 20 import static com.android.server.backup.BackupManagerService.MORE_DEBUG; 21 import static com.android.server.backup.BackupManagerService.TAG; 22 23 import android.app.backup.BackupManager; 24 import android.app.backup.BackupManager.OperationType; 25 import android.app.backup.RestoreSet; 26 import android.os.Handler; 27 import android.os.HandlerThread; 28 import android.os.Message; 29 import android.os.RemoteException; 30 import android.util.EventLog; 31 import android.util.Pair; 32 import android.util.Slog; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.backup.IBackupTransport; 36 import com.android.server.EventLogTags; 37 import com.android.server.backup.BackupAgentTimeoutParameters; 38 import com.android.server.backup.BackupRestoreTask; 39 import com.android.server.backup.DataChangedJournal; 40 import com.android.server.backup.TransportManager; 41 import com.android.server.backup.UserBackupManagerService; 42 import com.android.server.backup.fullbackup.PerformAdbBackupTask; 43 import com.android.server.backup.keyvalue.BackupRequest; 44 import com.android.server.backup.keyvalue.KeyValueBackupTask; 45 import com.android.server.backup.params.AdbBackupParams; 46 import com.android.server.backup.params.AdbParams; 47 import com.android.server.backup.params.AdbRestoreParams; 48 import com.android.server.backup.params.BackupParams; 49 import com.android.server.backup.params.ClearParams; 50 import com.android.server.backup.params.ClearRetryParams; 51 import com.android.server.backup.params.RestoreGetSetsParams; 52 import com.android.server.backup.params.RestoreParams; 53 import com.android.server.backup.restore.PerformAdbRestoreTask; 54 import com.android.server.backup.restore.PerformUnifiedRestoreTask; 55 import com.android.server.backup.transport.TransportClient; 56 57 import java.util.ArrayList; 58 import java.util.Collections; 59 import java.util.List; 60 import java.util.Objects; 61 62 /** 63 * Asynchronous backup/restore handler thread. 64 */ 65 public class BackupHandler extends Handler { 66 67 public static final int MSG_RUN_BACKUP = 1; 68 public static final int MSG_RUN_ADB_BACKUP = 2; 69 public static final int MSG_RUN_RESTORE = 3; 70 public static final int MSG_RUN_CLEAR = 4; 71 public static final int MSG_RUN_GET_RESTORE_SETS = 6; 72 public static final int MSG_RESTORE_SESSION_TIMEOUT = 8; 73 public static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9; 74 public static final int MSG_RUN_ADB_RESTORE = 10; 75 public static final int MSG_RETRY_CLEAR = 12; 76 public static final int MSG_REQUEST_BACKUP = 15; 77 public static final int MSG_SCHEDULE_BACKUP_PACKAGE = 16; 78 public static final int MSG_BACKUP_OPERATION_TIMEOUT = 17; 79 public static final int MSG_RESTORE_OPERATION_TIMEOUT = 18; 80 // backup task state machine tick 81 public static final int MSG_BACKUP_RESTORE_STEP = 20; 82 public static final int MSG_OP_COMPLETE = 21; 83 // Release the wakelock. This is used to ensure we don't hold it after 84 // a user is removed. This will also terminate the looper thread. 85 public static final int MSG_STOP = 22; 86 87 private final UserBackupManagerService backupManagerService; 88 private final BackupAgentTimeoutParameters mAgentTimeoutParameters; 89 90 private final HandlerThread mBackupThread; 91 92 @VisibleForTesting 93 volatile boolean mIsStopping = false; 94 BackupHandler( UserBackupManagerService backupManagerService, HandlerThread backupThread)95 public BackupHandler( 96 UserBackupManagerService backupManagerService, HandlerThread backupThread) { 97 super(backupThread.getLooper()); 98 mBackupThread = backupThread; 99 this.backupManagerService = backupManagerService; 100 mAgentTimeoutParameters = Objects.requireNonNull( 101 backupManagerService.getAgentTimeoutParameters(), 102 "Timeout parameters cannot be null"); 103 } 104 105 /** 106 * Put the BackupHandler into a stopping state where the remaining messages on the queue will be 107 * silently dropped and the {@link WakeLock} held by the {@link UserBackupManagerService} will 108 * then be released. 109 */ stop()110 public void stop() { 111 mIsStopping = true; 112 sendMessage(obtainMessage(BackupHandler.MSG_STOP)); 113 } 114 115 @Override dispatchMessage(Message message)116 public void dispatchMessage(Message message) { 117 try { 118 dispatchMessageInternal(message); 119 } catch (Exception e) { 120 // If the backup service is stopping, we'll suppress all exceptions to avoid crashes 121 // caused by code still running after the current user has become unavailable. 122 if (!mIsStopping) { 123 throw e; 124 } 125 } 126 } 127 128 @VisibleForTesting dispatchMessageInternal(Message message)129 void dispatchMessageInternal(Message message) { 130 super.dispatchMessage(message); 131 } 132 handleMessage(Message msg)133 public void handleMessage(Message msg) { 134 if (msg.what == MSG_STOP) { 135 Slog.v(TAG, "Stopping backup handler"); 136 backupManagerService.getWakelock().quit(); 137 mBackupThread.quitSafely(); 138 } 139 140 if (mIsStopping) { 141 // If we're finishing all other types of messages should be ignored 142 return; 143 } 144 145 TransportManager transportManager = backupManagerService.getTransportManager(); 146 switch (msg.what) { 147 case MSG_RUN_BACKUP: { 148 backupManagerService.setLastBackupPass(System.currentTimeMillis()); 149 150 String callerLogString = "BH/MSG_RUN_BACKUP"; 151 TransportClient transportClient = 152 transportManager.getCurrentTransportClient(callerLogString); 153 IBackupTransport transport = 154 transportClient != null 155 ? transportClient.connect(callerLogString) 156 : null; 157 if (transport == null) { 158 if (transportClient != null) { 159 transportManager 160 .disposeOfTransportClient(transportClient, callerLogString); 161 } 162 Slog.v(TAG, "Backup requested but no transport available"); 163 break; 164 } 165 166 // Snapshot the pending-backup set and work on that. 167 List<String> queue = new ArrayList<>(); 168 DataChangedJournal oldJournal = backupManagerService.getJournal(); 169 synchronized (backupManagerService.getQueueLock()) { 170 // Don't run backups if one is already running. 171 if (backupManagerService.isBackupRunning()) { 172 Slog.i(TAG, "Backup time but one already running"); 173 return; 174 } 175 176 if (DEBUG) { 177 Slog.v(TAG, "Running a backup pass"); 178 } 179 180 // Acquire the wakelock and pass it to the backup thread. It will be released 181 // once backup concludes. 182 backupManagerService.setBackupRunning(true); 183 backupManagerService.getWakelock().acquire(); 184 185 // Do we have any work to do? Construct the work queue 186 // then release the synchronization lock to actually run 187 // the backup. 188 if (backupManagerService.getPendingBackups().size() > 0) { 189 for (BackupRequest b : backupManagerService.getPendingBackups().values()) { 190 queue.add(b.packageName); 191 } 192 if (DEBUG) { 193 Slog.v(TAG, "clearing pending backups"); 194 } 195 backupManagerService.getPendingBackups().clear(); 196 197 // Start a new backup-queue journal file too 198 backupManagerService.setJournal(null); 199 200 } 201 } 202 203 // At this point, we have started a new journal file, and the old 204 // file identity is being passed to the backup processing task. 205 // When it completes successfully, that old journal file will be 206 // deleted. If we crash prior to that, the old journal is parsed 207 // at next boot and the journaled requests fulfilled. 208 boolean staged = true; 209 if (queue.size() > 0) { 210 // Spin up a backup state sequence and set it running 211 try { 212 OnTaskFinishedListener listener = 213 caller -> 214 transportManager 215 .disposeOfTransportClient(transportClient, caller); 216 KeyValueBackupTask.start( 217 backupManagerService, 218 transportClient, 219 transport.transportDirName(), 220 queue, 221 oldJournal, 222 /* observer */ null, 223 /* monitor */ null, 224 listener, 225 Collections.emptyList(), 226 /* userInitiated */ false, 227 /* nonIncremental */ false, 228 backupManagerService.getEligibilityRulesForOperation( 229 OperationType.BACKUP)); 230 } catch (Exception e) { 231 // unable to ask the transport its dir name -- transient failure, since 232 // the above check succeeded. Try again next time. 233 Slog.e(TAG, "Transport became unavailable attempting backup" 234 + " or error initializing backup task", e); 235 staged = false; 236 } 237 } else { 238 Slog.v(TAG, "Backup requested but nothing pending"); 239 staged = false; 240 } 241 242 if (!staged) { 243 transportManager.disposeOfTransportClient(transportClient, callerLogString); 244 // if we didn't actually hand off the wakelock, rewind until next time 245 synchronized (backupManagerService.getQueueLock()) { 246 backupManagerService.setBackupRunning(false); 247 } 248 backupManagerService.getWakelock().release(); 249 } 250 break; 251 } 252 253 case MSG_BACKUP_RESTORE_STEP: { 254 try { 255 BackupRestoreTask task = (BackupRestoreTask) msg.obj; 256 if (MORE_DEBUG) { 257 Slog.v(TAG, "Got next step for " + task + ", executing"); 258 } 259 task.execute(); 260 } catch (ClassCastException e) { 261 Slog.e(TAG, "Invalid backup/restore task in flight, obj=" + msg.obj); 262 } 263 break; 264 } 265 266 case MSG_OP_COMPLETE: { 267 try { 268 Pair<BackupRestoreTask, Long> taskWithResult = 269 (Pair<BackupRestoreTask, Long>) msg.obj; 270 taskWithResult.first.operationComplete(taskWithResult.second); 271 } catch (ClassCastException e) { 272 Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj); 273 } 274 break; 275 } 276 277 case MSG_RUN_ADB_BACKUP: { 278 // TODO: refactor full backup to be a looper-based state machine 279 // similar to normal backup/restore. 280 AdbBackupParams params = (AdbBackupParams) msg.obj; 281 PerformAdbBackupTask task = new PerformAdbBackupTask(backupManagerService, 282 params.fd, 283 params.observer, params.includeApks, params.includeObbs, 284 params.includeShared, params.doWidgets, params.curPassword, 285 params.encryptPassword, params.allApps, params.includeSystem, 286 params.doCompress, params.includeKeyValue, params.packages, params.latch, 287 params.backupEligibilityRules); 288 (new Thread(task, "adb-backup")).start(); 289 break; 290 } 291 292 case MSG_RUN_RESTORE: { 293 RestoreParams params = (RestoreParams) msg.obj; 294 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); 295 296 PerformUnifiedRestoreTask task = 297 new PerformUnifiedRestoreTask( 298 backupManagerService, 299 params.transportClient, 300 params.observer, 301 params.monitor, 302 params.token, 303 params.packageInfo, 304 params.pmToken, 305 params.isSystemRestore, 306 params.filterSet, 307 params.listener, 308 params.backupEligibilityRules); 309 310 synchronized (backupManagerService.getPendingRestores()) { 311 if (backupManagerService.isRestoreInProgress()) { 312 if (DEBUG) { 313 Slog.d(TAG, "Restore in progress, queueing."); 314 } 315 backupManagerService.getPendingRestores().add(task); 316 // This task will be picked up and executed when the the currently running 317 // restore task finishes. 318 } else { 319 if (DEBUG) { 320 Slog.d(TAG, "Starting restore."); 321 } 322 backupManagerService.setRestoreInProgress(true); 323 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task); 324 sendMessage(restoreMsg); 325 } 326 } 327 break; 328 } 329 330 case MSG_RUN_ADB_RESTORE: { 331 // TODO: refactor full restore to be a looper-based state machine 332 // similar to normal backup/restore. 333 AdbRestoreParams params = (AdbRestoreParams) msg.obj; 334 PerformAdbRestoreTask task = new PerformAdbRestoreTask(backupManagerService, 335 params.fd, 336 params.curPassword, params.encryptPassword, 337 params.observer, params.latch); 338 (new Thread(task, "adb-restore")).start(); 339 break; 340 } 341 342 case MSG_RUN_CLEAR: { 343 ClearParams params = (ClearParams) msg.obj; 344 Runnable task = 345 new PerformClearTask( 346 backupManagerService, 347 params.transportClient, 348 params.packageInfo, 349 params.listener); 350 task.run(); 351 break; 352 } 353 354 case MSG_RETRY_CLEAR: { 355 // reenqueues if the transport remains unavailable 356 ClearRetryParams params = (ClearRetryParams) msg.obj; 357 backupManagerService.clearBackupData(params.transportName, params.packageName); 358 break; 359 } 360 361 case MSG_RUN_GET_RESTORE_SETS: { 362 // Like other async operations, this is entered with the wakelock held 363 RestoreSet[] sets = null; 364 RestoreGetSetsParams params = (RestoreGetSetsParams) msg.obj; 365 String callerLogString = "BH/MSG_RUN_GET_RESTORE_SETS"; 366 try { 367 IBackupTransport transport = 368 params.transportClient.connectOrThrow(callerLogString); 369 sets = transport.getAvailableRestoreSets(); 370 // cache the result in the active session 371 synchronized (params.session) { 372 params.session.setRestoreSets(sets); 373 } 374 if (sets == null) { 375 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 376 } 377 } catch (Exception e) { 378 Slog.e(TAG, "Error from transport getting set list: " + e.getMessage()); 379 } finally { 380 if (params.observer != null) { 381 try { 382 params.observer.restoreSetsAvailable(sets); 383 } catch (RemoteException re) { 384 Slog.e(TAG, "Unable to report listing to observer"); 385 } catch (Exception e) { 386 Slog.e(TAG, "Restore observer threw: " + e.getMessage()); 387 } 388 } 389 390 // Done: reset the session timeout clock 391 removeMessages(MSG_RESTORE_SESSION_TIMEOUT); 392 sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, 393 mAgentTimeoutParameters.getRestoreSessionTimeoutMillis()); 394 395 params.listener.onFinished(callerLogString); 396 } 397 break; 398 } 399 400 case MSG_BACKUP_OPERATION_TIMEOUT: 401 case MSG_RESTORE_OPERATION_TIMEOUT: { 402 Slog.d(TAG, "Timeout message received for token=" + Integer.toHexString(msg.arg1)); 403 backupManagerService.handleCancel(msg.arg1, false); 404 break; 405 } 406 407 case MSG_RESTORE_SESSION_TIMEOUT: { 408 synchronized (backupManagerService) { 409 if (backupManagerService.getActiveRestoreSession() != null) { 410 // Client app left the restore session dangling. We know that it 411 // can't be in the middle of an actual restore operation because 412 // the timeout is suspended while a restore is in progress. Clean 413 // up now. 414 Slog.w(TAG, "Restore session timed out; aborting"); 415 backupManagerService.getActiveRestoreSession().markTimedOut(); 416 post(backupManagerService.getActiveRestoreSession().new EndRestoreRunnable( 417 backupManagerService, 418 backupManagerService.getActiveRestoreSession())); 419 } 420 } 421 break; 422 } 423 424 case MSG_FULL_CONFIRMATION_TIMEOUT: { 425 synchronized (backupManagerService.getAdbBackupRestoreConfirmations()) { 426 AdbParams params = backupManagerService.getAdbBackupRestoreConfirmations().get( 427 msg.arg1); 428 if (params != null) { 429 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation"); 430 431 // Release the waiter; timeout == completion 432 backupManagerService.signalAdbBackupRestoreCompletion(params); 433 434 // Remove the token from the set 435 backupManagerService.getAdbBackupRestoreConfirmations().delete(msg.arg1); 436 437 // Report a timeout to the observer, if any 438 if (params.observer != null) { 439 try { 440 params.observer.onTimeout(); 441 } catch (RemoteException e) { 442 /* don't care if the app has gone away */ 443 } 444 } 445 } else { 446 Slog.d(TAG, "couldn't find params for token " + msg.arg1); 447 } 448 } 449 break; 450 } 451 452 case MSG_REQUEST_BACKUP: { 453 BackupParams params = (BackupParams) msg.obj; 454 if (MORE_DEBUG) { 455 Slog.d(TAG, "MSG_REQUEST_BACKUP observer=" + params.observer); 456 } 457 backupManagerService.setBackupRunning(true); 458 backupManagerService.getWakelock().acquire(); 459 460 KeyValueBackupTask.start( 461 backupManagerService, 462 params.transportClient, 463 params.dirName, 464 params.kvPackages, 465 /* dataChangedJournal */ null, 466 params.observer, 467 params.monitor, 468 params.listener, 469 params.fullPackages, 470 /* userInitiated */ true, 471 params.nonIncrementalBackup, 472 params.mBackupEligibilityRules); 473 break; 474 } 475 476 case MSG_SCHEDULE_BACKUP_PACKAGE: { 477 String pkgName = (String) msg.obj; 478 if (MORE_DEBUG) { 479 Slog.d(TAG, "MSG_SCHEDULE_BACKUP_PACKAGE " + pkgName); 480 } 481 backupManagerService.dataChangedImpl(pkgName); 482 break; 483 } 484 } 485 } 486 } 487