Home | History | Annotate | Download | only in internal
      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.TAG;
     20 
     21 import android.annotation.Nullable;
     22 import android.app.AlarmManager;
     23 import android.app.backup.BackupTransport;
     24 import android.app.backup.IBackupObserver;
     25 import android.os.RemoteException;
     26 import android.os.SystemClock;
     27 import android.util.EventLog;
     28 import android.util.Slog;
     29 
     30 import com.android.internal.annotations.VisibleForTesting;
     31 import com.android.internal.backup.IBackupTransport;
     32 import com.android.server.EventLogTags;
     33 import com.android.server.backup.BackupManagerService;
     34 import com.android.server.backup.TransportManager;
     35 import com.android.server.backup.transport.TransportClient;
     36 
     37 import java.io.File;
     38 import java.util.ArrayList;
     39 import 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  */
     51 public 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