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