OSDN Git Service

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