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.restore;
18 
19 import static com.android.server.backup.BackupManagerService.DEBUG;
20 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
21 import static com.android.server.backup.BackupManagerService.TAG;
22 import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
23 import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
24 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
25 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
26 
27 import android.annotation.NonNull;
28 import android.app.ApplicationThreadConstants;
29 import android.app.IBackupAgent;
30 import android.app.backup.BackupAgent;
31 import android.app.backup.BackupAnnotations;
32 import android.app.backup.BackupManager;
33 import android.app.backup.FullBackup;
34 import android.app.backup.IBackupManagerMonitor;
35 import android.app.backup.IFullBackupRestoreObserver;
36 import android.content.pm.ApplicationInfo;
37 import android.content.pm.PackageInfo;
38 import android.content.pm.PackageManager.NameNotFoundException;
39 import android.content.pm.PackageManagerInternal;
40 import android.content.pm.Signature;
41 import android.os.ParcelFileDescriptor;
42 import android.os.RemoteException;
43 import android.provider.Settings;
44 import android.system.OsConstants;
45 import android.text.TextUtils;
46 import android.util.Slog;
47 
48 import com.android.internal.annotations.GuardedBy;
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.server.LocalServices;
51 import com.android.server.backup.BackupAgentTimeoutParameters;
52 import com.android.server.backup.BackupRestoreTask;
53 import com.android.server.backup.FileMetadata;
54 import com.android.server.backup.KeyValueAdbRestoreEngine;
55 import com.android.server.backup.OperationStorage;
56 import com.android.server.backup.OperationStorage.OpType;
57 import com.android.server.backup.UserBackupManagerService;
58 import com.android.server.backup.fullbackup.FullBackupObbConnection;
59 import com.android.server.backup.utils.BackupEligibilityRules;
60 import com.android.server.backup.utils.BytesReadListener;
61 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
62 import com.android.server.backup.utils.RestoreUtils;
63 import com.android.server.backup.utils.TarBackupReader;
64 
65 import java.io.File;
66 import java.io.FileOutputStream;
67 import java.io.IOException;
68 import java.io.InputStream;
69 import java.util.Arrays;
70 import java.util.HashMap;
71 import java.util.HashSet;
72 import java.util.List;
73 import java.util.Objects;
74 
75 /**
76  * Full restore engine, used by both adb restore and transport-based full restore.
77  */
78 public class FullRestoreEngine extends RestoreEngine {
79 
80     private final UserBackupManagerService mBackupManagerService;
81     private final OperationStorage mOperationStorage;
82     private final int mUserId;
83 
84     // Task in charge of monitoring timeouts
85     private final BackupRestoreTask mMonitorTask;
86 
87     private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
88 
89     // Dedicated observer, if any
90     private IFullBackupRestoreObserver mObserver;
91 
92     final IBackupManagerMonitor mMonitor;
93 
94     // Where we're delivering the file data as we go
95     private IBackupAgent mAgent;
96 
97     // Are we permitted to only deliver a specific package's metadata?
98     final PackageInfo mOnlyPackage;
99 
100     final boolean mAllowApks;
101 
102     // Which package are we currently handling data for?
103     private String mAgentPackage;
104 
105     // Info for working with the target app process
106     private ApplicationInfo mTargetApp;
107 
108     // Machinery for restoring OBBs
109     private FullBackupObbConnection mObbConnection = null;
110 
111     // possible handling states for a given package in the restore dataset
112     private final HashMap<String, RestorePolicy> mPackagePolicies
113             = new HashMap<>();
114 
115     // installer package names for each encountered app, derived from the manifests
116     private final HashMap<String, String> mPackageInstallers = new HashMap<>();
117 
118     // Signatures for a given package found in its manifest file
119     private final HashMap<String, Signature[]> mManifestSignatures
120             = new HashMap<>();
121 
122     // Packages we've already wiped data on when restoring their first file
123     private final HashSet<String> mClearedPackages = new HashSet<>();
124 
125     // Working buffer
126     final byte[] mBuffer;
127 
128     // Pipes for moving data
129     private ParcelFileDescriptor[] mPipes = null;
130     private final Object mPipesLock = new Object();
131 
132     // Widget blob to be restored out-of-band
133     private byte[] mWidgetData = null;
134     private long mAppVersion;
135 
136     final int mEphemeralOpToken;
137 
138     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
139     private final boolean mIsAdbRestore;
140     @GuardedBy("mPipesLock")
141     private boolean mPipesClosed;
142     private final BackupEligibilityRules mBackupEligibilityRules;
143 
144     private FileMetadata mReadOnlyParent = null;
145 
FullRestoreEngine( UserBackupManagerService backupManagerService, OperationStorage operationStorage, BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer, IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks, int ephemeralOpToken, boolean isAdbRestore, BackupEligibilityRules backupEligibilityRules)146     public FullRestoreEngine(
147             UserBackupManagerService backupManagerService, OperationStorage operationStorage,
148             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
149             IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
150             int ephemeralOpToken, boolean isAdbRestore,
151             BackupEligibilityRules backupEligibilityRules) {
152         mBackupManagerService = backupManagerService;
153         mOperationStorage = operationStorage;
154         mEphemeralOpToken = ephemeralOpToken;
155         mMonitorTask = monitorTask;
156         mObserver = observer;
157         mMonitor = monitor;
158         mOnlyPackage = onlyPackage;
159         mAllowApks = allowApks;
160         mBuffer = new byte[32 * 1024];
161         mAgentTimeoutParameters = Objects.requireNonNull(
162                 backupManagerService.getAgentTimeoutParameters(),
163                 "Timeout parameters cannot be null");
164         mIsAdbRestore = isAdbRestore;
165         mUserId = backupManagerService.getUserId();
166         mBackupEligibilityRules = backupEligibilityRules;
167     }
168 
169     @VisibleForTesting
FullRestoreEngine()170     FullRestoreEngine() {
171         mIsAdbRestore = false;
172         mAllowApks = false;
173         mEphemeralOpToken = 0;
174         mUserId = 0;
175         mBackupEligibilityRules = null;
176         mAgentTimeoutParameters = null;
177         mBuffer = null;
178         mBackupManagerService = null;
179         mOperationStorage = null;
180         mMonitor = null;
181         mMonitorTask = null;
182         mOnlyPackage = null;
183     }
184 
getAgent()185     public IBackupAgent getAgent() {
186         return mAgent;
187     }
188 
getWidgetData()189     public byte[] getWidgetData() {
190         return mWidgetData;
191     }
192 
restoreOneFile(InputStream instream, boolean mustKillAgent, byte[] buffer, PackageInfo onlyPackage, boolean allowApks, int token, IBackupManagerMonitor monitor)193     public boolean restoreOneFile(InputStream instream, boolean mustKillAgent, byte[] buffer,
194             PackageInfo onlyPackage, boolean allowApks, int token, IBackupManagerMonitor monitor) {
195         if (!isRunning()) {
196             Slog.w(TAG, "Restore engine used after halting");
197             return false;
198         }
199 
200         BytesReadListener bytesReadListener = bytesRead -> { };
201 
202         TarBackupReader tarBackupReader = new TarBackupReader(instream,
203                 bytesReadListener, monitor);
204 
205         FileMetadata info;
206         try {
207             if (MORE_DEBUG) {
208                 Slog.v(TAG, "Reading tar header for restoring file");
209             }
210             info = tarBackupReader.readTarHeaders();
211             if (info != null) {
212                 if (MORE_DEBUG) {
213                     info.dump();
214                 }
215 
216                 final String pkg = info.packageName;
217                 if (!pkg.equals(mAgentPackage)) {
218                     // In the single-package case, it's a semantic error to expect
219                     // one app's data but see a different app's on the wire
220                     if (onlyPackage != null) {
221                         if (!pkg.equals(onlyPackage.packageName)) {
222                             Slog.w(TAG, "Expected data for " + onlyPackage + " but saw " + pkg);
223                             setResult(RestoreEngine.TRANSPORT_FAILURE);
224                             setRunning(false);
225                             return false;
226                         }
227                     }
228 
229                     // okay, change in package; set up our various
230                     // bookkeeping if we haven't seen it yet
231                     if (!mPackagePolicies.containsKey(pkg)) {
232                         mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
233                     }
234 
235                     // Clean up the previous agent relationship if necessary,
236                     // and let the observer know we're considering a new app.
237                     if (mAgent != null) {
238                         if (DEBUG) {
239                             Slog.d(TAG, "Saw new package; finalizing old one");
240                         }
241                         // Now we're really done
242                         tearDownPipes();
243                         tearDownAgent(mTargetApp, mIsAdbRestore);
244                         mTargetApp = null;
245                         mAgentPackage = null;
246                     }
247                 }
248 
249                 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
250                     Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
251                             info);
252                     // readAppManifestAndReturnSignatures() will have extracted the version from
253                     // the manifest, so we save it to use in adb key-value restore later.
254                     mAppVersion = info.version;
255                     PackageManagerInternal pmi = LocalServices.getService(
256                             PackageManagerInternal.class);
257                     RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
258                             mBackupManagerService.getPackageManager(), allowApks, info, signatures,
259                             pmi, mUserId, mBackupEligibilityRules);
260                     mManifestSignatures.put(info.packageName, signatures);
261                     mPackagePolicies.put(pkg, restorePolicy);
262                     mPackageInstallers.put(pkg, info.installerPackageName);
263                     // We've read only the manifest content itself at this point,
264                     // so consume the footer before looping around to the next
265                     // input file
266                     tarBackupReader.skipTarPadding(info.size);
267                     mObserver = FullBackupRestoreObserverUtils.sendOnRestorePackage(mObserver, pkg);
268                 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
269                     // Metadata blobs!
270                     tarBackupReader.readMetadata(info);
271 
272                     // The following only exist because we want to keep refactoring as safe as
273                     // possible, without changing too much.
274                     // TODO: Refactor, so that there are no funny things like this.
275                     // This is read during TarBackupReader.readMetadata().
276                     mWidgetData = tarBackupReader.getWidgetData();
277                     // This can be nulled during TarBackupReader.readMetadata().
278                     monitor = tarBackupReader.getMonitor();
279 
280                     tarBackupReader.skipTarPadding(info.size);
281                 } else {
282                     // Non-manifest, so it's actual file data.  Is this a package
283                     // we're ignoring?
284                     boolean okay = true;
285                     RestorePolicy policy = mPackagePolicies.get(pkg);
286                     switch (policy) {
287                         case IGNORE:
288                             okay = false;
289                             break;
290 
291                         case ACCEPT_IF_APK:
292                             // If we're in accept-if-apk state, then the first file we
293                             // see MUST be the apk.
294                             if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
295                                 if (DEBUG) {
296                                     Slog.d(TAG, "APK file; installing");
297                                 }
298                                 // Try to install the app.
299                                 String installerPackageName = mPackageInstallers.get(pkg);
300                                 boolean isSuccessfullyInstalled = RestoreUtils.installApk(
301                                         instream, mBackupManagerService.getContext(),
302                                         mDeleteObserver, mManifestSignatures,
303                                         mPackagePolicies, info, installerPackageName,
304                                         bytesReadListener, mUserId);
305                                 // good to go; promote to ACCEPT
306                                 mPackagePolicies.put(pkg, isSuccessfullyInstalled
307                                         ? RestorePolicy.ACCEPT
308                                         : RestorePolicy.IGNORE);
309                                 // At this point we've consumed this file entry
310                                 // ourselves, so just strip the tar footer and
311                                 // go on to the next file in the input stream
312                                 tarBackupReader.skipTarPadding(info.size);
313                                 return true;
314                             } else {
315                                 // File data before (or without) the apk.  We can't
316                                 // handle it coherently in this case so ignore it.
317                                 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
318                                 okay = false;
319                             }
320                             break;
321 
322                         case ACCEPT:
323                             if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
324                                 if (DEBUG) {
325                                     Slog.d(TAG, "apk present but ACCEPT");
326                                 }
327                                 // we can take the data without the apk, so we
328                                 // *want* to do so.  skip the apk by declaring this
329                                 // one file not-okay without changing the restore
330                                 // policy for the package.
331                                 okay = false;
332                             }
333                             break;
334 
335                         default:
336                             // Something has gone dreadfully wrong when determining
337                             // the restore policy from the manifest.  Ignore the
338                             // rest of this package's data.
339                             Slog.e(TAG, "Invalid policy from manifest");
340                             okay = false;
341                             mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
342                             break;
343                     }
344 
345                     // Is it a *file* we need to drop or is it not a canonical path?
346                     if (!isRestorableFile(info) || !isCanonicalFilePath(info.path)) {
347                         okay = false;
348                     }
349 
350                     // If the policy is satisfied, go ahead and set up to pipe the
351                     // data to the agent.
352                     if (MORE_DEBUG && okay && mAgent != null) {
353                         Slog.i(TAG, "Reusing existing agent instance");
354                     }
355                     if (okay && mAgent == null) {
356                         if (MORE_DEBUG) {
357                             Slog.d(TAG, "Need to launch agent for " + pkg);
358                         }
359 
360                         try {
361                             mTargetApp =
362                                     mBackupManagerService.getPackageManager()
363                                             .getApplicationInfoAsUser(pkg, 0, mUserId);
364 
365                             // If we haven't sent any data to this app yet, we probably
366                             // need to clear it first. Check that.
367                             if (!mClearedPackages.contains(pkg)) {
368                                 // Apps with their own backup agents are responsible for coherently
369                                 // managing a full restore.
370                                 // In some rare cases they can't, especially in case of deferred
371                                 // restore. In this case check whether this app should be forced to
372                                 // clear up.
373                                 // TODO: Fix this properly with manifest parameter.
374                                 boolean forceClear = shouldForceClearAppDataOnFullRestore(
375                                         mTargetApp.packageName);
376                                 if (mTargetApp.backupAgentName == null || forceClear) {
377                                     if (DEBUG) {
378                                         Slog.d(TAG,
379                                                 "Clearing app data preparatory to full restore");
380                                     }
381                                     mBackupManagerService.clearApplicationDataBeforeRestore(pkg);
382                                 } else {
383                                     if (MORE_DEBUG) {
384                                         Slog.d(TAG, "backup agent ("
385                                                 + mTargetApp.backupAgentName + ") => no clear");
386                                     }
387                                 }
388                                 mClearedPackages.add(pkg);
389                             } else {
390                                 if (MORE_DEBUG) {
391                                     Slog.d(TAG, "We've initialized this app already; no clear "
392                                             + "required");
393                                 }
394                             }
395 
396                             // All set; now set up the IPC and launch the agent
397                             setUpPipes();
398                             mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
399                                     FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
400                                             ? ApplicationThreadConstants.BACKUP_MODE_RESTORE
401                                             : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL,
402                                     mBackupEligibilityRules.getBackupDestination());
403                             mAgentPackage = pkg;
404                         } catch (IOException | NameNotFoundException e) {
405                             // fall through to error handling
406                         }
407 
408                         if (mAgent == null) {
409                             Slog.e(TAG, "Unable to create agent for " + pkg);
410                             okay = false;
411                             tearDownPipes();
412                             mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
413                         }
414                     }
415 
416                     // Make sure we never give data to the wrong app.  This
417                     // should never happen but a little paranoia here won't go amiss.
418                     if (okay && !pkg.equals(mAgentPackage)) {
419                         Slog.e(TAG, "Restoring data for " + pkg
420                                 + " but agent is for " + mAgentPackage);
421                         okay = false;
422                     }
423 
424                     if (shouldSkipReadOnlyDir(info)) {
425                         // b/194894879: We don't support restore of read-only dirs.
426                         okay = false;
427                     }
428 
429                     // At this point we have an agent ready to handle the full
430                     // restore data as well as a pipe for sending data to
431                     // that agent.  Tell the agent to start reading from the
432                     // pipe.
433                     if (okay) {
434                         boolean agentSuccess = true;
435                         long toCopy = info.size;
436                         final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE);
437                         final long timeout = isSharedStorage ?
438                                 mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
439                                 mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
440                                         mTargetApp.uid);
441                         try {
442                             mBackupManagerService.prepareOperationTimeout(token,
443                                     timeout,
444                                     mMonitorTask,
445                                     OpType.RESTORE_WAIT);
446 
447                             if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
448                                 if (DEBUG) {
449                                     Slog.d(TAG, "Restoring OBB file for " + pkg
450                                             + " : " + info.path);
451                                 }
452                                 mObbConnection.restoreObbFile(pkg, mPipes[0],
453                                         info.size, info.type, info.path, info.mode,
454                                         info.mtime, token,
455                                         mBackupManagerService.getBackupManagerBinder());
456                             } else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) {
457                                 // This is only possible during adb restore.
458                                 // TODO: Refactor to clearly separate the flows.
459                                 if (DEBUG) {
460                                     Slog.d(TAG, "Restoring key-value file for " + pkg
461                                             + " : " + info.path);
462                                 }
463                                 // Set the version saved from manifest entry.
464                                 info.version = mAppVersion;
465                                 KeyValueAdbRestoreEngine restoreEngine =
466                                         new KeyValueAdbRestoreEngine(
467                                                 mBackupManagerService,
468                                                 mBackupManagerService.getDataDir(), info, mPipes[0],
469                                                 mAgent, token);
470                                 new Thread(restoreEngine, "restore-key-value-runner").start();
471                             } else {
472                                 if (MORE_DEBUG) {
473                                     Slog.d(TAG, "Invoking agent to restore file " + info.path);
474                                 }
475                                 // fire up the app's agent listening on the socket.  If
476                                 // the agent is running in the system process we can't
477                                 // just invoke it asynchronously, so we provide a thread
478                                 // for it here.
479                                 if (mTargetApp.processName.equals("system")) {
480                                     Slog.d(TAG, "system process agent - spinning a thread");
481                                     RestoreFileRunnable runner = new RestoreFileRunnable(
482                                             mBackupManagerService, mAgent, info, mPipes[0], token);
483                                     new Thread(runner, "restore-sys-runner").start();
484                                 } else {
485                                     mAgent.doRestoreFile(mPipes[0], info.size, info.type,
486                                             info.domain, info.path, info.mode, info.mtime,
487                                             token, mBackupManagerService.getBackupManagerBinder());
488                                 }
489                             }
490                         } catch (IOException e) {
491                             // couldn't dup the socket for a process-local restore
492                             Slog.d(TAG, "Couldn't establish restore");
493                             agentSuccess = false;
494                             okay = false;
495                         } catch (RemoteException e) {
496                             // whoops, remote entity went away.  We'll eat the content
497                             // ourselves, then, and not copy it over.
498                             Slog.e(TAG, "Agent crashed during full restore");
499                             agentSuccess = false;
500                             okay = false;
501                         }
502 
503                         // Copy over the data if the agent is still good
504                         if (okay) {
505                             if (MORE_DEBUG) {
506                                 Slog.v(TAG, "  copying to restore agent: " + toCopy + " bytes");
507                             }
508                             boolean pipeOkay = true;
509                             FileOutputStream pipe = new FileOutputStream(
510                                     mPipes[1].getFileDescriptor());
511                             while (toCopy > 0) {
512                                 int toRead = (toCopy > buffer.length)
513                                         ? buffer.length : (int) toCopy;
514                                 int nRead = instream.read(buffer, 0, toRead);
515                                 if (nRead <= 0) {
516                                     break;
517                                 }
518                                 toCopy -= nRead;
519 
520                                 // send it to the output pipe as long as things
521                                 // are still good
522                                 if (pipeOkay) {
523                                     try {
524                                         pipe.write(buffer, 0, nRead);
525                                     } catch (IOException e) {
526                                         Slog.e(TAG, "Failed to write to restore pipe: "
527                                                 + e.getMessage());
528                                         pipeOkay = false;
529                                     }
530                                 }
531                             }
532 
533                             // done sending that file!  Now we just need to consume
534                             // the delta from info.size to the end of block.
535                             tarBackupReader.skipTarPadding(info.size);
536 
537                             // and now that we've sent it all, wait for the remote
538                             // side to acknowledge receipt
539                             agentSuccess = mBackupManagerService.waitUntilOperationComplete(token);
540                         }
541 
542                         // okay, if the remote end failed at any point, deal with
543                         // it by ignoring the rest of the restore on it
544                         if (!agentSuccess) {
545                             Slog.w(TAG, "Agent failure restoring " + pkg + "; ending restore");
546                             mBackupManagerService.getBackupHandler().removeMessages(
547                                     MSG_RESTORE_OPERATION_TIMEOUT);
548                             tearDownPipes();
549                             tearDownAgent(mTargetApp, false);
550                             mAgent = null;
551                             mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
552 
553                             // If this was a single-package restore, we halt immediately
554                             // with an agent error under these circumstances
555                             if (onlyPackage != null) {
556                                 setResult(RestoreEngine.TARGET_FAILURE);
557                                 setRunning(false);
558                                 return false;
559                             }
560                         }
561                     }
562 
563                     // Problems setting up the agent communication, an explicitly
564                     // dropped file, or an already-ignored package: skip to the
565                     // next stream entry by reading and discarding this file.
566                     if (!okay) {
567                         if (MORE_DEBUG) {
568                             Slog.d(TAG, "[discarding file content]");
569                         }
570                         long bytesToConsume = (info.size + 511) & ~511;
571                         while (bytesToConsume > 0) {
572                             int toRead = (bytesToConsume > buffer.length)
573                                     ? buffer.length : (int) bytesToConsume;
574                             long nRead = instream.read(buffer, 0, toRead);
575                             if (nRead <= 0) {
576                                 break;
577                             }
578                             bytesToConsume -= nRead;
579                         }
580                     }
581                 }
582             }
583         } catch (IOException e) {
584             if (DEBUG) {
585                 Slog.w(TAG, "io exception on restore socket read: " + e.getMessage());
586             }
587             setResult(RestoreEngine.TRANSPORT_FAILURE);
588             info = null;
589         }
590 
591         // If we got here we're either running smoothly or we've finished
592         if (info == null) {
593             if (MORE_DEBUG) {
594                 Slog.i(TAG, "No [more] data for this package; tearing down");
595             }
596             tearDownPipes();
597             setRunning(false);
598             if (mustKillAgent) {
599                 tearDownAgent(mTargetApp, mIsAdbRestore);
600             }
601         }
602         return (info != null);
603     }
604 
shouldSkipReadOnlyDir(FileMetadata info)605     boolean shouldSkipReadOnlyDir(FileMetadata info) {
606         if (isValidParent(mReadOnlyParent, info)) {
607             // This file has a read-only parent directory, we shouldn't
608             // restore it.
609             return true;
610         } else {
611             // We're now in a different branch of the file tree, update the parent
612             // value.
613             if (isReadOnlyDir(info)) {
614                 // Current directory is read-only. Remember it so that we can skip all
615                 // of its contents.
616                 mReadOnlyParent = info;
617                 Slog.w(TAG, "Skipping restore of " + info.path + " and its contents as "
618                         + "read-only dirs are currently not supported.");
619                 return true;
620             } else {
621                 mReadOnlyParent = null;
622             }
623         }
624 
625         return false;
626     }
627 
isValidParent(FileMetadata parentDir, @NonNull FileMetadata childDir)628     private static boolean isValidParent(FileMetadata parentDir, @NonNull FileMetadata childDir) {
629         return parentDir != null
630                 && childDir.packageName.equals(parentDir.packageName)
631                 && childDir.domain.equals(parentDir.domain)
632                 && childDir.path.startsWith(getPathWithTrailingSeparator(parentDir.path));
633     }
634 
getPathWithTrailingSeparator(String path)635     private static String getPathWithTrailingSeparator(String path) {
636         return path.endsWith(File.separator) ? path : path + File.separator;
637     }
638 
isReadOnlyDir(FileMetadata file)639     private static boolean isReadOnlyDir(FileMetadata file) {
640         // Check if owner has 'write' bit in the file's mode value (see 'man -7 inode' for details).
641         return file.type == BackupAgent.TYPE_DIRECTORY && (file.mode & OsConstants.S_IWUSR) == 0;
642     }
643 
setUpPipes()644     private void setUpPipes() throws IOException {
645         synchronized (mPipesLock) {
646             mPipes = ParcelFileDescriptor.createPipe();
647             mPipesClosed = false;
648         }
649     }
650 
tearDownPipes()651     private void tearDownPipes() {
652         // Teardown might arise from the inline restore processing or from the asynchronous
653         // timeout mechanism, and these might race.  Make sure we don't try to close and
654         // null out the pipes twice.
655         synchronized (mPipesLock) {
656             if (!mPipesClosed && mPipes != null) {
657                 try {
658                     mPipes[0].close();
659                     mPipes[1].close();
660 
661                     mPipesClosed = true;
662                 } catch (IOException e) {
663                     Slog.w(TAG, "Couldn't close agent pipes", e);
664                 }
665             }
666         }
667     }
668 
tearDownAgent(ApplicationInfo app, boolean doRestoreFinished)669     private void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
670         if (mAgent != null) {
671             try {
672                 // In the adb restore case, we do restore-finished here
673                 if (doRestoreFinished) {
674                     final int token = mBackupManagerService.generateRandomIntegerToken();
675                     long fullBackupAgentTimeoutMillis =
676                             mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
677                     final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
678                             mBackupManagerService, mOperationStorage, token);
679                     mBackupManagerService.prepareOperationTimeout(
680                             token, fullBackupAgentTimeoutMillis, latch, OpType.RESTORE_WAIT);
681                     if (mTargetApp.processName.equals("system")) {
682                         if (MORE_DEBUG) {
683                             Slog.d(TAG, "system agent - restoreFinished on thread");
684                         }
685                         Runnable runner = new AdbRestoreFinishedRunnable(mAgent, token,
686                                 mBackupManagerService);
687                         new Thread(runner, "restore-sys-finished-runner").start();
688                     } else {
689                         mAgent.doRestoreFinished(token,
690                                 mBackupManagerService.getBackupManagerBinder());
691                     }
692 
693                     latch.await();
694                 }
695 
696                 mBackupManagerService.tearDownAgentAndKill(app);
697             } catch (RemoteException e) {
698                 Slog.d(TAG, "Lost app trying to shut down");
699             }
700             mAgent = null;
701         }
702     }
703 
handleTimeout()704     void handleTimeout() {
705         tearDownPipes();
706         setResult(RestoreEngine.TARGET_FAILURE);
707         setRunning(false);
708     }
709 
isRestorableFile(FileMetadata info)710     private boolean isRestorableFile(FileMetadata info) {
711         if (mBackupEligibilityRules.getBackupDestination()
712                 == BackupAnnotations.BackupDestination.DEVICE_TRANSFER) {
713             // Everything is eligible for device-to-device migration.
714             return true;
715         }
716         if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
717             if (MORE_DEBUG) {
718                 Slog.i(TAG, "Dropping cache file path " + info.path);
719             }
720             return false;
721         }
722 
723         if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
724             // It's possible this is "no-backup" dir contents in an archive stream
725             // produced on a device running a version of the OS that predates that
726             // API.  Respect the no-backup intention and don't let the data get to
727             // the app.
728             if (info.path.startsWith("no_backup/")) {
729                 if (MORE_DEBUG) {
730                     Slog.i(TAG, "Dropping no_backup file path " + info.path);
731                 }
732                 return false;
733             }
734         }
735 
736         // Otherwise we think this file is good to go
737         return true;
738     }
739 
isCanonicalFilePath(String path)740     private static boolean isCanonicalFilePath(String path) {
741         if (path.contains("..") || path.contains("//")) {
742             if (MORE_DEBUG) {
743                 Slog.w(TAG, "Dropping invalid path " + path);
744             }
745             return false;
746         }
747 
748         return true;
749     }
750 
751     /**
752      * Returns whether the package is in the list of the packages for which clear app data should
753      * be called despite the fact that they have backup agent.
754      *
755      * <p>The list is read from {@link Settings.Secure#PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
756      */
shouldForceClearAppDataOnFullRestore(String packageName)757     private boolean shouldForceClearAppDataOnFullRestore(String packageName) {
758         String packageListString = Settings.Secure.getStringForUser(
759                 mBackupManagerService.getContext().getContentResolver(),
760                 Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
761                 mUserId);
762         if (TextUtils.isEmpty(packageListString)) {
763             return false;
764         }
765 
766         List<String> packages = Arrays.asList(packageListString.split(";"));
767         return packages.contains(packageName);
768     }
769 
sendOnRestorePackage(String name)770     void sendOnRestorePackage(String name) {
771         if (mObserver != null) {
772             try {
773                 // TODO: use a more user-friendly name string
774                 mObserver.onRestorePackage(name);
775             } catch (RemoteException e) {
776                 Slog.w(TAG, "full restore observer went away: restorePackage");
777                 mObserver = null;
778             }
779         }
780     }
781 }
782