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.TAG; 21 22 import android.app.backup.BackupAnnotations.BackupDestination; 23 import android.app.backup.IBackupManagerMonitor; 24 import android.app.backup.RestoreSet; 25 import android.os.Handler; 26 import android.os.HandlerThread; 27 import android.os.Message; 28 import android.os.RemoteException; 29 import android.util.EventLog; 30 import android.util.Pair; 31 import android.util.Slog; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.server.EventLogTags; 35 import com.android.server.backup.BackupAgentTimeoutParameters; 36 import com.android.server.backup.BackupRestoreTask; 37 import com.android.server.backup.BackupRestoreTask.CancellationReason; 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.d(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.d(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 Slog.d(TAG, "Running a backup pass"); 181 182 // Acquire the wakelock and pass it to the backup thread. It will be released 183 // once backup concludes. 184 backupManagerService.setBackupRunning(true); 185 backupManagerService.getWakeLock().acquire(); 186 187 // Do we have any work to do? Construct the work queue 188 // then release the synchronization lock to actually run 189 // the backup. 190 if (backupManagerService.getPendingBackups().size() > 0) { 191 for (BackupRequest b : backupManagerService.getPendingBackups().values()) { 192 queue.add(b.packageName); 193 } 194 Slog.d(TAG, "clearing pending backups"); 195 backupManagerService.getPendingBackups().clear(); 196 197 // Start a new backup-queue journal file too 198 backupManagerService.setJournal(null); 199 200 } 201 } 202 203 // Ask the transport for a monitor that will be used to relay log events back to it. 204 IBackupManagerMonitor monitor = null; 205 try { 206 monitor = transport.getBackupManagerMonitor(); 207 } catch (RemoteException e) { 208 Slog.i(TAG, "Failed to retrieve monitor from transport"); 209 } 210 211 // At this point, we have started a new journal file, and the old 212 // file identity is being passed to the backup processing task. 213 // When it completes successfully, that old journal file will be 214 // deleted. If we crash prior to that, the old journal is parsed 215 // at next boot and the journaled requests fulfilled. 216 boolean staged = true; 217 if (queue.size() > 0) { 218 // Spin up a backup state sequence and set it running 219 try { 220 OnTaskFinishedListener listener = 221 caller -> 222 transportManager 223 .disposeOfTransportClient(transportConnection, 224 caller); 225 KeyValueBackupTask.start( 226 backupManagerService, 227 mOperationStorage, 228 transportConnection, 229 transport.transportDirName(), 230 queue, 231 oldJournal, 232 /* observer */ null, 233 monitor, 234 listener, 235 Collections.emptyList(), 236 /* userInitiated */ false, 237 /* nonIncremental */ false, 238 backupManagerService.getEligibilityRulesForOperation( 239 BackupDestination.CLOUD)); 240 } catch (Exception e) { 241 // unable to ask the transport its dir name -- transient failure, since 242 // the above check succeeded. Try again next time. 243 Slog.e(TAG, "Transport became unavailable attempting backup" 244 + " or error initializing backup task", e); 245 staged = false; 246 } 247 } else { 248 Slog.d(TAG, "Backup requested but nothing pending"); 249 staged = false; 250 } 251 252 if (!staged) { 253 transportManager.disposeOfTransportClient(transportConnection, callerLogString); 254 // if we didn't actually hand off the wakelock, rewind until next time 255 synchronized (backupManagerService.getQueueLock()) { 256 backupManagerService.setBackupRunning(false); 257 } 258 backupManagerService.getWakeLock().release(); 259 } 260 break; 261 } 262 263 case MSG_BACKUP_RESTORE_STEP: { 264 try { 265 BackupRestoreTask task = (BackupRestoreTask) msg.obj; 266 if (DEBUG) { 267 Slog.v(TAG, "Got next step for " + task + ", executing"); 268 } 269 task.execute(); 270 } catch (ClassCastException e) { 271 Slog.e(TAG, "Invalid backup/restore task in flight, obj=" + msg.obj); 272 } 273 break; 274 } 275 276 case MSG_OP_COMPLETE: { 277 try { 278 Pair<BackupRestoreTask, Long> taskWithResult = 279 (Pair<BackupRestoreTask, Long>) msg.obj; 280 taskWithResult.first.operationComplete(taskWithResult.second); 281 } catch (ClassCastException e) { 282 Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj); 283 } 284 break; 285 } 286 287 case MSG_RUN_ADB_BACKUP: { 288 // TODO: refactor full backup to be a looper-based state machine 289 // similar to normal backup/restore. 290 AdbBackupParams params = (AdbBackupParams) msg.obj; 291 PerformAdbBackupTask task = new PerformAdbBackupTask( 292 backupManagerService, mOperationStorage, params.fd, 293 params.observer, params.includeApks, params.includeObbs, 294 params.includeShared, params.doWidgets, params.curPassword, 295 params.encryptPassword, params.allApps, params.includeSystem, 296 params.doCompress, params.includeKeyValue, params.packages, params.latch, 297 params.backupEligibilityRules); 298 (new Thread(task, "adb-backup")).start(); 299 break; 300 } 301 302 case MSG_RUN_RESTORE: { 303 RestoreParams params = (RestoreParams) msg.obj; 304 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); 305 306 PerformUnifiedRestoreTask task = 307 new PerformUnifiedRestoreTask( 308 backupManagerService, 309 mOperationStorage, 310 params.mTransportConnection, 311 params.observer, 312 params.monitor, 313 params.token, 314 params.packageInfo, 315 params.pmToken, 316 params.isSystemRestore, 317 params.filterSet, 318 params.listener, 319 params.backupEligibilityRules); 320 321 synchronized (backupManagerService.getPendingRestores()) { 322 if (backupManagerService.isRestoreInProgress()) { 323 Slog.d(TAG, "Restore in progress, queueing."); 324 backupManagerService.getPendingRestores().add(task); 325 // This task will be picked up and executed when the the currently running 326 // restore task finishes. 327 } else { 328 Slog.d(TAG, "Starting restore."); 329 backupManagerService.setRestoreInProgress(true); 330 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task); 331 sendMessage(restoreMsg); 332 } 333 } 334 break; 335 } 336 337 case MSG_RUN_ADB_RESTORE: { 338 // TODO: refactor full restore to be a looper-based state machine 339 // similar to normal backup/restore. 340 AdbRestoreParams params = (AdbRestoreParams) msg.obj; 341 PerformAdbRestoreTask task = new PerformAdbRestoreTask(backupManagerService, 342 mOperationStorage, params.fd, 343 params.curPassword, params.encryptPassword, 344 params.observer, params.latch); 345 (new Thread(task, "adb-restore")).start(); 346 break; 347 } 348 349 case MSG_RUN_CLEAR: { 350 ClearParams params = (ClearParams) msg.obj; 351 Runnable task = 352 new PerformClearTask( 353 backupManagerService, 354 params.mTransportConnection, 355 params.packageInfo, 356 params.listener); 357 task.run(); 358 break; 359 } 360 361 case MSG_RETRY_CLEAR: { 362 // reenqueues if the transport remains unavailable 363 ClearRetryParams params = (ClearRetryParams) msg.obj; 364 backupManagerService.clearBackupData(params.transportName, params.packageName); 365 break; 366 } 367 368 case MSG_RUN_GET_RESTORE_SETS: { 369 // Like other async operations, this is entered with the wakelock held 370 List<RestoreSet> sets = null; 371 RestoreGetSetsParams params = (RestoreGetSetsParams) msg.obj; 372 String callerLogString = "BH/MSG_RUN_GET_RESTORE_SETS"; 373 try { 374 BackupTransportClient transport = 375 params.mTransportConnection.connectOrThrow(callerLogString); 376 sets = transport.getAvailableRestoreSets(); 377 // cache the result in the active session 378 synchronized (params.session) { 379 params.session.setRestoreSets(sets); 380 } 381 if (sets == null) { 382 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 383 } 384 } catch (Exception e) { 385 Slog.e(TAG, "Error from transport getting set list: " + e.getMessage()); 386 } finally { 387 if (params.observer != null) { 388 try { 389 if (sets == null) { 390 params.observer.restoreSetsAvailable(null); 391 } else { 392 params.observer.restoreSetsAvailable( 393 sets.toArray(new RestoreSet[0])); 394 } 395 } catch (RemoteException re) { 396 Slog.e(TAG, "Unable to report listing to observer"); 397 } catch (Exception e) { 398 Slog.e(TAG, "Restore observer threw: " + e.getMessage()); 399 } 400 } 401 402 // Done: reset the session timeout clock 403 removeMessages(MSG_RESTORE_SESSION_TIMEOUT); 404 sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, 405 mAgentTimeoutParameters.getRestoreSessionTimeoutMillis()); 406 407 params.listener.onFinished(callerLogString); 408 } 409 break; 410 } 411 412 case MSG_BACKUP_OPERATION_TIMEOUT: 413 case MSG_RESTORE_OPERATION_TIMEOUT: { 414 Slog.d(TAG, "Timeout for token=" + Integer.toHexString(msg.arg1)); 415 backupManagerService.handleCancel(msg.arg1, CancellationReason.TIMEOUT); 416 break; 417 } 418 419 case MSG_RESTORE_SESSION_TIMEOUT: { 420 synchronized (backupManagerService) { 421 if (backupManagerService.getActiveRestoreSession() != null) { 422 // Client app left the restore session dangling. We know that it 423 // can't be in the middle of an actual restore operation because 424 // the timeout is suspended while a restore is in progress. Clean 425 // up now. 426 Slog.w(TAG, "Restore session timed out; aborting"); 427 backupManagerService.getActiveRestoreSession().markTimedOut(); 428 post(backupManagerService.getActiveRestoreSession().new EndRestoreRunnable( 429 backupManagerService, 430 backupManagerService.getActiveRestoreSession())); 431 } 432 } 433 break; 434 } 435 436 case MSG_FULL_CONFIRMATION_TIMEOUT: { 437 synchronized (backupManagerService.getAdbBackupRestoreConfirmations()) { 438 AdbParams params = backupManagerService.getAdbBackupRestoreConfirmations().get( 439 msg.arg1); 440 if (params != null) { 441 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation"); 442 443 // Release the waiter; timeout == completion 444 backupManagerService.signalAdbBackupRestoreCompletion(params); 445 446 // Remove the token from the set 447 backupManagerService.getAdbBackupRestoreConfirmations().delete(msg.arg1); 448 449 // Report a timeout to the observer, if any 450 if (params.observer != null) { 451 try { 452 params.observer.onTimeout(); 453 } catch (RemoteException e) { 454 /* don't care if the app has gone away */ 455 } 456 } 457 } else { 458 Slog.d(TAG, "couldn't find params for token " + msg.arg1); 459 } 460 } 461 break; 462 } 463 464 case MSG_REQUEST_BACKUP: { 465 BackupParams params = (BackupParams) msg.obj; 466 if (DEBUG) { 467 Slog.d(TAG, "MSG_REQUEST_BACKUP observer=" + params.observer); 468 } 469 backupManagerService.setBackupRunning(true); 470 backupManagerService.getWakeLock().acquire(); 471 472 KeyValueBackupTask.start( 473 backupManagerService, 474 mOperationStorage, 475 params.mTransportConnection, 476 params.dirName, 477 params.kvPackages, 478 /* dataChangedJournal */ null, 479 params.observer, 480 params.monitor, 481 params.listener, 482 params.fullPackages, 483 /* userInitiated */ true, 484 params.nonIncrementalBackup, 485 params.mBackupEligibilityRules); 486 break; 487 } 488 489 case MSG_SCHEDULE_BACKUP_PACKAGE: { 490 String pkgName = (String) msg.obj; 491 if (DEBUG) { 492 Slog.d(TAG, "MSG_SCHEDULE_BACKUP_PACKAGE " + pkgName); 493 } 494 backupManagerService.dataChangedImpl(pkgName); 495 break; 496 } 497 } 498 } 499 } 500