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