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