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