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