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