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