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