• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.BackupManager.OperationType;
24 import android.app.backup.RestoreSet;
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.os.Message;
28 import android.os.RemoteException;
29 import android.util.EventLog;
30 import android.util.Pair;
31 import android.util.Slog;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.server.EventLogTags;
35 import com.android.server.backup.BackupAgentTimeoutParameters;
36 import com.android.server.backup.BackupRestoreTask;
37 import com.android.server.backup.DataChangedJournal;
38 import com.android.server.backup.OperationStorage;
39 import com.android.server.backup.TransportManager;
40 import com.android.server.backup.UserBackupManagerService;
41 import com.android.server.backup.fullbackup.PerformAdbBackupTask;
42 import com.android.server.backup.keyvalue.BackupRequest;
43 import com.android.server.backup.keyvalue.KeyValueBackupTask;
44 import com.android.server.backup.params.AdbBackupParams;
45 import com.android.server.backup.params.AdbParams;
46 import com.android.server.backup.params.AdbRestoreParams;
47 import com.android.server.backup.params.BackupParams;
48 import com.android.server.backup.params.ClearParams;
49 import com.android.server.backup.params.ClearRetryParams;
50 import com.android.server.backup.params.RestoreGetSetsParams;
51 import com.android.server.backup.params.RestoreParams;
52 import com.android.server.backup.restore.PerformAdbRestoreTask;
53 import com.android.server.backup.restore.PerformUnifiedRestoreTask;
54 import com.android.server.backup.transport.BackupTransportClient;
55 import com.android.server.backup.transport.TransportConnection;
56 
57 import java.util.ArrayList;
58 import java.util.Collections;
59 import java.util.List;
60 import java.util.Objects;
61 
62 /**
63  * Asynchronous backup/restore handler thread.
64  */
65 public class BackupHandler extends Handler {
66 
67     public static final int MSG_RUN_BACKUP = 1;
68     public static final int MSG_RUN_ADB_BACKUP = 2;
69     public static final int MSG_RUN_RESTORE = 3;
70     public static final int MSG_RUN_CLEAR = 4;
71     public static final int MSG_RUN_GET_RESTORE_SETS = 6;
72     public static final int MSG_RESTORE_SESSION_TIMEOUT = 8;
73     public static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
74     public static final int MSG_RUN_ADB_RESTORE = 10;
75     public static final int MSG_RETRY_CLEAR = 12;
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     // Release the wakelock. This is used to ensure we don't hold it after
84     // a user is removed. This will also terminate the looper thread.
85     public static final int MSG_STOP = 22;
86 
87     private final UserBackupManagerService backupManagerService;
88     private final OperationStorage mOperationStorage;
89     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
90 
91     private final HandlerThread mBackupThread;
92 
93     @VisibleForTesting
94     volatile boolean mIsStopping = false;
95 
BackupHandler( UserBackupManagerService backupManagerService, OperationStorage operationStorage, HandlerThread backupThread)96     public BackupHandler(
97             UserBackupManagerService backupManagerService, OperationStorage operationStorage,
98             HandlerThread backupThread) {
99         super(backupThread.getLooper());
100         mBackupThread = backupThread;
101         this.backupManagerService = backupManagerService;
102         mOperationStorage = operationStorage;
103         mAgentTimeoutParameters = Objects.requireNonNull(
104                 backupManagerService.getAgentTimeoutParameters(),
105                 "Timeout parameters cannot be null");
106     }
107 
108     /**
109      * Put the BackupHandler into a stopping state where the remaining messages on the queue will be
110      * silently dropped and the {@link WakeLock} held by the {@link UserBackupManagerService} will
111      * then be released.
112      */
stop()113     public void stop() {
114         mIsStopping = true;
115         sendMessage(obtainMessage(BackupHandler.MSG_STOP));
116     }
117 
118     @Override
dispatchMessage(Message message)119     public void dispatchMessage(Message message) {
120         try {
121             dispatchMessageInternal(message);
122         } catch (Exception e) {
123             // If the backup service is stopping, we'll suppress all exceptions to avoid crashes
124             // caused by code still running after the current user has become unavailable.
125             if (!mIsStopping) {
126                 throw e;
127             }
128         }
129     }
130 
131     @VisibleForTesting
dispatchMessageInternal(Message message)132     void dispatchMessageInternal(Message message) {
133         super.dispatchMessage(message);
134     }
135 
handleMessage(Message msg)136     public void handleMessage(Message msg) {
137         if (msg.what == MSG_STOP) {
138             Slog.v(TAG, "Stopping backup handler");
139             backupManagerService.getWakelock().quit();
140             mBackupThread.quitSafely();
141         }
142 
143         if (mIsStopping) {
144             // If we're finishing all other types of messages should be ignored
145             return;
146         }
147 
148         TransportManager transportManager = backupManagerService.getTransportManager();
149         switch (msg.what) {
150             case MSG_RUN_BACKUP: {
151                 backupManagerService.setLastBackupPass(System.currentTimeMillis());
152 
153                 String callerLogString = "BH/MSG_RUN_BACKUP";
154                 TransportConnection transportConnection =
155                         transportManager.getCurrentTransportClient(callerLogString);
156                 BackupTransportClient transport =
157                         transportConnection != null
158                                 ? transportConnection.connect(callerLogString)
159                                 : null;
160                 if (transport == null) {
161                     if (transportConnection != null) {
162                         transportManager
163                                 .disposeOfTransportClient(transportConnection, callerLogString);
164                     }
165                     Slog.v(TAG, "Backup requested but no transport available");
166                     break;
167                 }
168 
169                 // Snapshot the pending-backup set and work on that.
170                 List<String> queue = new ArrayList<>();
171                 DataChangedJournal oldJournal = backupManagerService.getJournal();
172                 synchronized (backupManagerService.getQueueLock()) {
173                     // Don't run backups if one is already running.
174                     if (backupManagerService.isBackupRunning()) {
175                         Slog.i(TAG, "Backup time but one already running");
176                         return;
177                     }
178 
179                     if (DEBUG) {
180                         Slog.v(TAG, "Running a backup pass");
181                     }
182 
183                     // Acquire the wakelock and pass it to the backup thread. It will be released
184                     // once backup concludes.
185                     backupManagerService.setBackupRunning(true);
186                     backupManagerService.getWakelock().acquire();
187 
188                     // Do we have any work to do?  Construct the work queue
189                     // then release the synchronization lock to actually run
190                     // the backup.
191                     if (backupManagerService.getPendingBackups().size() > 0) {
192                         for (BackupRequest b : backupManagerService.getPendingBackups().values()) {
193                             queue.add(b.packageName);
194                         }
195                         if (DEBUG) {
196                             Slog.v(TAG, "clearing pending backups");
197                         }
198                         backupManagerService.getPendingBackups().clear();
199 
200                         // Start a new backup-queue journal file too
201                         backupManagerService.setJournal(null);
202 
203                     }
204                 }
205 
206                 // At this point, we have started a new journal file, and the old
207                 // file identity is being passed to the backup processing task.
208                 // When it completes successfully, that old journal file will be
209                 // deleted.  If we crash prior to that, the old journal is parsed
210                 // at next boot and the journaled requests fulfilled.
211                 boolean staged = true;
212                 if (queue.size() > 0) {
213                     // Spin up a backup state sequence and set it running
214                     try {
215                         OnTaskFinishedListener listener =
216                                 caller ->
217                                         transportManager
218                                                 .disposeOfTransportClient(transportConnection,
219                                                         caller);
220                         KeyValueBackupTask.start(
221                                 backupManagerService,
222                                 mOperationStorage,
223                                 transportConnection,
224                                 transport.transportDirName(),
225                                 queue,
226                                 oldJournal,
227                                 /* observer */ null,
228                                 /* monitor */ null,
229                                 listener,
230                                 Collections.emptyList(),
231                                 /* userInitiated */ false,
232                                 /* nonIncremental */ false,
233                                 backupManagerService.getEligibilityRulesForOperation(
234                                         OperationType.BACKUP));
235                     } catch (Exception e) {
236                         // unable to ask the transport its dir name -- transient failure, since
237                         // the above check succeeded.  Try again next time.
238                         Slog.e(TAG, "Transport became unavailable attempting backup"
239                                 + " or error initializing backup task", e);
240                         staged = false;
241                     }
242                 } else {
243                     Slog.v(TAG, "Backup requested but nothing pending");
244                     staged = false;
245                 }
246 
247                 if (!staged) {
248                     transportManager.disposeOfTransportClient(transportConnection, callerLogString);
249                     // if we didn't actually hand off the wakelock, rewind until next time
250                     synchronized (backupManagerService.getQueueLock()) {
251                         backupManagerService.setBackupRunning(false);
252                     }
253                     backupManagerService.getWakelock().release();
254                 }
255                 break;
256             }
257 
258             case MSG_BACKUP_RESTORE_STEP: {
259                 try {
260                     BackupRestoreTask task = (BackupRestoreTask) msg.obj;
261                     if (MORE_DEBUG) {
262                         Slog.v(TAG, "Got next step for " + task + ", executing");
263                     }
264                     task.execute();
265                 } catch (ClassCastException e) {
266                     Slog.e(TAG, "Invalid backup/restore task in flight, obj=" + msg.obj);
267                 }
268                 break;
269             }
270 
271             case MSG_OP_COMPLETE: {
272                 try {
273                     Pair<BackupRestoreTask, Long> taskWithResult =
274                             (Pair<BackupRestoreTask, Long>) msg.obj;
275                     taskWithResult.first.operationComplete(taskWithResult.second);
276                 } catch (ClassCastException e) {
277                     Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
278                 }
279                 break;
280             }
281 
282             case MSG_RUN_ADB_BACKUP: {
283                 // TODO: refactor full backup to be a looper-based state machine
284                 // similar to normal backup/restore.
285                 AdbBackupParams params = (AdbBackupParams) msg.obj;
286                 PerformAdbBackupTask task = new PerformAdbBackupTask(
287                         backupManagerService, mOperationStorage, params.fd,
288                         params.observer, params.includeApks, params.includeObbs,
289                         params.includeShared, params.doWidgets, params.curPassword,
290                         params.encryptPassword, params.allApps, params.includeSystem,
291                         params.doCompress, params.includeKeyValue, params.packages, params.latch,
292                         params.backupEligibilityRules);
293                 (new Thread(task, "adb-backup")).start();
294                 break;
295             }
296 
297             case MSG_RUN_RESTORE: {
298                 RestoreParams params = (RestoreParams) msg.obj;
299                 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
300 
301                 PerformUnifiedRestoreTask task =
302                         new PerformUnifiedRestoreTask(
303                                 backupManagerService,
304                                 mOperationStorage,
305                                 params.mTransportConnection,
306                                 params.observer,
307                                 params.monitor,
308                                 params.token,
309                                 params.packageInfo,
310                                 params.pmToken,
311                                 params.isSystemRestore,
312                                 params.filterSet,
313                                 params.listener,
314                                 params.backupEligibilityRules);
315 
316                 synchronized (backupManagerService.getPendingRestores()) {
317                     if (backupManagerService.isRestoreInProgress()) {
318                         if (DEBUG) {
319                             Slog.d(TAG, "Restore in progress, queueing.");
320                         }
321                         backupManagerService.getPendingRestores().add(task);
322                         // This task will be picked up and executed when the the currently running
323                         // restore task finishes.
324                     } else {
325                         if (DEBUG) {
326                             Slog.d(TAG, "Starting restore.");
327                         }
328                         backupManagerService.setRestoreInProgress(true);
329                         Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
330                         sendMessage(restoreMsg);
331                     }
332                 }
333                 break;
334             }
335 
336             case MSG_RUN_ADB_RESTORE: {
337                 // TODO: refactor full restore to be a looper-based state machine
338                 // similar to normal backup/restore.
339                 AdbRestoreParams params = (AdbRestoreParams) msg.obj;
340                 PerformAdbRestoreTask task = new PerformAdbRestoreTask(backupManagerService,
341                         mOperationStorage, params.fd,
342                         params.curPassword, params.encryptPassword,
343                         params.observer, params.latch);
344                 (new Thread(task, "adb-restore")).start();
345                 break;
346             }
347 
348             case MSG_RUN_CLEAR: {
349                 ClearParams params = (ClearParams) msg.obj;
350                 Runnable task =
351                         new PerformClearTask(
352                                 backupManagerService,
353                                 params.mTransportConnection,
354                                 params.packageInfo,
355                                 params.listener);
356                 task.run();
357                 break;
358             }
359 
360             case MSG_RETRY_CLEAR: {
361                 // reenqueues if the transport remains unavailable
362                 ClearRetryParams params = (ClearRetryParams) msg.obj;
363                 backupManagerService.clearBackupData(params.transportName, params.packageName);
364                 break;
365             }
366 
367             case MSG_RUN_GET_RESTORE_SETS: {
368                 // Like other async operations, this is entered with the wakelock held
369                 RestoreSet[] sets = null;
370                 RestoreGetSetsParams params = (RestoreGetSetsParams) msg.obj;
371                 String callerLogString = "BH/MSG_RUN_GET_RESTORE_SETS";
372                 try {
373                     BackupTransportClient transport =
374                             params.mTransportConnection.connectOrThrow(callerLogString);
375                     sets = transport.getAvailableRestoreSets();
376                     // cache the result in the active session
377                     synchronized (params.session) {
378                         params.session.setRestoreSets(sets);
379                     }
380                     if (sets == null) {
381                         EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
382                     }
383                 } catch (Exception e) {
384                     Slog.e(TAG, "Error from transport getting set list: " + e.getMessage());
385                 } finally {
386                     if (params.observer != null) {
387                         try {
388                             params.observer.restoreSetsAvailable(sets);
389                         } catch (RemoteException re) {
390                             Slog.e(TAG, "Unable to report listing to observer");
391                         } catch (Exception e) {
392                             Slog.e(TAG, "Restore observer threw: " + e.getMessage());
393                         }
394                     }
395 
396                     // Done: reset the session timeout clock
397                     removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
398                     sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
399                             mAgentTimeoutParameters.getRestoreSessionTimeoutMillis());
400 
401                     params.listener.onFinished(callerLogString);
402                 }
403                 break;
404             }
405 
406             case MSG_BACKUP_OPERATION_TIMEOUT:
407             case MSG_RESTORE_OPERATION_TIMEOUT: {
408                 Slog.d(TAG, "Timeout message received for token=" + Integer.toHexString(msg.arg1));
409                 backupManagerService.handleCancel(msg.arg1, false);
410                 break;
411             }
412 
413             case MSG_RESTORE_SESSION_TIMEOUT: {
414                 synchronized (backupManagerService) {
415                     if (backupManagerService.getActiveRestoreSession() != null) {
416                         // Client app left the restore session dangling.  We know that it
417                         // can't be in the middle of an actual restore operation because
418                         // the timeout is suspended while a restore is in progress.  Clean
419                         // up now.
420                         Slog.w(TAG, "Restore session timed out; aborting");
421                         backupManagerService.getActiveRestoreSession().markTimedOut();
422                         post(backupManagerService.getActiveRestoreSession().new EndRestoreRunnable(
423                                 backupManagerService,
424                                 backupManagerService.getActiveRestoreSession()));
425                     }
426                 }
427                 break;
428             }
429 
430             case MSG_FULL_CONFIRMATION_TIMEOUT: {
431                 synchronized (backupManagerService.getAdbBackupRestoreConfirmations()) {
432                     AdbParams params = backupManagerService.getAdbBackupRestoreConfirmations().get(
433                             msg.arg1);
434                     if (params != null) {
435                         Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
436 
437                         // Release the waiter; timeout == completion
438                         backupManagerService.signalAdbBackupRestoreCompletion(params);
439 
440                         // Remove the token from the set
441                         backupManagerService.getAdbBackupRestoreConfirmations().delete(msg.arg1);
442 
443                         // Report a timeout to the observer, if any
444                         if (params.observer != null) {
445                             try {
446                                 params.observer.onTimeout();
447                             } catch (RemoteException e) {
448                                 /* don't care if the app has gone away */
449                             }
450                         }
451                     } else {
452                         Slog.d(TAG, "couldn't find params for token " + msg.arg1);
453                     }
454                 }
455                 break;
456             }
457 
458             case MSG_REQUEST_BACKUP: {
459                 BackupParams params = (BackupParams) msg.obj;
460                 if (MORE_DEBUG) {
461                     Slog.d(TAG, "MSG_REQUEST_BACKUP observer=" + params.observer);
462                 }
463                 backupManagerService.setBackupRunning(true);
464                 backupManagerService.getWakelock().acquire();
465 
466                 KeyValueBackupTask.start(
467                         backupManagerService,
468                         mOperationStorage,
469                         params.mTransportConnection,
470                         params.dirName,
471                         params.kvPackages,
472                         /* dataChangedJournal */ null,
473                         params.observer,
474                         params.monitor,
475                         params.listener,
476                         params.fullPackages,
477                         /* userInitiated */ true,
478                         params.nonIncrementalBackup,
479                         params.mBackupEligibilityRules);
480                 break;
481             }
482 
483             case MSG_SCHEDULE_BACKUP_PACKAGE: {
484                 String pkgName = (String) msg.obj;
485                 if (MORE_DEBUG) {
486                     Slog.d(TAG, "MSG_SCHEDULE_BACKUP_PACKAGE " + pkgName);
487                 }
488                 backupManagerService.dataChangedImpl(pkgName);
489                 break;
490             }
491         }
492     }
493 }
494