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