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 
BackupHandler(BackupManagerService backupManagerService, Looper looper)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 
handleMessage(Message msg)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