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