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