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