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.Flags;
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             int chunkSizeInBytes = 8 * 1024; // 8KB
394             if (Flags.enableMaxSizeWritesToPipes()) {
395                 // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
396                 chunkSizeInBytes = 64 * 1024; // 64KB
397             }
398             final byte[] buffer = new byte[chunkSizeInBytes];
399             for (int i = 0; i < N; i++) {
400                 mBackupRunner = null;
401                 PackageInfo currentPackage = mPackages.get(i);
402                 String packageName = currentPackage.packageName;
403                 if (DEBUG) {
404                     Slog.i(TAG, "Initiating full-data transport backup of " + packageName
405                             + " token: " + mCurrentOpToken);
406                 }
407                 EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, packageName);
408 
409                 transportPipes = ParcelFileDescriptor.createPipe();
410 
411                 // Tell the transport the data's coming
412                 int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
413                 int backupPackageStatus;
414                 long quota = Long.MAX_VALUE;
415                 synchronized (mCancelLock) {
416                     if (mCancelAll) {
417                         break;
418                     }
419                     backupPackageStatus = transport.performFullBackup(currentPackage,
420                             transportPipes[0], flags);
421 
422                     if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
423                         quota = transport.getBackupQuota(currentPackage.packageName,
424                                 true /* isFullBackup */);
425                         // Now set up the backup engine / data source end of things
426                         enginePipes = ParcelFileDescriptor.createPipe();
427                         mBackupRunner =
428                                 new SinglePackageBackupRunner(enginePipes[1], currentPackage,
429                                         mTransportConnection, quota, mBackupRunnerOpToken,
430                                         transport.getTransportFlags());
431                         // The runner dup'd the pipe half, so we close it here
432                         enginePipes[1].close();
433                         enginePipes[1] = null;
434 
435                         mIsDoingBackup = true;
436                     }
437                 }
438                 if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
439 
440                     // The transport has its own copy of the read end of the pipe,
441                     // so close ours now
442                     transportPipes[0].close();
443                     transportPipes[0] = null;
444 
445                     // Spin off the runner to fetch the app's data and pipe it
446                     // into the engine pipes
447                     (new Thread(mBackupRunner, "package-backup-bridge")).start();
448 
449                     // Read data off the engine pipe and pass it to the transport
450                     // pipe until we hit EOD on the input stream.  We do not take
451                     // close() responsibility for these FDs into these stream wrappers.
452                     FileInputStream in = new FileInputStream(
453                             enginePipes[0].getFileDescriptor());
454                     FileOutputStream out = new FileOutputStream(
455                             transportPipes[1].getFileDescriptor());
456                     long totalRead = 0;
457                     final long preflightResult = mBackupRunner.getPreflightResultBlocking();
458                     // Preflight result is negative if some error happened on preflight.
459                     if (preflightResult < 0) {
460                         if (MORE_DEBUG) {
461                             Slog.d(TAG, "Backup error after preflight of package "
462                                     + packageName + ": " + preflightResult
463                                     + ", not running backup.");
464                         }
465                         mBackupManagerMonitorEventSender.monitorEvent(
466                                 BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT,
467                                 mCurrentPackage,
468                                 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
469                                 mBackupManagerMonitorEventSender.putMonitoringExtra(null,
470                                         BackupManagerMonitor.EXTRA_LOG_PREFLIGHT_ERROR,
471                                         preflightResult));
472                         backupPackageStatus = (int) preflightResult;
473                     } else {
474                         int nRead = 0;
475                         do {
476                             nRead = in.read(buffer);
477                             if (MORE_DEBUG) {
478                                 Slog.v(TAG, "in.read(buffer) from app: " + nRead);
479                             }
480                             if (nRead > 0) {
481                                 out.write(buffer, 0, nRead);
482                                 synchronized (mCancelLock) {
483                                     if (!mCancelAll) {
484                                         backupPackageStatus = transport.sendBackupData(nRead);
485                                     }
486                                 }
487                                 totalRead += nRead;
488                                 if (mBackupObserver != null && preflightResult > 0) {
489                                     BackupObserverUtils
490                                             .sendBackupOnUpdate(mBackupObserver, packageName,
491                                                     new BackupProgress(preflightResult, totalRead));
492                                 }
493                             }
494                         } while (nRead > 0
495                                 && backupPackageStatus == BackupTransport.TRANSPORT_OK);
496                         // Despite preflight succeeded, package still can hit quota on flight.
497                         if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
498                             Slog.w(TAG, "Package hit quota limit in-flight " + packageName
499                                     + ": " + totalRead + " of " + quota);
500                             mBackupManagerMonitorEventSender.monitorEvent(
501                                     BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT,
502                                     mCurrentPackage,
503                                     BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
504                                     null);
505                             mBackupRunner.sendQuotaExceeded(totalRead, quota);
506                         }
507                     }
508 
509                     final int backupRunnerResult = mBackupRunner.getBackupResultBlocking();
510 
511                     synchronized (mCancelLock) {
512                         mIsDoingBackup = false;
513                         // If mCancelCurrent is true, we have already called cancelFullBackup().
514                         if (!mCancelAll) {
515                             if (backupRunnerResult == BackupTransport.TRANSPORT_OK) {
516                                 // If we were otherwise in a good state, now interpret the final
517                                 // result based on what finishBackup() returns.  If we're in a
518                                 // failure case already, preserve that result and ignore whatever
519                                 // finishBackup() reports.
520                                 final int finishResult = transport.finishBackup();
521                                 if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
522                                     backupPackageStatus = finishResult;
523                                 }
524                             } else {
525                                 transport.cancelFullBackup();
526                             }
527                         }
528                     }
529 
530                     // A transport-originated error here means that we've hit an error that the
531                     // runner doesn't know about, so it's still moving data but we're pulling the
532                     // rug out from under it.  Don't ask for its result:  we already know better
533                     // and we'll hang if we block waiting for it, since it relies on us to
534                     // read back the data it's writing into the engine.  Just proceed with
535                     // a graceful failure.  The runner/engine mechanism will tear itself
536                     // down cleanly when we close the pipes from this end.  Transport-level
537                     // errors take precedence over agent/app-specific errors for purposes of
538                     // determining our course of action.
539                     if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
540                         // We still could fail in backup runner thread.
541                         if (backupRunnerResult != BackupTransport.TRANSPORT_OK) {
542                             // If there was an error in runner thread and
543                             // not TRANSPORT_ERROR here, overwrite it.
544                             backupPackageStatus = backupRunnerResult;
545                         }
546                     } else {
547                         if (MORE_DEBUG) {
548                             Slog.i(TAG, "Transport-level failure; cancelling agent work");
549                         }
550                     }
551 
552                     if (MORE_DEBUG) {
553                         Slog.i(TAG, "Done delivering backup data: result="
554                                 + backupPackageStatus);
555                     }
556 
557                     if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
558                         Slog.w(TAG, "Error " + backupPackageStatus + " backing up "
559                                 + packageName);
560                     }
561 
562                     // Also ask the transport how long it wants us to wait before
563                     // moving on to the next package, if any.
564                     backoff = transport.requestFullBackupTime();
565                     if (DEBUG_SCHEDULING) {
566                         Slog.i(TAG, "Transport suggested backoff=" + backoff);
567                     }
568 
569                 }
570 
571                 // Roll this package to the end of the backup queue if we're
572                 // in a queue-driven mode (regardless of success/failure)
573                 if (mUpdateSchedule) {
574                     mUserBackupManagerService.enqueueFullBackup(
575                             packageName, System.currentTimeMillis());
576                 }
577 
578                 if (backupPackageStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
579                     BackupObserverUtils
580                             .sendBackupOnPackageResult(mBackupObserver, packageName,
581                                     BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
582                     if (DEBUG) {
583                         Slog.i(TAG, "Transport rejected backup of " + packageName
584                                 + ", skipping");
585                     }
586                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName,
587                             "transport rejected");
588                     // This failure state can come either a-priori from the transport, or
589                     // from the preflight pass.  If we got as far as preflight, we now need
590                     // to tear down the target process.
591                     if (mBackupRunner != null) {
592                         mUserBackupManagerService.tearDownAgentAndKill(
593                                 currentPackage.applicationInfo);
594                     }
595                     // ... and continue looping.
596                 } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
597                     BackupObserverUtils
598                             .sendBackupOnPackageResult(mBackupObserver, packageName,
599                                     BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
600                     if (DEBUG) {
601                         Slog.i(TAG, "Transport quota exceeded for package: " + packageName);
602                         EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
603                                 packageName);
604                     }
605                     mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
606                     // Do nothing, clean up, and continue looping.
607                 } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
608                     BackupObserverUtils
609                             .sendBackupOnPackageResult(mBackupObserver, packageName,
610                                     BackupManager.ERROR_AGENT_FAILURE);
611                     Slog.w(TAG, "Application failure for package: " + packageName);
612                     EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
613                     mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
614                     // Do nothing, clean up, and continue looping.
615                 } else if (backupPackageStatus == BackupManager.ERROR_BACKUP_CANCELLED) {
616                     BackupObserverUtils
617                             .sendBackupOnPackageResult(mBackupObserver, packageName,
618                                     BackupManager.ERROR_BACKUP_CANCELLED);
619                     Slog.w(TAG, "Backup cancelled. package=" + packageName +
620                             ", cancelAll=" + mCancelAll);
621                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName);
622                     mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
623                     // Do nothing, clean up, and continue looping.
624                 } else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
625                     BackupObserverUtils
626                             .sendBackupOnPackageResult(mBackupObserver, packageName,
627                                     BackupManager.ERROR_TRANSPORT_ABORTED);
628                     Slog.w(TAG, "Transport failed; aborting backup: " + backupPackageStatus);
629                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
630                     // Abort entire backup pass.
631                     backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
632                     mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
633                     return;
634                 } else {
635                     // Success!
636                     BackupObserverUtils
637                             .sendBackupOnPackageResult(mBackupObserver, packageName,
638                                     BackupManager.SUCCESS);
639                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, packageName);
640                     mUserBackupManagerService.logBackupComplete(packageName);
641                 }
642                 cleanUpPipes(transportPipes);
643                 cleanUpPipes(enginePipes);
644                 if (currentPackage.applicationInfo != null) {
645                     Slog.i(TAG, "Unbinding agent in " + packageName);
646                     try {
647                         mUserBackupManagerService.getActivityManager().unbindBackupAgent(
648                                 currentPackage.applicationInfo);
649                     } catch (RemoteException e) { /* can't happen; activity manager is local */ }
650                 }
651             }
652         } catch (Exception e) {
653             backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
654             Slog.w(TAG, "Exception trying full transport backup", e);
655             mBackupManagerMonitorEventSender.monitorEvent(
656                     BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP,
657                     mCurrentPackage,
658                     BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
659                     mBackupManagerMonitorEventSender.putMonitoringExtra(null,
660                             BackupManagerMonitor.EXTRA_LOG_EXCEPTION_FULL_BACKUP,
661                             Log.getStackTraceString(e)));
662 
663         } finally {
664 
665             if (mCancelAll) {
666                 backupRunStatus = BackupManager.ERROR_BACKUP_CANCELLED;
667             }
668 
669             if (DEBUG) {
670                 Slog.i(TAG, "Full backup completed with status: " + backupRunStatus);
671             }
672             BackupObserverUtils.sendBackupFinished(mBackupObserver, backupRunStatus);
673 
674             cleanUpPipes(transportPipes);
675             cleanUpPipes(enginePipes);
676 
677             unregisterTask();
678 
679             if (mJob != null) {
680                 mJob.finishBackupPass(mUserId);
681             }
682 
683             synchronized (mUserBackupManagerService.getQueueLock()) {
684                 mUserBackupManagerService.setRunningFullBackupTask(null);
685             }
686 
687             mListener.onFinished("PFTBT.run()");
688 
689             mLatch.countDown();
690 
691             // Now that we're actually done with schedule-driven work, reschedule
692             // the next pass based on the new queue state.
693             if (mUpdateSchedule) {
694                 mUserBackupManagerService.scheduleNextFullBackupJob(backoff);
695             }
696 
697             Slog.i(TAG, "Full data backup pass finished.");
698             mUserBackupManagerService.getWakelock().release();
699         }
700     }
701 
cleanUpPipes(ParcelFileDescriptor[] pipes)702     void cleanUpPipes(ParcelFileDescriptor[] pipes) {
703         if (pipes != null) {
704             if (pipes[0] != null) {
705                 ParcelFileDescriptor fd = pipes[0];
706                 pipes[0] = null;
707                 try {
708                     fd.close();
709                 } catch (IOException e) {
710                     Slog.w(TAG, "Unable to close pipe!");
711                 }
712             }
713             if (pipes[1] != null) {
714                 ParcelFileDescriptor fd = pipes[1];
715                 pipes[1] = null;
716                 try {
717                     fd.close();
718                 } catch (IOException e) {
719                     Slog.w(TAG, "Unable to close pipe!");
720                 }
721             }
722         }
723     }
724 
725     // Run the backup and pipe it back to the given socket -- expects to run on
726     // a standalone thread.  The  runner owns this half of the pipe, and closes
727     // it to indicate EOD to the other end.
728     class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
729         final AtomicLong mResult = new AtomicLong(BackupTransport.AGENT_ERROR);
730         final CountDownLatch mLatch = new CountDownLatch(1);
731         final TransportConnection mTransportConnection;
732         final long mQuota;
733         private final int mCurrentOpToken;
734         private final int mTransportFlags;
735 
SinglePackageBackupPreflight( TransportConnection transportConnection, long quota, int currentOpToken, int transportFlags)736         SinglePackageBackupPreflight(
737                 TransportConnection transportConnection,
738                 long quota,
739                 int currentOpToken,
740                 int transportFlags) {
741             mTransportConnection = transportConnection;
742             mQuota = quota;
743             mCurrentOpToken = currentOpToken;
744             mTransportFlags = transportFlags;
745         }
746 
747         @Override
preflightFullBackup(PackageInfo pkg, IBackupAgent agent)748         public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
749             int result;
750             long fullBackupAgentTimeoutMillis =
751                     mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
752             try {
753                 mUserBackupManagerService.prepareOperationTimeout(
754                         mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OpType.BACKUP_WAIT);
755                 if (MORE_DEBUG) {
756                     Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
757                 }
758                 agent.doMeasureFullBackup(mQuota, mCurrentOpToken,
759                         mUserBackupManagerService.getBackupManagerBinder(), mTransportFlags);
760 
761                 // Now wait to get our result back.  If this backstop timeout is reached without
762                 // the latch being thrown, flow will continue as though a result or "normal"
763                 // timeout had been produced.  In case of a real backstop timeout, mResult
764                 // will still contain the value it was constructed with, AGENT_ERROR, which
765                 // intentionaly falls into the "just report failure" code.
766                 mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
767 
768                 long totalSize = mResult.get();
769                 // If preflight timed out, mResult will contain error code as int.
770                 if (totalSize < 0) {
771                     return (int) totalSize;
772                 }
773                 if (MORE_DEBUG) {
774                     Slog.v(TAG, "Got preflight response; size=" + totalSize);
775                 }
776 
777                 BackupTransportClient transport =
778                         mTransportConnection.connectOrThrow("PFTBT$SPBP.preflightFullBackup()");
779                 result = transport.checkFullBackupSize(totalSize);
780                 if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
781                     if (MORE_DEBUG) {
782                         Slog.d(TAG, "Package hit quota limit on preflight " +
783                                 pkg.packageName + ": " + totalSize + " of " + mQuota);
784                     }
785                     RemoteCall.execute(
786                             callback -> agent.doQuotaExceeded(totalSize, mQuota, callback),
787                             mAgentTimeoutParameters.getQuotaExceededTimeoutMillis());
788                 }
789             } catch (Exception e) {
790                 Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
791                 result = BackupTransport.AGENT_ERROR;
792             }
793             return result;
794         }
795 
796         @Override
execute()797         public void execute() {
798             // Unused.
799         }
800 
801         @Override
operationComplete(long result)802         public void operationComplete(long result) {
803             // got the callback, and our preflightFullBackup() method is waiting for the result
804             if (MORE_DEBUG) {
805                 Slog.i(TAG, "Preflight op complete, result=" + result);
806             }
807             mResult.set(result);
808             mLatch.countDown();
809             mOperationStorage.removeOperation(mCurrentOpToken);
810         }
811 
812         @Override
handleCancel(boolean cancelAll)813         public void handleCancel(boolean cancelAll) {
814             if (MORE_DEBUG) {
815                 Slog.i(TAG, "Preflight cancelled; failing");
816             }
817             mResult.set(BackupTransport.AGENT_ERROR);
818             mLatch.countDown();
819             mOperationStorage.removeOperation(mCurrentOpToken);
820         }
821 
822         @Override
getExpectedSizeOrErrorCode()823         public long getExpectedSizeOrErrorCode() {
824             long fullBackupAgentTimeoutMillis =
825                     mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
826             try {
827                 mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
828                 return mResult.get();
829             } catch (InterruptedException e) {
830                 return BackupTransport.NO_MORE_DATA;
831             }
832         }
833     }
834 
835     class SinglePackageBackupRunner implements Runnable, BackupRestoreTask {
836         final ParcelFileDescriptor mOutput;
837         final PackageInfo mTarget;
838         final SinglePackageBackupPreflight mPreflight;
839         final CountDownLatch mPreflightLatch;
840         final CountDownLatch mBackupLatch;
841         private final int mCurrentOpToken;
842         private final int mEphemeralToken;
843         private FullBackupEngine mEngine;
844         private volatile int mPreflightResult;
845         private volatile int mBackupResult;
846         private final long mQuota;
847         private volatile boolean mIsCancelled;
848         private final int mTransportFlags;
849 
SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target, TransportConnection transportConnection, long quota, int currentOpToken, int transportFlags)850         SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
851                 TransportConnection transportConnection, long quota, int currentOpToken,
852                 int transportFlags) throws IOException {
853             mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
854             mTarget = target;
855             mCurrentOpToken = currentOpToken;
856             mEphemeralToken = mUserBackupManagerService.generateRandomIntegerToken();
857             mPreflight = new SinglePackageBackupPreflight(
858                     transportConnection, quota, mEphemeralToken, transportFlags);
859             mPreflightLatch = new CountDownLatch(1);
860             mBackupLatch = new CountDownLatch(1);
861             mPreflightResult = BackupTransport.AGENT_ERROR;
862             mBackupResult = BackupTransport.AGENT_ERROR;
863             mQuota = quota;
864             mTransportFlags = transportFlags;
865             registerTask(target.packageName);
866         }
867 
registerTask(String packageName)868         void registerTask(String packageName) {
869             Set<String> packages = Sets.newHashSet(packageName);
870             mOperationStorage.registerOperationForPackages(mCurrentOpToken, OpState.PENDING,
871                     packages, this, OpType.BACKUP_WAIT);
872         }
873 
unregisterTask()874         void unregisterTask() {
875             mOperationStorage.removeOperation(mCurrentOpToken);
876         }
877 
878         @Override
run()879         public void run() {
880             FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
881             mEngine =
882                     new FullBackupEngine(
883                             mUserBackupManagerService,
884                             out,
885                             mPreflight,
886                             mTarget,
887                             false,
888                             this,
889                             mQuota,
890                             mCurrentOpToken,
891                             mTransportFlags,
892                             mBackupEligibilityRules,
893                             mBackupManagerMonitorEventSender);
894             try {
895                 try {
896                     if (!mIsCancelled) {
897                         mPreflightResult = mEngine.preflightCheck();
898                     }
899                 } finally {
900                     mPreflightLatch.countDown();
901                 }
902                 // If there is no error on preflight, continue backup.
903                 if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
904                     if (!mIsCancelled) {
905                         mBackupResult = mEngine.backupOnePackage();
906                     }
907                 }
908             } catch (Exception e) {
909                 Slog.w(TAG, "Exception during full package backup of " + mTarget.packageName,
910                         e);
911             } finally {
912                 unregisterTask();
913                 mBackupLatch.countDown();
914                 try {
915                     mOutput.close();
916                 } catch (IOException e) {
917                     Slog.w(TAG, "Error closing transport pipe in runner");
918                 }
919             }
920         }
921 
sendQuotaExceeded(final long backupDataBytes, final long quotaBytes)922         public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
923             mEngine.sendQuotaExceeded(backupDataBytes, quotaBytes);
924         }
925 
926         // If preflight succeeded, returns positive number - preflight size,
927         // otherwise return negative error code.
getPreflightResultBlocking()928         long getPreflightResultBlocking() {
929             long fullBackupAgentTimeoutMillis =
930                     mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
931             try {
932                 mPreflightLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
933                 if (mIsCancelled) {
934                     return BackupManager.ERROR_BACKUP_CANCELLED;
935                 }
936                 if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
937                     return mPreflight.getExpectedSizeOrErrorCode();
938                 } else {
939                     return mPreflightResult;
940                 }
941             } catch (InterruptedException e) {
942                 return BackupTransport.AGENT_ERROR;
943             }
944         }
945 
getBackupResultBlocking()946         int getBackupResultBlocking() {
947             long fullBackupAgentTimeoutMillis =
948                     mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
949             try {
950                 mBackupLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
951                 if (mIsCancelled) {
952                     return BackupManager.ERROR_BACKUP_CANCELLED;
953                 }
954                 return mBackupResult;
955             } catch (InterruptedException e) {
956                 return BackupTransport.AGENT_ERROR;
957             }
958         }
959 
960 
961         // BackupRestoreTask interface: specifically, timeout detection
962 
963         @Override
execute()964         public void execute() { /* intentionally empty */ }
965 
966         @Override
operationComplete(long result)967         public void operationComplete(long result) { /* intentionally empty */ }
968 
969         @Override
handleCancel(boolean cancelAll)970         public void handleCancel(boolean cancelAll) {
971             if (DEBUG) {
972                 Slog.w(TAG, "Full backup cancel of " + mTarget.packageName);
973             }
974 
975             mBackupManagerMonitorEventSender.monitorEvent(
976                     BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL,
977                     mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
978             mIsCancelled = true;
979             // Cancel tasks spun off by this task.
980             mUserBackupManagerService.handleCancel(mEphemeralToken, cancelAll);
981             mUserBackupManagerService.tearDownAgentAndKill(mTarget.applicationInfo);
982             // Free up everyone waiting on this task and its children.
983             mPreflightLatch.countDown();
984             mBackupLatch.countDown();
985             // We are done with this operation.
986             mOperationStorage.removeOperation(mCurrentOpToken);
987         }
988     }
989 }
990