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