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