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