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