• 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.fullbackup;
18 
19 import static android.app.backup.BackupAnnotations.OperationType.BACKUP;
20 
21 import static com.android.server.backup.BackupManagerService.DEBUG;
22 
23 import android.annotation.Nullable;
24 import android.app.IBackupAgent;
25 import android.app.backup.BackupManager;
26 import android.app.backup.BackupManagerMonitor;
27 import android.app.backup.BackupProgress;
28 import android.app.backup.BackupTransport;
29 import android.app.backup.IBackupManagerMonitor;
30 import android.app.backup.IBackupObserver;
31 import android.app.backup.IFullBackupRestoreObserver;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.os.ParcelFileDescriptor;
36 import android.os.RemoteException;
37 import android.util.ArraySet;
38 import android.util.EventLog;
39 import android.util.Log;
40 import android.util.Slog;
41 
42 import com.android.server.EventLogTags;
43 import com.android.server.backup.BackupAgentTimeoutParameters;
44 import com.android.server.backup.BackupRestoreTask;
45 import com.android.server.backup.Flags;
46 import com.android.server.backup.FullBackupJob;
47 import com.android.server.backup.OperationStorage;
48 import com.android.server.backup.OperationStorage.OpState;
49 import com.android.server.backup.OperationStorage.OpType;
50 import com.android.server.backup.TransportManager;
51 import com.android.server.backup.UserBackupManagerService;
52 import com.android.server.backup.internal.OnTaskFinishedListener;
53 import com.android.server.backup.remote.RemoteCall;
54 import com.android.server.backup.transport.BackupTransportClient;
55 import com.android.server.backup.transport.TransportConnection;
56 import com.android.server.backup.transport.TransportNotAvailableException;
57 import com.android.server.backup.utils.BackupEligibilityRules;
58 import com.android.server.backup.utils.BackupManagerMonitorEventSender;
59 import com.android.server.backup.utils.BackupObserverUtils;
60 
61 import com.google.android.collect.Sets;
62 
63 import java.io.FileInputStream;
64 import java.io.FileOutputStream;
65 import java.io.IOException;
66 import java.util.ArrayList;
67 import java.util.List;
68 import java.util.Objects;
69 import java.util.Set;
70 import java.util.concurrent.CountDownLatch;
71 import java.util.concurrent.TimeUnit;
72 import java.util.concurrent.atomic.AtomicLong;
73 
74 /**
75  * Full backup task extension used for transport-oriented operation.
76  *
77  * Flow:
78  * For each requested package:
79  *     - Spin off a new SinglePackageBackupRunner (mBackupRunner) for the current package.
80  *     - Wait until preflight is complete. (mBackupRunner.getPreflightResultBlocking())
81  *     - If preflight data size is within limit, start reading data from agent pipe and writing
82  *       to transport pipe. While there is data to send, call transport.sendBackupData(int) to
83  *       tell the transport how many bytes to expect on its pipe.
84  *     - After sending all data, call transport.finishBackup() if things went well. And
85  *       transport.cancelFullBackup() otherwise.
86  *
87  * Interactions with mCurrentOperations:
88  *     - An entry for this object is added to mCurrentOperations for the entire lifetime of this
89  *       object. Used to cancel the operation.
90  *     - SinglePackageBackupRunner and SinglePackageBackupPreflight will put ephemeral entries
91  *       to get timeouts or operation complete callbacks.
92  *
93  * Handling cancels:
94  *     - The contract we provide is that the task won't interact with the transport after
95  *       handleCancel() is done executing.
96  *     - This task blocks at 3 points: 1. Preflight result check 2. Reading on agent side pipe
97  *       and 3. Get backup result from mBackupRunner.
98  *     - Bubbling up handleCancel to mBackupRunner handles all 3: 1. Calls handleCancel on the
99  *       preflight operation which counts down on the preflight latch. 2. Tears down the agent,
100  *       so read() returns -1. 3. Notifies mCurrentOpLock which unblocks
101  *       mBackupRunner.getBackupResultBlocking().
102  */
103 public class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
104     /**
105      * @throws IllegalStateException if there's no transport available.
106      */
newWithCurrentTransport( UserBackupManagerService backupManagerService, OperationStorage operationStorage, IFullBackupRestoreObserver observer, String[] whichPackages, boolean updateSchedule, FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver, IBackupManagerMonitor monitor, boolean userInitiated, String caller, BackupEligibilityRules backupEligibilityRules)107     public static PerformFullTransportBackupTask newWithCurrentTransport(
108             UserBackupManagerService backupManagerService,
109             OperationStorage operationStorage,
110             IFullBackupRestoreObserver observer,
111             String[] whichPackages,
112             boolean updateSchedule,
113             FullBackupJob runningJob,
114             CountDownLatch latch,
115             IBackupObserver backupObserver,
116             IBackupManagerMonitor monitor,
117             boolean userInitiated,
118             String caller,
119             BackupEligibilityRules backupEligibilityRules) {
120         TransportManager transportManager = backupManagerService.getTransportManager();
121         TransportConnection transportConnection = transportManager.getCurrentTransportClient(
122                 caller);
123         if (transportConnection == null) {
124             throw new IllegalStateException("No TransportConnection available");
125         }
126         OnTaskFinishedListener listener =
127                 listenerCaller ->
128                         transportManager.disposeOfTransportClient(transportConnection,
129                                 listenerCaller);
130         return new PerformFullTransportBackupTask(
131                 backupManagerService,
132                 operationStorage,
133                 transportConnection,
134                 observer,
135                 whichPackages,
136                 updateSchedule,
137                 runningJob,
138                 latch,
139                 backupObserver,
140                 monitor,
141                 listener,
142                 userInitiated,
143                 backupEligibilityRules);
144     }
145 
146     private static final String TAG = "PFTBT";
147     private UserBackupManagerService mUserBackupManagerService;
148     private final Object mCancelLock = new Object();
149 
150     OperationStorage mOperationStorage;
151     List<PackageInfo> mPackages;
152     boolean mUpdateSchedule;
153     CountDownLatch mLatch;
154     FullBackupJob mJob;             // if a scheduled job needs to be finished afterwards
155     IBackupObserver mBackupObserver;
156     boolean mUserInitiated;
157     SinglePackageBackupRunner mBackupRunner;
158     private final int mBackupRunnerOpToken;
159     private final OnTaskFinishedListener mListener;
160     private final TransportConnection mTransportConnection;
161     private final int mUserId;
162 
163     // This is true when a backup operation for some package is in progress.
164     private volatile boolean mIsDoingBackup;
165     private volatile boolean mCancelled;
166     private final int mCurrentOpToken;
167     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
168     private final BackupEligibilityRules mBackupEligibilityRules;
169     private BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
170 
PerformFullTransportBackupTask(UserBackupManagerService backupManagerService, OperationStorage operationStorage, TransportConnection transportConnection, IFullBackupRestoreObserver observer, String[] whichPackages, boolean updateSchedule, FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver, @Nullable IBackupManagerMonitor monitor, @Nullable OnTaskFinishedListener listener, boolean userInitiated, BackupEligibilityRules backupEligibilityRules)171     public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
172             OperationStorage operationStorage,
173             TransportConnection transportConnection,
174             IFullBackupRestoreObserver observer,
175             String[] whichPackages, boolean updateSchedule,
176             FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver,
177             @Nullable IBackupManagerMonitor monitor, @Nullable OnTaskFinishedListener listener,
178             boolean userInitiated, BackupEligibilityRules backupEligibilityRules) {
179         super(observer);
180         mUserBackupManagerService = backupManagerService;
181         mOperationStorage = operationStorage;
182         mTransportConnection = transportConnection;
183         mUpdateSchedule = updateSchedule;
184         mLatch = latch;
185         mJob = runningJob;
186         mPackages = new ArrayList<>(whichPackages.length);
187         mBackupObserver = backupObserver;
188         mListener = (listener != null) ? listener : OnTaskFinishedListener.NOP;
189         mUserInitiated = userInitiated;
190         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
191         mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
192         mBackupManagerMonitorEventSender =
193                 new BackupManagerMonitorEventSender(monitor);
194         mAgentTimeoutParameters = Objects.requireNonNull(
195                 backupManagerService.getAgentTimeoutParameters(),
196                 "Timeout parameters cannot be null");
197         mUserId = backupManagerService.getUserId();
198         mBackupEligibilityRules = backupEligibilityRules;
199 
200         if (backupManagerService.isBackupOperationInProgress()) {
201             Slog.d(TAG, "Skipping full backup. A backup is already in progress.");
202             mCancelled = true;
203             return;
204         }
205 
206         for (String pkg : whichPackages) {
207             try {
208                 PackageManager pm = backupManagerService.getPackageManager();
209                 PackageInfo packageInfo = pm.getPackageInfoAsUser(pkg,
210                         PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
211                 if (!mBackupEligibilityRules.appIsEligibleForBackup(packageInfo.applicationInfo)) {
212                     // Cull any packages that have indicated that backups are not permitted,
213                     // that run as system-domain uids but do not define their own backup agents,
214                     // as well as any explicit mention of the 'special' shared-storage agent
215                     // package (we handle that one at the end).
216                     if (DEBUG) {
217                         Slog.d(TAG, "Ignoring ineligible package " + pkg);
218                     }
219                     mBackupManagerMonitorEventSender.monitorEvent(
220                             BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE,
221                             packageInfo,
222                             BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
223                             /* extras= */ null);
224                     BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, pkg,
225                             BackupManager.ERROR_BACKUP_NOT_ALLOWED);
226                     continue;
227                 } else if (!mBackupEligibilityRules.appGetsFullBackup(packageInfo)) {
228                     // Cull any packages that are found in the queue but now aren't supposed
229                     // to get full-data backup operations.
230                     if (DEBUG) {
231                         Slog.d(TAG, "Ignoring full-data backup of key/value participant "
232                                 + pkg);
233                     }
234                     mBackupManagerMonitorEventSender.monitorEvent(
235                             BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT,
236                             packageInfo,
237                             BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
238                             /* extras= */ null);
239                     BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, pkg,
240                             BackupManager.ERROR_BACKUP_NOT_ALLOWED);
241                     continue;
242                 } else if (mBackupEligibilityRules.appIsStopped(packageInfo.applicationInfo)) {
243                     // Cull any packages in the 'stopped' state: they've either just been
244                     // installed or have explicitly been force-stopped by the user.  In both
245                     // cases we do not want to launch them for backup.
246                     if (DEBUG) {
247                         Slog.d(TAG, "Ignoring stopped package " + pkg);
248                     }
249                     mBackupManagerMonitorEventSender.monitorEvent(
250                             BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED,
251                             packageInfo,
252                             BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
253                             /* extras= */ null);
254                     BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, pkg,
255                             BackupManager.ERROR_BACKUP_NOT_ALLOWED);
256                     continue;
257                 }
258                 mPackages.add(packageInfo);
259             } catch (NameNotFoundException e) {
260                 Slog.i(TAG, "Requested package " + pkg + " not found; ignoring");
261                 mBackupManagerMonitorEventSender.monitorEvent(
262                         BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND,
263                         /* pkg= */ null,
264                         BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
265                         /* extras= */ null);
266             }
267         }
268 
269         Set<String> packageNames = Sets.newHashSet();
270         for (PackageInfo pkgInfo : mPackages) {
271             packageNames.add(pkgInfo.packageName);
272         }
273 
274         Slog.d(TAG, "backupmanager pftbt token=" + Integer.toHexString(mCurrentOpToken));
275         mOperationStorage.registerOperationForPackages(mCurrentOpToken, OpState.PENDING,
276                 packageNames, this, OpType.BACKUP);
277     }
278 
279     // public, because called from KeyValueBackupTask.finishTask.
unregisterTask()280     public void unregisterTask() {
281         mOperationStorage.removeOperation(mCurrentOpToken);
282     }
283 
284     @Override
execute()285     public void execute() {
286         // Nothing to do.
287     }
288 
289     @Override
handleCancel(@ancellationReason int cancellationReason)290     public void handleCancel(@CancellationReason int cancellationReason) {
291         synchronized (mCancelLock) {
292             // This callback is only used for cancelling the entire backup operation. Cancelling of
293             // a single package due to timeout is handled by SinglePackageBackupRunner and
294             // SinglePackageBackupPreflight.
295             if (cancellationReason == CancellationReason.TIMEOUT) {
296                 Slog.wtf(TAG, "This task cannot time out");
297                 return;
298             }
299 
300             // We don't cancel the entire operation if a single agent is disconnected unexpectedly.
301             // SinglePackageBackupRunner and SinglePackageBackupPreflight will receive the same
302             // callback and fail gracefully. The operation should then continue to the next package.
303             if (cancellationReason == CancellationReason.AGENT_DISCONNECTED) {
304                 return;
305             }
306 
307             if (mCancelled) {
308                 Slog.d(TAG, "Ignoring duplicate cancel call.");
309                 return;
310             }
311 
312             mCancelled = true;
313             if (mIsDoingBackup) {
314                 mUserBackupManagerService.handleCancel(mBackupRunnerOpToken, cancellationReason);
315                 try {
316                     // If we're running a backup we should be connected to a transport
317                     BackupTransportClient transport =
318                             mTransportConnection.getConnectedTransport("PFTBT.handleCancel()");
319                     transport.cancelFullBackup();
320                 } catch (RemoteException | TransportNotAvailableException e) {
321                     Slog.w(TAG, "Error calling cancelFullBackup() on transport: " + e);
322                     // Can't do much.
323                 }
324             }
325         }
326     }
327 
328     @Override
operationComplete(long result)329     public void operationComplete(long result) {
330         // Nothing to do.
331     }
332 
333     @Override
run()334     public void run() {
335 
336         // data from the app, passed to us for bridging to the transport
337         ParcelFileDescriptor[] enginePipes = null;
338 
339         // Pipe through which we write data to the transport
340         ParcelFileDescriptor[] transportPipes = null;
341 
342         long backoff = 0;
343         int backupRunStatus = BackupManager.SUCCESS;
344 
345         try {
346             if (!mUserBackupManagerService.isEnabled()
347                     || !mUserBackupManagerService.isSetupComplete()) {
348                 // Backups are globally disabled, so don't proceed.
349                 Slog.i(TAG,
350                         "full backup requested but enabled=" + mUserBackupManagerService.isEnabled()
351                                 + " setupComplete=" + mUserBackupManagerService.isSetupComplete()
352                                 + "; ignoring");
353                 int monitoringEvent;
354                 if (mUserBackupManagerService.isSetupComplete()) {
355                     monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED;
356                 } else {
357                     monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
358                 }
359                 mBackupManagerMonitorEventSender.monitorEvent(
360                         monitoringEvent,
361                         /* pkg= */ null,
362                         BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
363                         /* extras= */ null);
364                 mUpdateSchedule = false;
365                 backupRunStatus = BackupManager.ERROR_BACKUP_NOT_ALLOWED;
366                 return;
367             }
368 
369             BackupTransportClient transport = mTransportConnection.connect("PFTBT.run()");
370             if (transport == null) {
371                 Slog.w(TAG, "Transport not present; full data backup not performed");
372                 backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
373                 mBackupManagerMonitorEventSender.monitorEvent(
374                         BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT,
375                         /* pkg= */ null,
376                         BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
377                         /* extras= */ null);
378                 return;
379             }
380 
381             // In some cases there may not be a monitor passed in when creating this task. So, if we
382             // don't have one already we ask the transport for a monitor.
383             if (mBackupManagerMonitorEventSender.getMonitor() == null) {
384                 try {
385                     mBackupManagerMonitorEventSender
386                             .setMonitor(transport.getBackupManagerMonitor());
387                 } catch (RemoteException e) {
388                     Slog.i(TAG, "Failed to retrieve monitor from transport");
389                 }
390             }
391 
392             // We ask the transport which packages should not be put in restricted mode and cache
393             // the result in UBMS to be used later when the apps are started for backup.
394             setNoRestrictedModePackages(transport, mPackages);
395 
396             // Set up to send data to the transport
397             final int N = mPackages.size();
398             int chunkSizeInBytes = 8 * 1024; // 8KB
399             if (Flags.enableMaxSizeWritesToPipes()) {
400                 // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
401                 chunkSizeInBytes = 64 * 1024; // 64KB
402             }
403             final byte[] buffer = new byte[chunkSizeInBytes];
404             for (int i = 0; i < N; i++) {
405                 mBackupRunner = null;
406                 PackageInfo currentPackage = mPackages.get(i);
407                 String packageName = currentPackage.packageName;
408                 Slog.i(TAG, "Initiating full-data transport backup of " + packageName + " token: "
409                         + mCurrentOpToken);
410                 EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, packageName);
411 
412                 transportPipes = ParcelFileDescriptor.createPipe();
413 
414                 // Tell the transport the data's coming
415                 int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
416                 int backupPackageStatus;
417                 long quota = Long.MAX_VALUE;
418                 synchronized (mCancelLock) {
419                     if (mCancelled) {
420                         break;
421                     }
422                     backupPackageStatus = transport.performFullBackup(currentPackage,
423                             transportPipes[0], flags);
424 
425                     if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
426                         quota = transport.getBackupQuota(currentPackage.packageName,
427                                 true /* isFullBackup */);
428                         // Now set up the backup engine / data source end of things
429                         enginePipes = ParcelFileDescriptor.createPipe();
430                         mBackupRunner =
431                                 new SinglePackageBackupRunner(enginePipes[1], currentPackage,
432                                         mTransportConnection, quota, mBackupRunnerOpToken,
433                                         transport.getTransportFlags());
434                         // The runner dup'd the pipe half, so we close it here
435                         enginePipes[1].close();
436                         enginePipes[1] = null;
437 
438                         mIsDoingBackup = true;
439                     }
440                 }
441                 if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
442 
443                     // The transport has its own copy of the read end of the pipe,
444                     // so close ours now
445                     transportPipes[0].close();
446                     transportPipes[0] = null;
447 
448                     // Spin off the runner to fetch the app's data and pipe it
449                     // into the engine pipes
450                     (new Thread(mBackupRunner, "package-backup-bridge")).start();
451 
452                     // Read data off the engine pipe and pass it to the transport
453                     // pipe until we hit EOD on the input stream.  We do not take
454                     // close() responsibility for these FDs into these stream wrappers.
455                     FileInputStream in = new FileInputStream(
456                             enginePipes[0].getFileDescriptor());
457                     FileOutputStream out = new FileOutputStream(
458                             transportPipes[1].getFileDescriptor());
459                     long totalRead = 0;
460                     final long preflightResult = mBackupRunner.getPreflightResultBlocking();
461                     // Preflight result is negative if some error happened on preflight.
462                     if (preflightResult < 0) {
463                         if (DEBUG) {
464                             Slog.d(TAG, "Backup error after preflight of package "
465                                     + packageName + ": " + preflightResult
466                                     + ", not running backup.");
467                         }
468                         mBackupManagerMonitorEventSender.monitorEvent(
469                                 BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT,
470                                 currentPackage,
471                                 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
472                                 BackupManagerMonitorEventSender.putMonitoringExtra(
473                                         /* extras= */ null,
474                                         BackupManagerMonitor.EXTRA_LOG_PREFLIGHT_ERROR,
475                                         preflightResult));
476                         backupPackageStatus = (int) preflightResult;
477                     } else {
478                         int nRead = 0;
479                         do {
480                             nRead = in.read(buffer);
481                             if (DEBUG) {
482                                 Slog.v(TAG, "in.read(buffer) from app: " + nRead);
483                             }
484                             if (nRead > 0) {
485                                 out.write(buffer, 0, nRead);
486                                 synchronized (mCancelLock) {
487                                     if (!mCancelled) {
488                                         backupPackageStatus = transport.sendBackupData(nRead);
489                                     }
490                                 }
491                                 totalRead += nRead;
492                                 if (mBackupObserver != null && preflightResult > 0) {
493                                     BackupObserverUtils
494                                             .sendBackupOnUpdate(mBackupObserver, packageName,
495                                                     new BackupProgress(preflightResult, totalRead));
496                                 }
497                             }
498                         } while (nRead > 0
499                                 && backupPackageStatus == BackupTransport.TRANSPORT_OK);
500                         // Despite preflight succeeded, package still can hit quota on flight.
501                         if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
502                             Slog.w(TAG, "Package hit quota limit in-flight " + packageName
503                                     + ": " + totalRead + " of " + quota);
504                             mBackupManagerMonitorEventSender.monitorEvent(
505                                     BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT,
506                                     currentPackage,
507                                     BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
508                                     /* extras= */ null);
509                             mBackupRunner.sendQuotaExceeded(totalRead, quota);
510                         }
511                     }
512 
513                     final int backupRunnerResult = mBackupRunner.getBackupResultBlocking();
514 
515                     synchronized (mCancelLock) {
516                         mIsDoingBackup = false;
517                         // If mCancelCurrent is true, we have already called cancelFullBackup().
518                         if (!mCancelled) {
519                             if (backupRunnerResult == BackupTransport.TRANSPORT_OK) {
520                                 // If we were otherwise in a good state, now interpret the final
521                                 // result based on what finishBackup() returns.  If we're in a
522                                 // failure case already, preserve that result and ignore whatever
523                                 // finishBackup() reports.
524                                 final int finishResult = transport.finishBackup();
525                                 if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
526                                     backupPackageStatus = finishResult;
527                                 }
528                             } else {
529                                 transport.cancelFullBackup();
530                             }
531                         }
532                     }
533 
534                     // A transport-originated error here means that we've hit an error that the
535                     // runner doesn't know about, so it's still moving data but we're pulling the
536                     // rug out from under it.  Don't ask for its result:  we already know better
537                     // and we'll hang if we block waiting for it, since it relies on us to
538                     // read back the data it's writing into the engine.  Just proceed with
539                     // a graceful failure.  The runner/engine mechanism will tear itself
540                     // down cleanly when we close the pipes from this end.  Transport-level
541                     // errors take precedence over agent/app-specific errors for purposes of
542                     // determining our course of action.
543                     if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
544                         // We still could fail in backup runner thread.
545                         if (backupRunnerResult != BackupTransport.TRANSPORT_OK) {
546                             // If there was an error in runner thread and
547                             // not TRANSPORT_ERROR here, overwrite it.
548                             backupPackageStatus = backupRunnerResult;
549                         }
550                     } else {
551                         if (DEBUG) {
552                             Slog.i(TAG, "Transport-level failure; cancelling agent work");
553                         }
554                     }
555 
556                     if (DEBUG) {
557                         Slog.i(TAG, "Done delivering backup data: result="
558                                 + backupPackageStatus);
559                     }
560 
561                     if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
562                         Slog.w(TAG, "Error " + backupPackageStatus + " backing up "
563                                 + packageName);
564                     }
565 
566                     // Also ask the transport how long it wants us to wait before
567                     // moving on to the next package, if any.
568                     backoff = transport.requestFullBackupTime();
569                     Slog.i(TAG, "Transport suggested backoff=" + backoff);
570                 }
571 
572                 // Roll this package to the end of the backup queue if we're
573                 // in a queue-driven mode (regardless of success/failure)
574                 if (mUpdateSchedule) {
575                     mUserBackupManagerService.enqueueFullBackup(
576                             packageName, System.currentTimeMillis());
577                 }
578 
579                 if (backupPackageStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
580                     BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, packageName,
581                             BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
582                     Slog.i(TAG, "Transport rejected backup of " + packageName + ", skipping");
583                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName,
584                             "transport rejected");
585                     // This failure state can come either a-priori from the transport, or
586                     // from the preflight pass.  If we got as far as preflight, we now need
587                     // to tear down the target process.
588                     if (mBackupRunner != null) {
589                         mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
590                                 currentPackage.applicationInfo, /* allowKill= */ true);
591                     }
592                     // ... and continue looping.
593                 } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
594                     BackupObserverUtils
595                             .sendBackupOnPackageResult(mBackupObserver, packageName,
596                                     BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
597                     Slog.i(TAG, "Transport quota exceeded for package: " + packageName);
598                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED, packageName);
599                     mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
600                             currentPackage.applicationInfo, /* allowKill= */ true);
601                     // Do nothing, clean up, and continue looping.
602                 } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
603                     BackupObserverUtils
604                             .sendBackupOnPackageResult(mBackupObserver, packageName,
605                                     BackupManager.ERROR_AGENT_FAILURE);
606                     Slog.w(TAG, "Application failure for package: " + packageName);
607                     EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
608                     mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
609                             currentPackage.applicationInfo, /* allowKill= */ true);
610                     // Do nothing, clean up, and continue looping.
611                 } else if (backupPackageStatus == BackupManager.ERROR_BACKUP_CANCELLED) {
612                     BackupObserverUtils
613                             .sendBackupOnPackageResult(mBackupObserver, packageName,
614                                     BackupManager.ERROR_BACKUP_CANCELLED);
615                     Slog.w(TAG, "Backup cancelled. package=" + packageName +
616                             ", entire session cancelled=" + mCancelled);
617                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName);
618                     mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
619                             currentPackage.applicationInfo, /* allowKill= */ true);
620                     // Do nothing, clean up, and continue looping.
621                 } else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
622                     BackupObserverUtils
623                             .sendBackupOnPackageResult(mBackupObserver, packageName,
624                                     BackupManager.ERROR_TRANSPORT_ABORTED);
625                     Slog.w(TAG, "Transport failed; aborting backup: " + backupPackageStatus);
626                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
627                     // Abort entire backup pass.
628                     backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
629                     mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
630                             currentPackage.applicationInfo, /* allowKill= */ true);
631                     return;
632                 } else {
633                     // Success!
634                     BackupObserverUtils
635                             .sendBackupOnPackageResult(mBackupObserver, packageName,
636                                     BackupManager.SUCCESS);
637                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, packageName);
638                     mUserBackupManagerService.logBackupComplete(packageName);
639                 }
640                 cleanUpPipes(transportPipes);
641                 cleanUpPipes(enginePipes);
642                 if (currentPackage.applicationInfo != null) {
643                     Slog.i(TAG, "Unbinding agent in " + packageName);
644                     try {
645                         mUserBackupManagerService.getActivityManager().unbindBackupAgent(
646                                 currentPackage.applicationInfo);
647                     } catch (RemoteException e) { /* can't happen; activity manager is local */ }
648                 }
649             }
650         } catch (Exception e) {
651             backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
652             Slog.w(TAG, "Exception trying full transport backup", e);
653             mBackupManagerMonitorEventSender.monitorEvent(
654                     BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP,
655                     /* pkg= */ null,
656                     BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
657                     BackupManagerMonitorEventSender.putMonitoringExtra(/* extras= */ null,
658                             BackupManagerMonitor.EXTRA_LOG_EXCEPTION_FULL_BACKUP,
659                             Log.getStackTraceString(e)));
660 
661         } finally {
662 
663             if (mCancelled) {
664                 backupRunStatus = BackupManager.ERROR_BACKUP_CANCELLED;
665             }
666 
667             Slog.i(TAG, "Full backup completed with status: " + backupRunStatus);
668             BackupObserverUtils.sendBackupFinished(mBackupObserver, backupRunStatus);
669 
670             cleanUpPipes(transportPipes);
671             cleanUpPipes(enginePipes);
672 
673             unregisterTask();
674 
675             if (mJob != null) {
676                 mJob.finishBackupPass(mUserId);
677             }
678 
679             synchronized (mUserBackupManagerService.getQueueLock()) {
680                 mUserBackupManagerService.setRunningFullBackupTask(null);
681             }
682 
683             mListener.onFinished("PFTBT.run()");
684 
685             mLatch.countDown();
686 
687             // Now that we're actually done with schedule-driven work, reschedule
688             // the next pass based on the new queue state.
689             if (mUpdateSchedule) {
690                 mUserBackupManagerService.scheduleNextFullBackupJob(backoff);
691             }
692 
693             // Clear this to avoid using the memory until reboot.
694             mUserBackupManagerService
695                     .getBackupAgentConnectionManager().clearNoRestrictedModePackages();
696 
697             Slog.i(TAG, "Full data backup pass finished.");
698             mUserBackupManagerService.getWakeLock().release();
699         }
700     }
701 
cleanUpPipes(ParcelFileDescriptor[] pipes)702     void cleanUpPipes(ParcelFileDescriptor[] pipes) {
703         if (pipes != null) {
704             if (pipes[0] != null) {
705                 ParcelFileDescriptor fd = pipes[0];
706                 pipes[0] = null;
707                 try {
708                     fd.close();
709                 } catch (IOException e) {
710                     Slog.w(TAG, "Unable to close pipe!");
711                 }
712             }
713             if (pipes[1] != null) {
714                 ParcelFileDescriptor fd = pipes[1];
715                 pipes[1] = null;
716                 try {
717                     fd.close();
718                 } catch (IOException e) {
719                     Slog.w(TAG, "Unable to close pipe!");
720                 }
721             }
722         }
723     }
724 
setNoRestrictedModePackages(BackupTransportClient transport, List<PackageInfo> packages)725     private void setNoRestrictedModePackages(BackupTransportClient transport,
726             List<PackageInfo> packages) {
727         try {
728             Set<String> packageNames = new ArraySet<>();
729             for (int i = 0; i < packages.size(); i++) {
730                 packageNames.add(packages.get(i).packageName);
731             }
732             packageNames = transport.getPackagesThatShouldNotUseRestrictedMode(packageNames,
733                     BACKUP);
734             mUserBackupManagerService.getBackupAgentConnectionManager().setNoRestrictedModePackages(
735                     packageNames, BACKUP);
736         } catch (RemoteException e) {
737             Slog.i(TAG, "Failed to retrieve no restricted mode packages from transport");
738         }
739     }
740 
741     // Run the backup and pipe it back to the given socket -- expects to run on
742     // a standalone thread.  The  runner owns this half of the pipe, and closes
743     // it to indicate EOD to the other end.
744     class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
745         final AtomicLong mResult = new AtomicLong(BackupTransport.AGENT_ERROR);
746         final CountDownLatch mLatch = new CountDownLatch(1);
747         final TransportConnection mTransportConnection;
748         final long mQuota;
749         private final int mCurrentOpToken;
750         private final int mTransportFlags;
751 
SinglePackageBackupPreflight( TransportConnection transportConnection, long quota, int currentOpToken, int transportFlags)752         SinglePackageBackupPreflight(
753                 TransportConnection transportConnection,
754                 long quota,
755                 int currentOpToken,
756                 int transportFlags) {
757             mTransportConnection = transportConnection;
758             mQuota = quota;
759             mCurrentOpToken = currentOpToken;
760             mTransportFlags = transportFlags;
761         }
762 
763         @Override
preflightFullBackup(PackageInfo pkg, IBackupAgent agent)764         public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
765             int result;
766             long fullBackupAgentTimeoutMillis =
767                     mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
768             try {
769                 mUserBackupManagerService.prepareOperationTimeout(
770                         mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OpType.BACKUP_WAIT);
771                 if (DEBUG) {
772                     Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
773                 }
774                 agent.doMeasureFullBackup(mQuota, mCurrentOpToken,
775                         mUserBackupManagerService.getBackupManagerBinder(), mTransportFlags);
776 
777                 // Now wait to get our result back.  If this backstop timeout is reached without
778                 // the latch being thrown, flow will continue as though a result or "normal"
779                 // timeout had been produced.  In case of a real backstop timeout, mResult
780                 // will still contain the value it was constructed with, AGENT_ERROR, which
781                 // intentionaly falls into the "just report failure" code.
782                 mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
783 
784                 long totalSize = mResult.get();
785                 // If preflight timed out, mResult will contain error code as int.
786                 if (totalSize < 0) {
787                     return (int) totalSize;
788                 }
789                 if (DEBUG) {
790                     Slog.v(TAG, "Got preflight response; size=" + totalSize);
791                 }
792 
793                 BackupTransportClient transport =
794                         mTransportConnection.connectOrThrow("PFTBT$SPBP.preflightFullBackup()");
795                 result = transport.checkFullBackupSize(totalSize);
796                 if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
797                     if (DEBUG) {
798                         Slog.d(TAG, "Package hit quota limit on preflight " +
799                                 pkg.packageName + ": " + totalSize + " of " + mQuota);
800                     }
801                     RemoteCall.execute(
802                             callback -> agent.doQuotaExceeded(totalSize, mQuota, callback),
803                             mAgentTimeoutParameters.getQuotaExceededTimeoutMillis());
804                 }
805             } catch (Exception e) {
806                 Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
807                 result = BackupTransport.AGENT_ERROR;
808             }
809             return result;
810         }
811 
812         @Override
execute()813         public void execute() {
814             // Unused.
815         }
816 
817         @Override
operationComplete(long result)818         public void operationComplete(long result) {
819             // got the callback, and our preflightFullBackup() method is waiting for the result
820             if (DEBUG) {
821                 Slog.i(TAG, "Preflight op complete, result=" + result);
822             }
823             mResult.set(result);
824             mLatch.countDown();
825             mOperationStorage.removeOperation(mCurrentOpToken);
826         }
827 
828         @Override
handleCancel(@ancellationReason int cancellationReason)829         public void handleCancel(@CancellationReason int cancellationReason) {
830             if (DEBUG) {
831                 Slog.i(TAG, "Preflight cancelled; failing");
832             }
833             mResult.set(BackupTransport.AGENT_ERROR);
834             mLatch.countDown();
835             mOperationStorage.removeOperation(mCurrentOpToken);
836         }
837 
838         @Override
getExpectedSizeOrErrorCode()839         public long getExpectedSizeOrErrorCode() {
840             long fullBackupAgentTimeoutMillis =
841                     mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
842             try {
843                 mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
844                 return mResult.get();
845             } catch (InterruptedException e) {
846                 return BackupTransport.NO_MORE_DATA;
847             }
848         }
849     }
850 
851     class SinglePackageBackupRunner implements Runnable, BackupRestoreTask {
852         final ParcelFileDescriptor mOutput;
853         final PackageInfo mTarget;
854         final SinglePackageBackupPreflight mPreflight;
855         final CountDownLatch mPreflightLatch;
856         final CountDownLatch mBackupLatch;
857         private final int mCurrentOpToken;
858         private final int mEphemeralToken;
859         private FullBackupEngine mEngine;
860         private volatile int mPreflightResult;
861         private volatile int mBackupResult;
862         private final long mQuota;
863         private volatile boolean mIsCancelled;
864         private final int mTransportFlags;
865 
SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target, TransportConnection transportConnection, long quota, int currentOpToken, int transportFlags)866         SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
867                 TransportConnection transportConnection, long quota, int currentOpToken,
868                 int transportFlags) throws IOException {
869             mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
870             mTarget = target;
871             mCurrentOpToken = currentOpToken;
872             mEphemeralToken = mUserBackupManagerService.generateRandomIntegerToken();
873             mPreflight = new SinglePackageBackupPreflight(
874                     transportConnection, quota, mEphemeralToken, transportFlags);
875             mPreflightLatch = new CountDownLatch(1);
876             mBackupLatch = new CountDownLatch(1);
877             mPreflightResult = BackupTransport.AGENT_ERROR;
878             mBackupResult = BackupTransport.AGENT_ERROR;
879             mQuota = quota;
880             mTransportFlags = transportFlags;
881             registerTask(target.packageName);
882         }
883 
registerTask(String packageName)884         void registerTask(String packageName) {
885             Set<String> packages = Sets.newHashSet(packageName);
886             mOperationStorage.registerOperationForPackages(mCurrentOpToken, OpState.PENDING,
887                     packages, this, OpType.BACKUP_WAIT);
888         }
889 
unregisterTask()890         void unregisterTask() {
891             mOperationStorage.removeOperation(mCurrentOpToken);
892         }
893 
894         @Override
run()895         public void run() {
896             FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
897             mEngine =
898                     new FullBackupEngine(
899                             mUserBackupManagerService,
900                             out,
901                             mPreflight,
902                             mTarget,
903                             false,
904                             this,
905                             mQuota,
906                             mCurrentOpToken,
907                             mTransportFlags,
908                             mBackupEligibilityRules,
909                             mBackupManagerMonitorEventSender);
910             try {
911                 try {
912                     if (!mIsCancelled) {
913                         mPreflightResult = mEngine.preflightCheck();
914                     }
915                 } finally {
916                     mPreflightLatch.countDown();
917                 }
918                 // If there is no error on preflight, continue backup.
919                 if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
920                     if (!mIsCancelled) {
921                         mBackupResult = mEngine.backupOnePackage();
922                     }
923                 }
924             } catch (Exception e) {
925                 Slog.w(TAG, "Exception during full package backup of " + mTarget.packageName,
926                         e);
927             } finally {
928                 unregisterTask();
929                 mBackupLatch.countDown();
930                 try {
931                     mOutput.close();
932                 } catch (IOException e) {
933                     Slog.w(TAG, "Error closing transport pipe in runner");
934                 }
935             }
936         }
937 
sendQuotaExceeded(final long backupDataBytes, final long quotaBytes)938         public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
939             mEngine.sendQuotaExceeded(backupDataBytes, quotaBytes);
940         }
941 
942         // If preflight succeeded, returns positive number - preflight size,
943         // otherwise return negative error code.
getPreflightResultBlocking()944         long getPreflightResultBlocking() {
945             long fullBackupAgentTimeoutMillis =
946                     mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
947             try {
948                 mPreflightLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
949                 if (mIsCancelled) {
950                     return BackupManager.ERROR_BACKUP_CANCELLED;
951                 }
952                 if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
953                     return mPreflight.getExpectedSizeOrErrorCode();
954                 } else {
955                     return mPreflightResult;
956                 }
957             } catch (InterruptedException e) {
958                 return BackupTransport.AGENT_ERROR;
959             }
960         }
961 
getBackupResultBlocking()962         int getBackupResultBlocking() {
963             long fullBackupAgentTimeoutMillis =
964                     mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
965             try {
966                 mBackupLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
967                 if (mIsCancelled) {
968                     return BackupManager.ERROR_BACKUP_CANCELLED;
969                 }
970                 return mBackupResult;
971             } catch (InterruptedException e) {
972                 return BackupTransport.AGENT_ERROR;
973             }
974         }
975 
976         @Override
execute()977         public void execute() { /* intentionally empty */ }
978 
979         @Override
operationComplete(long result)980         public void operationComplete(long result) { /* intentionally empty */ }
981 
982         @Override
handleCancel(@ancellationReason int cancellationReason)983         public void handleCancel(@CancellationReason int cancellationReason) {
984             Slog.w(
985                     TAG,
986                     "Cancelled backup: " + mTarget.packageName + " reason:" + cancellationReason);
987 
988             mBackupManagerMonitorEventSender.monitorEvent(
989                     BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL,
990                     mTarget,
991                     BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
992                     BackupManagerMonitorEventSender.putMonitoringExtra(
993                             /* extras= */ null,
994                             BackupManagerMonitor.EXTRA_LOG_CANCELLATION_REASON,
995                             cancellationReason));
996             mIsCancelled = true;
997             // Cancel tasks spun off by this task.
998             mUserBackupManagerService.handleCancel(mEphemeralToken, cancellationReason);
999             mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
1000                     mTarget.applicationInfo, /* allowKill= */ true);
1001             // Free up everyone waiting on this task and its children.
1002             mPreflightLatch.countDown();
1003             mBackupLatch.countDown();
1004             // We are done with this operation.
1005             mOperationStorage.removeOperation(mCurrentOpToken);
1006         }
1007     }
1008 }
1009