blob: ba153bf90ebea1c871c6dd78737d740306743ab5 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.backup.internal;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.app.backup.RestoreSet;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.EventLog;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformAdbBackupTask;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.keyvalue.BackupRequest;
import com.android.server.backup.keyvalue.KeyValueBackupTask;
import com.android.server.backup.params.AdbBackupParams;
import com.android.server.backup.params.AdbParams;
import com.android.server.backup.params.AdbRestoreParams;
import com.android.server.backup.params.BackupParams;
import com.android.server.backup.params.ClearParams;
import com.android.server.backup.params.ClearRetryParams;
import com.android.server.backup.params.RestoreGetSetsParams;
import com.android.server.backup.params.RestoreParams;
import com.android.server.backup.restore.PerformAdbRestoreTask;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
import com.android.server.backup.transport.TransportClient;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Asynchronous backup/restore handler thread.
*/
public class BackupHandler extends Handler {
public static final int MSG_RUN_BACKUP = 1;
public static final int MSG_RUN_ADB_BACKUP = 2;
public static final int MSG_RUN_RESTORE = 3;
public static final int MSG_RUN_CLEAR = 4;
public static final int MSG_RUN_GET_RESTORE_SETS = 6;
public static final int MSG_RESTORE_SESSION_TIMEOUT = 8;
public static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
public static final int MSG_RUN_ADB_RESTORE = 10;
public static final int MSG_RETRY_INIT = 11;
public static final int MSG_RETRY_CLEAR = 12;
public static final int MSG_WIDGET_BROADCAST = 13;
public static final int MSG_RUN_FULL_TRANSPORT_BACKUP = 14;
public static final int MSG_REQUEST_BACKUP = 15;
public static final int MSG_SCHEDULE_BACKUP_PACKAGE = 16;
public static final int MSG_BACKUP_OPERATION_TIMEOUT = 17;
public static final int MSG_RESTORE_OPERATION_TIMEOUT = 18;
// backup task state machine tick
public static final int MSG_BACKUP_RESTORE_STEP = 20;
public static final int MSG_OP_COMPLETE = 21;
private final UserBackupManagerService backupManagerService;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public BackupHandler(UserBackupManagerService backupManagerService, Looper looper) {
super(looper);
this.backupManagerService = backupManagerService;
mAgentTimeoutParameters = Preconditions.checkNotNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
}
public void handleMessage(Message msg) {
TransportManager transportManager = backupManagerService.getTransportManager();
switch (msg.what) {
case MSG_RUN_BACKUP: {
backupManagerService.setLastBackupPass(System.currentTimeMillis());
String callerLogString = "BH/MSG_RUN_BACKUP";
TransportClient transportClient =
transportManager.getCurrentTransportClient(callerLogString);
IBackupTransport transport =
transportClient != null
? transportClient.connect(callerLogString)
: null;
if (transport == null) {
if (transportClient != null) {
transportManager
.disposeOfTransportClient(transportClient, callerLogString);
}
Slog.v(TAG, "Backup requested but no transport available");
synchronized (backupManagerService.getQueueLock()) {
backupManagerService.setBackupRunning(false);
}
backupManagerService.getWakelock().release();
break;
}
// Snapshot the pending-backup set and work on that.
List<String> queue = new ArrayList<>();
DataChangedJournal oldJournal = backupManagerService.getJournal();
synchronized (backupManagerService.getQueueLock()) {
// Do we have any work to do? Construct the work queue
// then release the synchronization lock to actually run
// the backup.
if (backupManagerService.getPendingBackups().size() > 0) {
for (BackupRequest b : backupManagerService.getPendingBackups().values()) {
queue.add(b.packageName);
}
if (DEBUG) {
Slog.v(TAG, "clearing pending backups");
}
backupManagerService.getPendingBackups().clear();
// Start a new backup-queue journal file too
backupManagerService.setJournal(null);
}
}
// At this point, we have started a new journal file, and the old
// file identity is being passed to the backup processing task.
// When it completes successfully, that old journal file will be
// deleted. If we crash prior to that, the old journal is parsed
// at next boot and the journaled requests fulfilled.
boolean staged = true;
if (queue.size() > 0) {
// Spin up a backup state sequence and set it running
try {
OnTaskFinishedListener listener =
caller ->
transportManager
.disposeOfTransportClient(transportClient, caller);
KeyValueBackupTask.start(
backupManagerService,
transportClient,
transport.transportDirName(),
queue,
oldJournal,
/* observer */ null,
/* monitor */ null,
listener,
Collections.emptyList(),
/* userInitiated */ false,
/* nonIncremental */ false);
} catch (Exception e) {
// unable to ask the transport its dir name -- transient failure, since
// the above check succeeded. Try again next time.
Slog.e(TAG, "Transport became unavailable attempting backup"
+ " or error initializing backup task", e);
staged = false;
}
} else {
Slog.v(TAG, "Backup requested but nothing pending");
staged = false;
}
if (!staged) {
transportManager.disposeOfTransportClient(transportClient, callerLogString);
// if we didn't actually hand off the wakelock, rewind until next time
synchronized (backupManagerService.getQueueLock()) {
backupManagerService.setBackupRunning(false);
}
backupManagerService.getWakelock().release();
}
break;
}
case MSG_BACKUP_RESTORE_STEP: {
try {
BackupRestoreTask task = (BackupRestoreTask) msg.obj;
if (MORE_DEBUG) {
Slog.v(TAG, "Got next step for " + task + ", executing");
}
task.execute();
} catch (ClassCastException e) {
Slog.e(TAG, "Invalid backup/restore task in flight, obj=" + msg.obj);
}
break;
}
case MSG_OP_COMPLETE: {
try {
Pair<BackupRestoreTask, Long> taskWithResult =
(Pair<BackupRestoreTask, Long>) msg.obj;
taskWithResult.first.operationComplete(taskWithResult.second);
} catch (ClassCastException e) {
Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
}
break;
}
case MSG_RUN_ADB_BACKUP: {
// TODO: refactor full backup to be a looper-based state machine
// similar to normal backup/restore.
AdbBackupParams params = (AdbBackupParams) msg.obj;
PerformAdbBackupTask task = new PerformAdbBackupTask(backupManagerService,
params.fd,
params.observer, params.includeApks, params.includeObbs,
params.includeShared, params.doWidgets, params.curPassword,
params.encryptPassword, params.allApps, params.includeSystem,
params.doCompress, params.includeKeyValue, params.packages, params.latch);
(new Thread(task, "adb-backup")).start();
break;
}
case MSG_RUN_FULL_TRANSPORT_BACKUP: {
PerformFullTransportBackupTask task = (PerformFullTransportBackupTask) msg.obj;
(new Thread(task, "transport-backup")).start();
break;
}
case MSG_RUN_RESTORE: {
RestoreParams params = (RestoreParams) msg.obj;
Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
PerformUnifiedRestoreTask task =
new PerformUnifiedRestoreTask(
backupManagerService,
params.transportClient,
params.observer,
params.monitor,
params.token,
params.packageInfo,
params.pmToken,
params.isSystemRestore,
params.filterSet,
params.listener);
synchronized (backupManagerService.getPendingRestores()) {
if (backupManagerService.isRestoreInProgress()) {
if (DEBUG) {
Slog.d(TAG, "Restore in progress, queueing.");
}
backupManagerService.getPendingRestores().add(task);
// This task will be picked up and executed when the the currently running
// restore task finishes.
} else {
if (DEBUG) {
Slog.d(TAG, "Starting restore.");
}
backupManagerService.setRestoreInProgress(true);
Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
sendMessage(restoreMsg);
}
}
break;
}
case MSG_RUN_ADB_RESTORE: {
// TODO: refactor full restore to be a looper-based state machine
// similar to normal backup/restore.
AdbRestoreParams params = (AdbRestoreParams) msg.obj;
PerformAdbRestoreTask task = new PerformAdbRestoreTask(backupManagerService,
params.fd,
params.curPassword, params.encryptPassword,
params.observer, params.latch);
(new Thread(task, "adb-restore")).start();
break;
}
case MSG_RUN_CLEAR: {
ClearParams params = (ClearParams) msg.obj;
Runnable task =
new PerformClearTask(
backupManagerService,
params.transportClient,
params.packageInfo,
params.listener);
task.run();
break;
}
case MSG_RETRY_CLEAR: {
// reenqueues if the transport remains unavailable
ClearRetryParams params = (ClearRetryParams) msg.obj;
backupManagerService.clearBackupData(params.transportName, params.packageName);
break;
}
case MSG_RUN_GET_RESTORE_SETS: {
// Like other async operations, this is entered with the wakelock held
RestoreSet[] sets = null;
RestoreGetSetsParams params = (RestoreGetSetsParams) msg.obj;
String callerLogString = "BH/MSG_RUN_GET_RESTORE_SETS";
try {
IBackupTransport transport =
params.transportClient.connectOrThrow(callerLogString);
sets = transport.getAvailableRestoreSets();
// cache the result in the active session
synchronized (params.session) {
params.session.setRestoreSets(sets);
}
if (sets == null) {
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
}
} catch (Exception e) {
Slog.e(TAG, "Error from transport getting set list: " + e.getMessage());
} finally {
if (params.observer != null) {
try {
params.observer.restoreSetsAvailable(sets);
} catch (RemoteException re) {
Slog.e(TAG, "Unable to report listing to observer");
} catch (Exception e) {
Slog.e(TAG, "Restore observer threw: " + e.getMessage());
}
}
// Done: reset the session timeout clock
removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
params.listener.onFinished(callerLogString);
}
break;
}
case MSG_BACKUP_OPERATION_TIMEOUT:
case MSG_RESTORE_OPERATION_TIMEOUT: {
Slog.d(TAG, "Timeout message received for token=" + Integer.toHexString(msg.arg1));
backupManagerService.handleCancel(msg.arg1, false);
break;
}
case MSG_RESTORE_SESSION_TIMEOUT: {
synchronized (backupManagerService) {
if (backupManagerService.getActiveRestoreSession() != null) {
// Client app left the restore session dangling. We know that it
// can't be in the middle of an actual restore operation because
// the timeout is suspended while a restore is in progress. Clean
// up now.
Slog.w(TAG, "Restore session timed out; aborting");
backupManagerService.getActiveRestoreSession().markTimedOut();
post(backupManagerService.getActiveRestoreSession().new EndRestoreRunnable(
backupManagerService,
backupManagerService.getActiveRestoreSession()));
}
}
break;
}
case MSG_FULL_CONFIRMATION_TIMEOUT: {
synchronized (backupManagerService.getAdbBackupRestoreConfirmations()) {
AdbParams params = backupManagerService.getAdbBackupRestoreConfirmations().get(
msg.arg1);
if (params != null) {
Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
// Release the waiter; timeout == completion
backupManagerService.signalAdbBackupRestoreCompletion(params);
// Remove the token from the set
backupManagerService.getAdbBackupRestoreConfirmations().delete(msg.arg1);
// Report a timeout to the observer, if any
if (params.observer != null) {
try {
params.observer.onTimeout();
} catch (RemoteException e) {
/* don't care if the app has gone away */
}
}
} else {
Slog.d(TAG, "couldn't find params for token " + msg.arg1);
}
}
break;
}
case MSG_WIDGET_BROADCAST: {
final Intent intent = (Intent) msg.obj;
backupManagerService.getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM);
break;
}
case MSG_REQUEST_BACKUP: {
BackupParams params = (BackupParams) msg.obj;
if (MORE_DEBUG) {
Slog.d(TAG, "MSG_REQUEST_BACKUP observer=" + params.observer);
}
backupManagerService.setBackupRunning(true);
backupManagerService.getWakelock().acquire();
KeyValueBackupTask.start(
backupManagerService,
params.transportClient,
params.dirName,
params.kvPackages,
/* dataChangedJournal */ null,
params.observer,
params.monitor,
params.listener,
params.fullPackages,
/* userInitiated */ true,
params.nonIncrementalBackup);
break;
}
case MSG_SCHEDULE_BACKUP_PACKAGE: {
String pkgName = (String) msg.obj;
if (MORE_DEBUG) {
Slog.d(TAG, "MSG_SCHEDULE_BACKUP_PACKAGE " + pkgName);
}
backupManagerService.dataChangedImpl(pkgName);
break;
}
}
}
}