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.BackupManagerService.TAG; 20 21import android.annotation.Nullable; 22import android.app.AlarmManager; 23import android.app.backup.BackupTransport; 24import android.app.backup.IBackupObserver; 25import android.os.RemoteException; 26import android.os.SystemClock; 27import android.util.EventLog; 28import android.util.Slog; 29 30import com.android.internal.annotations.VisibleForTesting; 31import com.android.internal.backup.IBackupTransport; 32import com.android.server.EventLogTags; 33import com.android.server.backup.BackupManagerService; 34import com.android.server.backup.TransportManager; 35import com.android.server.backup.transport.TransportClient; 36 37import java.io.File; 38import java.util.ArrayList; 39import java.util.List; 40 41/** 42 * Attempts to call {@link BackupTransport#initializeDevice()} followed by 43 * {@link BackupTransport#finishBackup()} for the transport names passed in with the intent of 44 * wiping backup data from the transport. 45 * 46 * If the transport returns error, it will record the operation as pending and schedule it to run in 47 * a future time according to {@link BackupTransport#requestBackupTime()}. The result status 48 * reported to observers will be the last unsuccessful status reported by the transports. If every 49 * operation was successful then it's {@link BackupTransport#TRANSPORT_OK}. 50 */ 51public class PerformInitializeTask implements Runnable { 52 private final BackupManagerService mBackupManagerService; 53 private final TransportManager mTransportManager; 54 private final String[] mQueue; 55 private final File mBaseStateDir; 56 private final OnTaskFinishedListener mListener; 57 @Nullable private IBackupObserver mObserver; 58 59 public PerformInitializeTask( 60 BackupManagerService backupManagerService, 61 String[] transportNames, 62 @Nullable IBackupObserver observer, 63 OnTaskFinishedListener listener) { 64 this( 65 backupManagerService, 66 backupManagerService.getTransportManager(), 67 transportNames, 68 observer, 69 listener, 70 backupManagerService.getBaseStateDir()); 71 } 72 73 @VisibleForTesting 74 PerformInitializeTask( 75 BackupManagerService backupManagerService, 76 TransportManager transportManager, 77 String[] transportNames, 78 @Nullable IBackupObserver observer, 79 OnTaskFinishedListener listener, 80 File baseStateDir) { 81 mBackupManagerService = backupManagerService; 82 mTransportManager = transportManager; 83 mQueue = transportNames; 84 mObserver = observer; 85 mListener = listener; 86 mBaseStateDir = baseStateDir; 87 } 88 89 private void notifyResult(String target, int status) { 90 try { 91 if (mObserver != null) { 92 mObserver.onResult(target, status); 93 } 94 } catch (RemoteException ignored) { 95 mObserver = null; // don't try again 96 } 97 } 98 99 private void notifyFinished(int status) { 100 try { 101 if (mObserver != null) { 102 mObserver.backupFinished(status); 103 } 104 } catch (RemoteException ignored) { 105 mObserver = null; 106 } 107 } 108 109 public void run() { 110 // mWakelock is *acquired* when execution begins here 111 String callerLogString = "PerformInitializeTask.run()"; 112 List<TransportClient> transportClientsToDisposeOf = new ArrayList<>(mQueue.length); 113 int result = BackupTransport.TRANSPORT_OK; 114 try { 115 for (String transportName : mQueue) { 116 TransportClient transportClient = 117 mTransportManager.getTransportClient(transportName, callerLogString); 118 if (transportClient == null) { 119 Slog.e(TAG, "Requested init for " + transportName + " but not found"); 120 continue; 121 } 122 transportClientsToDisposeOf.add(transportClient); 123 124 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); 125 String transportDirName = 126 mTransportManager.getTransportDirName( 127 transportClient.getTransportComponent()); 128 EventLog.writeEvent(EventLogTags.BACKUP_START, transportDirName); 129 long startRealtime = SystemClock.elapsedRealtime(); 130 131 IBackupTransport transport = transportClient.connectOrThrow(callerLogString); 132 int status = transport.initializeDevice(); 133 134 if (status == BackupTransport.TRANSPORT_OK) { 135 status = transport.finishBackup(); 136 } 137 138 // Okay, the wipe really happened. Clean up our local bookkeeping. 139 if (status == BackupTransport.TRANSPORT_OK) { 140 Slog.i(TAG, "Device init successful"); 141 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); 142 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 143 File stateFileDir = new File(mBaseStateDir, transportDirName); 144 mBackupManagerService.resetBackupState(stateFileDir); 145 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); 146 mBackupManagerService.recordInitPending(false, transportName, transportDirName); 147 notifyResult(transportName, BackupTransport.TRANSPORT_OK); 148 } else { 149 // If this didn't work, requeue this one and try again 150 // after a suitable interval 151 Slog.e(TAG, "Transport error in initializeDevice()"); 152 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 153 mBackupManagerService.recordInitPending(true, transportName, transportDirName); 154 notifyResult(transportName, status); 155 result = status; 156 157 // do this via another alarm to make sure of the wakelock states 158 long delay = transport.requestBackupTime(); 159 Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay); 160 mBackupManagerService.getAlarmManager().set( 161 AlarmManager.RTC_WAKEUP, 162 System.currentTimeMillis() + delay, 163 mBackupManagerService.getRunInitIntent()); 164 } 165 } 166 } catch (Exception e) { 167 Slog.e(TAG, "Unexpected error performing init", e); 168 result = BackupTransport.TRANSPORT_ERROR; 169 } finally { 170 for (TransportClient transportClient : transportClientsToDisposeOf) { 171 mTransportManager.disposeOfTransportClient(transportClient, callerLogString); 172 } 173 notifyFinished(result); 174 mListener.onFinished(callerLogString); 175 } 176 } 177} 178