blob: 6b78fbf608992a1a28551ca55aed64668de77f01 [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.TAG;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupObserver;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.EventLog;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.transport.TransportClient;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* Attempts to call {@link BackupTransport#initializeDevice()} followed by
* {@link BackupTransport#finishBackup()} for the transport names passed in with the intent of
* wiping backup data from the transport.
*
* If the transport returns error, it will record the operation as pending and schedule it to run in
* a future time according to {@link BackupTransport#requestBackupTime()}. The result status
* reported to observers will be the last unsuccessful status reported by the transports. If every
* operation was successful then it's {@link BackupTransport#TRANSPORT_OK}.
*/
public class PerformInitializeTask implements Runnable {
private final UserBackupManagerService mBackupManagerService;
private final TransportManager mTransportManager;
private final String[] mQueue;
private final File mBaseStateDir;
private final OnTaskFinishedListener mListener;
@Nullable private IBackupObserver mObserver;
public PerformInitializeTask(
UserBackupManagerService backupManagerService,
String[] transportNames,
@Nullable IBackupObserver observer,
OnTaskFinishedListener listener) {
this(
backupManagerService,
backupManagerService.getTransportManager(),
transportNames,
observer,
listener,
backupManagerService.getBaseStateDir());
}
@VisibleForTesting
PerformInitializeTask(
UserBackupManagerService backupManagerService,
TransportManager transportManager,
String[] transportNames,
@Nullable IBackupObserver observer,
OnTaskFinishedListener listener,
File baseStateDir) {
mBackupManagerService = backupManagerService;
mTransportManager = transportManager;
mQueue = transportNames;
mObserver = observer;
mListener = listener;
mBaseStateDir = baseStateDir;
}
private void notifyResult(String target, int status) {
try {
if (mObserver != null) {
mObserver.onResult(target, status);
}
} catch (RemoteException ignored) {
mObserver = null; // don't try again
}
}
private void notifyFinished(int status) {
try {
if (mObserver != null) {
mObserver.backupFinished(status);
}
} catch (RemoteException ignored) {
mObserver = null;
}
}
public void run() {
// mWakelock is *acquired* when execution begins here
String callerLogString = "PerformInitializeTask.run()";
List<TransportClient> transportClientsToDisposeOf = new ArrayList<>(mQueue.length);
int result = BackupTransport.TRANSPORT_OK;
try {
for (String transportName : mQueue) {
TransportClient transportClient =
mTransportManager.getTransportClient(transportName, callerLogString);
if (transportClient == null) {
Slog.e(TAG, "Requested init for " + transportName + " but not found");
continue;
}
transportClientsToDisposeOf.add(transportClient);
Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
String transportDirName =
mTransportManager.getTransportDirName(
transportClient.getTransportComponent());
EventLog.writeEvent(EventLogTags.BACKUP_START, transportDirName);
long startRealtime = SystemClock.elapsedRealtime();
IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
int status = transport.initializeDevice();
if (status != BackupTransport.TRANSPORT_OK) {
Slog.e(TAG, "Transport error in initializeDevice()");
} else {
status = transport.finishBackup();
if (status != BackupTransport.TRANSPORT_OK) {
Slog.e(TAG, "Transport error in finishBackup()");
}
}
// Okay, the wipe really happened. Clean up our local bookkeeping.
if (status == BackupTransport.TRANSPORT_OK) {
Slog.i(TAG, "Device init successful");
int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
File stateFileDir = new File(mBaseStateDir, transportDirName);
mBackupManagerService.resetBackupState(stateFileDir);
EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
mBackupManagerService.recordInitPending(false, transportName, transportDirName);
notifyResult(transportName, BackupTransport.TRANSPORT_OK);
} else {
// If this didn't work, requeue this one and try again
// after a suitable interval
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
mBackupManagerService.recordInitPending(true, transportName, transportDirName);
notifyResult(transportName, status);
result = status;
// do this via another alarm to make sure of the wakelock states
long delay = transport.requestBackupTime();
Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay);
mBackupManagerService.getAlarmManager().set(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + delay,
mBackupManagerService.getRunInitIntent());
}
}
} catch (Exception e) {
Slog.e(TAG, "Unexpected error performing init", e);
result = BackupTransport.TRANSPORT_ERROR;
} finally {
for (TransportClient transportClient : transportClientsToDisposeOf) {
mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
}
notifyFinished(result);
mListener.onFinished(callerLogString);
}
}
}