blob: 20aa0647d261ccd3ed7b7ca6459b8271d1ddcb64 [file] [log] [blame]
Christopher Tate487529a2009-04-29 14:03:25 -07001/*
2 * Copyright (C) 2009 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
Christopher Tate45281862010-03-05 15:46:30 -080017package android.app.backup;
Christopher Tate487529a2009-04-29 14:03:25 -070018
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +000019import android.annotation.Nullable;
Christopher Tate181fafa2009-05-14 11:12:14 -070020import android.app.IBackupAgent;
Christopher Tatef85f5b22013-04-18 16:57:43 -070021import android.app.QueuedWork;
Michal Karpinskib5e09312018-02-19 13:55:23 +000022import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
Christopher Tate181fafa2009-05-14 11:12:14 -070023import android.content.Context;
24import android.content.ContextWrapper;
Christopher Tate79ec80d2011-06-24 14:58:49 -070025import android.content.pm.ApplicationInfo;
Christopher Tate19024922010-01-22 16:39:53 -080026import android.os.Binder;
Christopher Tatef85f5b22013-04-18 16:57:43 -070027import android.os.Handler;
Christopher Tate487529a2009-04-29 14:03:25 -070028import android.os.IBinder;
Christopher Tatef85f5b22013-04-18 16:57:43 -070029import android.os.Looper;
Christopher Tate22b87872009-05-04 16:41:53 -070030import android.os.ParcelFileDescriptor;
Christopher Tate5cb5c332013-02-21 14:32:12 -080031import android.os.Process;
Christopher Tate487529a2009-04-29 14:03:25 -070032import android.os.RemoteException;
Stefanot14bbded2018-10-11 12:07:53 +010033import android.os.UserHandle;
Elliott Hughes34385d32014-04-28 11:11:32 -070034import android.system.ErrnoException;
35import android.system.Os;
36import android.system.OsConstants;
37import android.system.StructStat;
Matthew Williams303650c2015-04-17 18:22:51 -070038import android.util.ArraySet;
Christopher Tate487529a2009-04-29 14:03:25 -070039import android.util.Log;
40
Christopher Tate91bb0e52016-09-30 17:52:19 -070041import libcore.io.IoUtils;
42
Jeff Sharkey8a372a02016-03-16 16:25:45 -060043import org.xmlpull.v1.XmlPullParserException;
44
Christopher Tate79ec80d2011-06-24 14:58:49 -070045import java.io.File;
Christopher Tate7926a692011-07-11 11:31:57 -070046import java.io.FileOutputStream;
Joe Onorato83248c42009-06-17 17:55:20 -070047import java.io.IOException;
Ruslan Tkhakokhovd7f5fdb42019-12-10 11:15:45 +000048import java.util.Collections;
49import java.util.HashSet;
Christopher Tate79ec80d2011-06-24 14:58:49 -070050import java.util.LinkedList;
Ruslan Tkhakokhovd7f5fdb42019-12-10 11:15:45 +000051import java.util.List;
Matthew Williams303650c2015-04-17 18:22:51 -070052import java.util.Map;
53import java.util.Set;
Christopher Tatef85f5b22013-04-18 16:57:43 -070054import java.util.concurrent.CountDownLatch;
Christopher Tate79ec80d2011-06-24 14:58:49 -070055
Christopher Tate487529a2009-04-29 14:03:25 -070056/**
Scott Maind17da432010-04-29 21:42:58 -070057 * Provides the central interface between an
Christopher Tate4e14a822010-04-08 12:54:23 -070058 * application and Android's data backup infrastructure. An application that wishes
59 * to participate in the backup and restore mechanism will declare a subclass of
60 * {@link android.app.backup.BackupAgent}, implement the
Scott Maind17da432010-04-29 21:42:58 -070061 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()}
62 * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} methods,
63 * and provide the name of its backup agent class in its {@code AndroidManifest.xml} file via
Joe Fernandez61fd1e82011-10-26 13:39:11 -070064 * the <code>
65 * <a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code>
Scott Maind17da432010-04-29 21:42:58 -070066 * tag's {@code android:backupAgent} attribute.
Joe Fernandez61fd1e82011-10-26 13:39:11 -070067 *
68 * <div class="special reference">
69 * <h3>Developer Guides</h3>
70 * <p>For more information about using BackupAgent, read the
71 * <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p></div>
72 *
Scott Maind17da432010-04-29 21:42:58 -070073 * <h3>Basic Operation</h3>
Kenny Root5a20ea12010-02-23 18:49:11 -080074 * <p>
Christopher Tate4e14a822010-04-08 12:54:23 -070075 * When the application makes changes to data that it wishes to keep backed up,
76 * it should call the
77 * {@link android.app.backup.BackupManager#dataChanged() BackupManager.dataChanged()} method.
Scott Maind17da432010-04-29 21:42:58 -070078 * This notifies the Android Backup Manager that the application needs an opportunity
79 * to update its backup image. The Backup Manager, in turn, schedules a
Christopher Tate4e14a822010-04-08 12:54:23 -070080 * backup pass to be performed at an opportune time.
81 * <p>
Scott Maind17da432010-04-29 21:42:58 -070082 * Restore operations are typically performed only when applications are first
Christopher Tate4e14a822010-04-08 12:54:23 -070083 * installed on a device. At that time, the operating system checks to see whether
Scott Maind17da432010-04-29 21:42:58 -070084 * there is a previously-saved data set available for the application being installed, and if so,
85 * begins an immediate restore pass to deliver the backup data as part of the installation
Christopher Tate4e14a822010-04-08 12:54:23 -070086 * process.
87 * <p>
Scott Maind17da432010-04-29 21:42:58 -070088 * When a backup or restore pass is run, the application's process is launched
89 * (if not already running), the manifest-declared backup agent class (in the {@code
90 * android:backupAgent} attribute) is instantiated within
91 * that process, and the agent's {@link #onCreate()} method is invoked. This prepares the
Christopher Tate4e14a822010-04-08 12:54:23 -070092 * agent instance to run the actual backup or restore logic. At this point the
93 * agent's
94 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()} or
95 * {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} method will be
96 * invoked as appropriate for the operation being performed.
97 * <p>
Scott Maind17da432010-04-29 21:42:58 -070098 * A backup data set consists of one or more "entities," flattened binary data
99 * records that are each identified with a key string unique within the data set. Adding a
100 * record to the active data set or updating an existing record is done by simply
Christopher Tate4e14a822010-04-08 12:54:23 -0700101 * writing new entity data under the desired key. Deleting an entity from the data set
102 * is done by writing an entity under that key with header specifying a negative data
103 * size, and no actual entity data.
104 * <p>
105 * <b>Helper Classes</b>
106 * <p>
107 * An extensible agent based on convenient helper classes is available in
108 * {@link android.app.backup.BackupAgentHelper}. That class is particularly
109 * suited to handling of simple file or {@link android.content.SharedPreferences}
110 * backup and restore.
Michal Karpinski6aa05b82017-08-09 17:24:59 +0100111 * <p>
112 * <b>Threading</b>
113 * <p>
114 * The constructor, as well as {@link #onCreate()} and {@link #onDestroy()} lifecycle callbacks run
115 * on the main thread (UI thread) of the application that implements the BackupAgent.
116 * The data-handling callbacks:
117 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()},
118 * {@link #onFullBackup(FullBackupDataOutput)},
119 * {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()},
120 * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()},
121 * {@link #onRestoreFinished()}, and {@link #onQuotaExceeded(long, long) onQuotaExceeded()}
122 * run on binder pool threads.
Christopher Tate4e14a822010-04-08 12:54:23 -0700123 *
124 * @see android.app.backup.BackupManager
125 * @see android.app.backup.BackupAgentHelper
126 * @see android.app.backup.BackupDataInput
127 * @see android.app.backup.BackupDataOutput
Christopher Tate487529a2009-04-29 14:03:25 -0700128 */
Christopher Tate181fafa2009-05-14 11:12:14 -0700129public abstract class BackupAgent extends ContextWrapper {
Joe Onorato83248c42009-06-17 17:55:20 -0700130 private static final String TAG = "BackupAgent";
Alan Jeonf0e32ee2014-12-13 22:44:53 +0900131 private static final boolean DEBUG = false;
Joe Onorato83248c42009-06-17 17:55:20 -0700132
Christopher Tate79ec80d2011-06-24 14:58:49 -0700133 /** @hide */
Bernardo Rufino6a422d62018-08-28 13:19:20 +0100134 public static final int RESULT_SUCCESS = 0;
135 /** @hide */
136 public static final int RESULT_ERROR = -1;
137
138 /** @hide */
Christopher Tate79ec80d2011-06-24 14:58:49 -0700139 public static final int TYPE_EOF = 0;
140
141 /**
142 * During a full restore, indicates that the file system object being restored
143 * is an ordinary file.
144 */
145 public static final int TYPE_FILE = 1;
146
147 /**
148 * During a full restore, indicates that the file system object being restored
149 * is a directory.
150 */
151 public static final int TYPE_DIRECTORY = 2;
152
153 /** @hide */
154 public static final int TYPE_SYMLINK = 3;
155
Robert Berry39194c02018-01-11 13:50:56 +0000156 /**
157 * Flag for {@link BackupDataOutput#getTransportFlags()} and
158 * {@link FullBackupDataOutput#getTransportFlags()} only.
159 *
Colin Cross4da17742018-04-11 17:27:24 +0000160 * <p>The transport has client-side encryption enabled. i.e., the user's backup has been
161 * encrypted with a key known only to the device, and not to the remote storage solution. Even
162 * if an attacker had root access to the remote storage provider they should not be able to
163 * decrypt the user's backup data.
Robert Berry39194c02018-01-11 13:50:56 +0000164 */
165 public static final int FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED = 1;
166
Robert Berry98a8e242018-01-25 13:29:16 +0000167 /**
168 * Flag for {@link BackupDataOutput#getTransportFlags()} and
169 * {@link FullBackupDataOutput#getTransportFlags()} only.
170 *
171 * <p>The transport is for a device-to-device transfer. There is no third party or intermediate
172 * storage. The user's backup data is sent directly to another device over e.g., USB or WiFi.
173 */
174 public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2;
175
Bernardo Rufinoeaa78b92018-01-26 11:25:37 +0000176 /**
177 * Flag for {@link BackupDataOutput#getTransportFlags()} and
178 * {@link FullBackupDataOutput#getTransportFlags()} only.
179 *
180 * <p>Used for internal testing only. Do not check this flag in production code.
181 *
182 * @hide
183 */
184 public static final int FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED = 1 << 31;
185
Christopher Tatef85f5b22013-04-18 16:57:43 -0700186 Handler mHandler = null;
187
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +0000188 @Nullable private UserHandle mUser;
189
Christopher Tatecba59412014-04-01 10:38:29 -0700190 Handler getHandler() {
191 if (mHandler == null) {
192 mHandler = new Handler(Looper.getMainLooper());
193 }
194 return mHandler;
195 }
196
Christopher Tatef85f5b22013-04-18 16:57:43 -0700197 class SharedPrefsSynchronizer implements Runnable {
198 public final CountDownLatch mLatch = new CountDownLatch(1);
199
200 @Override
201 public void run() {
202 QueuedWork.waitToFinish();
203 mLatch.countDown();
204 }
205 };
206
207 // Syncing shared preferences deferred writes needs to happen on the main looper thread
208 private void waitForSharedPrefs() {
Christopher Tatecba59412014-04-01 10:38:29 -0700209 Handler h = getHandler();
Christopher Tatef85f5b22013-04-18 16:57:43 -0700210 final SharedPrefsSynchronizer s = new SharedPrefsSynchronizer();
Christopher Tatecba59412014-04-01 10:38:29 -0700211 h.postAtFrontOfQueue(s);
Christopher Tatef85f5b22013-04-18 16:57:43 -0700212 try {
213 s.mLatch.await();
214 } catch (InterruptedException e) { /* ignored */ }
215 }
216
217
Christopher Tate181fafa2009-05-14 11:12:14 -0700218 public BackupAgent() {
219 super(null);
220 }
Christopher Tate487529a2009-04-29 14:03:25 -0700221
Christopher Tate4e14a822010-04-08 12:54:23 -0700222 /**
223 * Provided as a convenience for agent implementations that need an opportunity
224 * to do one-time initialization before the actual backup or restore operation
225 * is begun.
226 * <p>
Christopher Tate4e14a822010-04-08 12:54:23 -0700227 */
Christopher Tate181fafa2009-05-14 11:12:14 -0700228 public void onCreate() {
229 }
230
Christopher Tate4e14a822010-04-08 12:54:23 -0700231 /**
Stefanot14bbded2018-10-11 12:07:53 +0100232 * Provided as a convenience for agent implementations that need an opportunity
233 * to do one-time initialization before the actual backup or restore operation
234 * is begun with information about the calling user.
235 * <p>
236 *
237 * @hide
238 */
239 public void onCreate(UserHandle user) {
240 onCreate();
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +0000241
242 mUser = user;
Stefanot14bbded2018-10-11 12:07:53 +0100243 }
244
245 /**
Christopher Tate4e14a822010-04-08 12:54:23 -0700246 * Provided as a convenience for agent implementations that need to do some
247 * sort of shutdown process after backup or restore is completed.
248 * <p>
249 * Agents do not need to override this method.
250 */
Christopher Tate181fafa2009-05-14 11:12:14 -0700251 public void onDestroy() {
252 }
Christopher Tate487529a2009-04-29 14:03:25 -0700253
254 /**
Kenny Root5a20ea12010-02-23 18:49:11 -0800255 * The application is being asked to write any data changed since the last
256 * time it performed a backup operation. The state data recorded during the
257 * last backup pass is provided in the <code>oldState</code> file
258 * descriptor. If <code>oldState</code> is <code>null</code>, no old state
259 * is available and the application should perform a full backup. In both
260 * cases, a representation of the final backup state after this pass should
261 * be written to the file pointed to by the file descriptor wrapped in
262 * <code>newState</code>.
Christopher Tate4e14a822010-04-08 12:54:23 -0700263 * <p>
264 * Each entity written to the {@link android.app.backup.BackupDataOutput}
265 * <code>data</code> stream will be transmitted
266 * over the current backup transport and stored in the remote data set under
267 * the key supplied as part of the entity. Writing an entity with a negative
268 * data size instructs the transport to delete whatever entity currently exists
269 * under that key from the remote data set.
Elliott Hughes34385d32014-04-28 11:11:32 -0700270 *
Kenny Root5a20ea12010-02-23 18:49:11 -0800271 * @param oldState An open, read-only ParcelFileDescriptor pointing to the
272 * last backup state provided by the application. May be
273 * <code>null</code>, in which case no prior state is being
274 * provided and the application should perform a full backup.
275 * @param data A structured wrapper around an open, read/write
Christopher Tate4e14a822010-04-08 12:54:23 -0700276 * file descriptor pointing to the backup data destination.
Kenny Root5a20ea12010-02-23 18:49:11 -0800277 * Typically the application will use backup helper classes to
278 * write to this file.
279 * @param newState An open, read/write ParcelFileDescriptor pointing to an
280 * empty file. The application should record the final backup
Christopher Tate4e14a822010-04-08 12:54:23 -0700281 * state here after writing the requested data to the <code>data</code>
282 * output stream.
Christopher Tate487529a2009-04-29 14:03:25 -0700283 */
Joe Onorato290bb012009-05-13 18:57:29 -0400284 public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
Christopher Tate2e40d112014-07-15 12:37:38 -0700285 ParcelFileDescriptor newState) throws IOException;
Kenny Root5a20ea12010-02-23 18:49:11 -0800286
Christopher Tate487529a2009-04-29 14:03:25 -0700287 /**
Kenny Root5a20ea12010-02-23 18:49:11 -0800288 * The application is being restored from backup and should replace any
289 * existing data with the contents of the backup. The backup data is
Christopher Tate4e14a822010-04-08 12:54:23 -0700290 * provided through the <code>data</code> parameter. Once
Kenny Root5a20ea12010-02-23 18:49:11 -0800291 * the restore is finished, the application should write a representation of
292 * the final state to the <code>newState</code> file descriptor.
293 * <p>
294 * The application is responsible for properly erasing its old data and
295 * replacing it with the data supplied to this method. No "clear user data"
296 * operation will be performed automatically by the operating system. The
297 * exception to this is in the case of a failed restore attempt: if
298 * onRestore() throws an exception, the OS will assume that the
299 * application's data may now be in an incoherent state, and will clear it
300 * before proceeding.
Elliott Hughes34385d32014-04-28 11:11:32 -0700301 *
Kenny Root5a20ea12010-02-23 18:49:11 -0800302 * @param data A structured wrapper around an open, read-only
Christopher Tate4e14a822010-04-08 12:54:23 -0700303 * file descriptor pointing to a full snapshot of the
304 * application's data. The application should consume every
305 * entity represented in this data stream.
Scott Mainb83a2832010-04-29 13:26:53 -0700306 * @param appVersionCode The value of the <a
307 * href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code
308 * android:versionCode}</a> manifest attribute,
309 * from the application that backed up this particular data set. This
Christopher Tate4e14a822010-04-08 12:54:23 -0700310 * makes it possible for an application's agent to distinguish among any
Kenny Root5a20ea12010-02-23 18:49:11 -0800311 * possible older data versions when asked to perform the restore
312 * operation.
313 * @param newState An open, read/write ParcelFileDescriptor pointing to an
314 * empty file. The application should record the final backup
Christopher Tate4e14a822010-04-08 12:54:23 -0700315 * state here after restoring its data from the <code>data</code> stream.
Christopher Tate4a627c72011-04-01 14:43:32 -0700316 * When a full-backup dataset is being restored, this will be <code>null</code>.
Christopher Tate487529a2009-04-29 14:03:25 -0700317 */
Christopher Tate5cbbf562009-06-22 16:44:51 -0700318 public abstract void onRestore(BackupDataInput data, int appVersionCode,
Christopher Tate2e40d112014-07-15 12:37:38 -0700319 ParcelFileDescriptor newState) throws IOException;
Christopher Tate487529a2009-04-29 14:03:25 -0700320
Christopher Tate4a627c72011-04-01 14:43:32 -0700321 /**
Dianne Hackborn3accca02013-09-20 09:32:11 -0700322 * New version of {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}
323 * that handles a long app version code. Default implementation casts the version code to
324 * an int and calls {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}.
325 */
326 public void onRestore(BackupDataInput data, long appVersionCode,
327 ParcelFileDescriptor newState)
328 throws IOException {
329 onRestore(data, (int) appVersionCode, newState);
330 }
331
332 /**
Ruslan Tkhakokhovd7f5fdb42019-12-10 11:15:45 +0000333 * New version of {@link #onRestore(BackupDataInput, long, android.os.ParcelFileDescriptor)}
334 * that has a list of keys to be excluded from the restore. Key/value pairs for which the key
335 * is present in {@code excludedKeys} have already been excluded from the restore data by the
336 * system. The list is passed to the agent to make it aware of what data has been removed (in
337 * case it has any application-level consequences) as well as the data that should be removed
338 * by the agent itself.
339 *
340 * The default implementation calls {@link #onRestore(BackupDataInput, long,
341 * android.os.ParcelFileDescriptor)}.
342 *
343 * @param excludedKeys A list of keys to be excluded from restore.
344 *
345 * @hide
346 */
347 public void onRestore(BackupDataInput data, long appVersionCode,
348 ParcelFileDescriptor newState,
349 Set<String> excludedKeys)
350 throws IOException {
351 onRestore(data, appVersionCode, newState);
352 }
353
354 /**
Christopher Tatea7835b62014-07-11 17:25:57 -0700355 * The application is having its entire file system contents backed up. {@code data}
356 * points to the backup destination, and the app has the opportunity to choose which
357 * files are to be stored. To commit a file as part of the backup, call the
358 * {@link #fullBackupFile(File, FullBackupDataOutput)} helper method. After all file
359 * data is written to the output, the agent returns from this method and the backup
360 * operation concludes.
361 *
362 * <p>Certain parts of the app's data are never backed up even if the app explicitly
363 * sends them to the output:
364 *
365 * <ul>
366 * <li>The contents of the {@link #getCacheDir()} directory</li>
Christopher Tatea8a739f2015-03-05 18:31:38 -0800367 * <li>The contents of the {@link #getCodeCacheDir()} directory</li>
Christopher Tatea7835b62014-07-11 17:25:57 -0700368 * <li>The contents of the {@link #getNoBackupFilesDir()} directory</li>
369 * <li>The contents of the app's shared library directory</li>
370 * </ul>
371 *
372 * <p>The default implementation of this method backs up the entirety of the
373 * application's "owned" file system trees to the output other than the few exceptions
374 * listed above. Apps only need to override this method if they need to impose special
375 * limitations on which files are being stored beyond the control that
376 * {@link #getNoBackupFilesDir()} offers.
Matthew Williams303650c2015-04-17 18:22:51 -0700377 * Alternatively they can provide an xml resource to specify what data to include or exclude.
378 *
Christopher Tatea7835b62014-07-11 17:25:57 -0700379 *
380 * @param data A structured wrapper pointing to the backup destination.
381 * @throws IOException
382 *
383 * @see Context#getNoBackupFilesDir()
384 * @see #fullBackupFile(File, FullBackupDataOutput)
385 * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
Christopher Tate75a99702011-05-18 16:28:19 -0700386 */
Christopher Tate79ec80d2011-06-24 14:58:49 -0700387 public void onFullBackup(FullBackupDataOutput data) throws IOException {
Matthew Williams303650c2015-04-17 18:22:51 -0700388 FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this);
389 if (!backupScheme.isFullBackupContentEnabled()) {
390 return;
391 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700392
Michal Karpinskib5e09312018-02-19 13:55:23 +0000393 Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap;
394 ArraySet<PathWithRequiredFlags> manifestExcludeSet;
Matthew Williams303650c2015-04-17 18:22:51 -0700395 try {
396 manifestIncludeMap =
397 backupScheme.maybeParseAndGetCanonicalIncludePaths();
398 manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths();
399 } catch (IOException | XmlPullParserException e) {
400 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
401 Log.v(FullBackup.TAG_XML_PARSER,
402 "Exception trying to parse fullBackupContent xml file!"
403 + " Aborting full backup.", e);
404 }
405 return;
406 }
407
408 final String packageName = getPackageName();
409 final ApplicationInfo appInfo = getApplicationInfo();
410
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700411 // System apps have control over where their default storage context
412 // is pointed, so we're always explicit when building paths.
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600413 final Context ceContext = createCredentialProtectedStorageContext();
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700414 final String rootDir = ceContext.getDataDir().getCanonicalPath();
415 final String filesDir = ceContext.getFilesDir().getCanonicalPath();
416 final String noBackupDir = ceContext.getNoBackupFilesDir().getCanonicalPath();
417 final String databaseDir = ceContext.getDatabasePath("foo").getParentFile()
418 .getCanonicalPath();
419 final String sharedPrefsDir = ceContext.getSharedPreferencesPath("foo").getParentFile()
420 .getCanonicalPath();
421 final String cacheDir = ceContext.getCacheDir().getCanonicalPath();
422 final String codeCacheDir = ceContext.getCodeCacheDir().getCanonicalPath();
423
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600424 final Context deContext = createDeviceProtectedStorageContext();
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700425 final String deviceRootDir = deContext.getDataDir().getCanonicalPath();
426 final String deviceFilesDir = deContext.getFilesDir().getCanonicalPath();
427 final String deviceNoBackupDir = deContext.getNoBackupFilesDir().getCanonicalPath();
428 final String deviceDatabaseDir = deContext.getDatabasePath("foo").getParentFile()
429 .getCanonicalPath();
430 final String deviceSharedPrefsDir = deContext.getSharedPreferencesPath("foo")
431 .getParentFile().getCanonicalPath();
432 final String deviceCacheDir = deContext.getCacheDir().getCanonicalPath();
433 final String deviceCodeCacheDir = deContext.getCodeCacheDir().getCanonicalPath();
434
435 final String libDir = (appInfo.nativeLibraryDir != null)
Christopher Tate2efd2db2011-07-19 16:32:49 -0700436 ? new File(appInfo.nativeLibraryDir).getCanonicalPath()
Christopher Tate79ec80d2011-06-24 14:58:49 -0700437 : null;
438
Matthew Williams303650c2015-04-17 18:22:51 -0700439 // Maintain a set of excluded directories so that as we traverse the tree we know we're not
440 // going places we don't expect, and so the manifest includes can't take precedence over
441 // what the framework decides is not to be included.
442 final ArraySet<String> traversalExcludeSet = new ArraySet<String>();
Christopher Tate79ec80d2011-06-24 14:58:49 -0700443
Matthew Williams303650c2015-04-17 18:22:51 -0700444 // Add the directories we always exclude.
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700445 traversalExcludeSet.add(filesDir);
446 traversalExcludeSet.add(noBackupDir);
447 traversalExcludeSet.add(databaseDir);
448 traversalExcludeSet.add(sharedPrefsDir);
Matthew Williams303650c2015-04-17 18:22:51 -0700449 traversalExcludeSet.add(cacheDir);
450 traversalExcludeSet.add(codeCacheDir);
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700451
452 traversalExcludeSet.add(deviceFilesDir);
453 traversalExcludeSet.add(deviceNoBackupDir);
454 traversalExcludeSet.add(deviceDatabaseDir);
455 traversalExcludeSet.add(deviceSharedPrefsDir);
456 traversalExcludeSet.add(deviceCacheDir);
457 traversalExcludeSet.add(deviceCodeCacheDir);
458
Christopher Tate79ec80d2011-06-24 14:58:49 -0700459 if (libDir != null) {
Matthew Williams303650c2015-04-17 18:22:51 -0700460 traversalExcludeSet.add(libDir);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700461 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700462
Matthew Williams303650c2015-04-17 18:22:51 -0700463 // Root dir first.
464 applyXmlFiltersAndDoFullBackupForDomain(
465 packageName, FullBackup.ROOT_TREE_TOKEN, manifestIncludeMap,
466 manifestExcludeSet, traversalExcludeSet, data);
467 traversalExcludeSet.add(rootDir);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700468
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700469 applyXmlFiltersAndDoFullBackupForDomain(
470 packageName, FullBackup.DEVICE_ROOT_TREE_TOKEN, manifestIncludeMap,
471 manifestExcludeSet, traversalExcludeSet, data);
472 traversalExcludeSet.add(deviceRootDir);
473
Matthew Williams303650c2015-04-17 18:22:51 -0700474 // Data dir next.
475 traversalExcludeSet.remove(filesDir);
476 applyXmlFiltersAndDoFullBackupForDomain(
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700477 packageName, FullBackup.FILES_TREE_TOKEN, manifestIncludeMap,
Matthew Williams303650c2015-04-17 18:22:51 -0700478 manifestExcludeSet, traversalExcludeSet, data);
479 traversalExcludeSet.add(filesDir);
480
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700481 traversalExcludeSet.remove(deviceFilesDir);
482 applyXmlFiltersAndDoFullBackupForDomain(
483 packageName, FullBackup.DEVICE_FILES_TREE_TOKEN, manifestIncludeMap,
484 manifestExcludeSet, traversalExcludeSet, data);
485 traversalExcludeSet.add(deviceFilesDir);
486
Matthew Williams303650c2015-04-17 18:22:51 -0700487 // Database directory.
488 traversalExcludeSet.remove(databaseDir);
489 applyXmlFiltersAndDoFullBackupForDomain(
490 packageName, FullBackup.DATABASE_TREE_TOKEN, manifestIncludeMap,
491 manifestExcludeSet, traversalExcludeSet, data);
492 traversalExcludeSet.add(databaseDir);
493
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700494 traversalExcludeSet.remove(deviceDatabaseDir);
495 applyXmlFiltersAndDoFullBackupForDomain(
496 packageName, FullBackup.DEVICE_DATABASE_TREE_TOKEN, manifestIncludeMap,
497 manifestExcludeSet, traversalExcludeSet, data);
498 traversalExcludeSet.add(deviceDatabaseDir);
499
Matthew Williams303650c2015-04-17 18:22:51 -0700500 // SharedPrefs.
501 traversalExcludeSet.remove(sharedPrefsDir);
502 applyXmlFiltersAndDoFullBackupForDomain(
503 packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, manifestIncludeMap,
504 manifestExcludeSet, traversalExcludeSet, data);
505 traversalExcludeSet.add(sharedPrefsDir);
Christopher Tate416c39e2013-02-14 16:55:46 -0800506
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700507 traversalExcludeSet.remove(deviceSharedPrefsDir);
508 applyXmlFiltersAndDoFullBackupForDomain(
509 packageName, FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN, manifestIncludeMap,
510 manifestExcludeSet, traversalExcludeSet, data);
511 traversalExcludeSet.add(deviceSharedPrefsDir);
512
Christopher Tate416c39e2013-02-14 16:55:46 -0800513 // getExternalFilesDir() location associated with this app. Technically there should
514 // not be any files here if the app does not properly have permission to access
515 // external storage, but edge cases happen. fullBackupFileTree() catches
Christopher Tate5cb5c332013-02-21 14:32:12 -0800516 // IOExceptions and similar, and treats them as non-fatal, so we rely on that; and
517 // we know a priori that processes running as the system UID are not permitted to
518 // access external storage, so we check for that as well to avoid nastygrams in
519 // the log.
520 if (Process.myUid() != Process.SYSTEM_UID) {
521 File efLocation = getExternalFilesDir(null);
522 if (efLocation != null) {
Matthew Williams303650c2015-04-17 18:22:51 -0700523 applyXmlFiltersAndDoFullBackupForDomain(
524 packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN, manifestIncludeMap,
525 manifestExcludeSet, traversalExcludeSet, data);
526 }
527
528 }
529 }
530
531 /**
Christopher Tated43879c2016-04-04 12:54:43 -0700532 * Notification that the application's current backup operation causes it to exceed
533 * the maximum size permitted by the transport. The ongoing backup operation is
534 * halted and rolled back: any data that had been stored by a previous backup operation
535 * is still intact. Typically the quota-exceeded state will be detected before any data
536 * is actually transmitted over the network.
Sergey Poromov872d3b62016-01-12 15:48:08 +0100537 *
Christopher Tated43879c2016-04-04 12:54:43 -0700538 * <p>The {@code quotaBytes} value is the total data size currently permitted for this
539 * application. If desired, the application can use this as a hint for determining
540 * how much data to store. For example, a messaging application might choose to
541 * store only the newest messages, dropping enough older content to stay under
542 * the quota.
543 *
544 * <p class="note">Note that the maximum quota for the application can change over
545 * time. In particular, in the future the quota may grow. Applications that adapt
546 * to the quota when deciding what data to store should be aware of this and implement
547 * their data storage mechanisms in a way that can take advantage of additional
548 * quota.
549 *
550 * @param backupDataBytes The amount of data measured while initializing the backup
551 * operation, if the total exceeds the app's alloted quota. If initial measurement
552 * suggested that the data would fit but then too much data was actually submitted
553 * as part of the operation, then this value is the amount of data that had been
554 * streamed into the transport at the time the quota was reached.
555 * @param quotaBytes The maximum data size that the transport currently permits
556 * this application to store as a backup.
Sergey Poromov872d3b62016-01-12 15:48:08 +0100557 */
558 public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
559 }
560
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +0000561 private int getBackupUserId() {
562 return mUser == null ? super.getUserId() : mUser.getIdentifier();
563 }
564
Sergey Poromov872d3b62016-01-12 15:48:08 +0100565 /**
Matthew Williams303650c2015-04-17 18:22:51 -0700566 * Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>.
567 * If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path
Michal Karpinskib5e09312018-02-19 13:55:23 +0000568 * is a directory, but only if all the required flags of the include rule are satisfied by
569 * the transport.
Matthew Williams303650c2015-04-17 18:22:51 -0700570 */
571 private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken,
Michal Karpinskib5e09312018-02-19 13:55:23 +0000572 Map<String, Set<PathWithRequiredFlags>> includeMap,
573 ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet,
574 FullBackupDataOutput data) throws IOException {
Matthew Williams303650c2015-04-17 18:22:51 -0700575 if (includeMap == null || includeMap.size() == 0) {
576 // Do entire sub-tree for the provided token.
577 fullBackupFileTree(packageName, domainToken,
578 FullBackup.getBackupScheme(this).tokenToDirectoryPath(domainToken),
579 filterSet, traversalExcludeSet, data);
580 } else if (includeMap.get(domainToken) != null) {
581 // This will be null if the xml parsing didn't yield any rules for
582 // this domain (there may still be rules for other domains).
Michal Karpinskib5e09312018-02-19 13:55:23 +0000583 for (PathWithRequiredFlags includeFile : includeMap.get(domainToken)) {
584 if (areIncludeRequiredTransportFlagsSatisfied(includeFile.getRequiredFlags(),
585 data.getTransportFlags())) {
586 fullBackupFileTree(packageName, domainToken, includeFile.getPath(), filterSet,
587 traversalExcludeSet, data);
588 }
Christopher Tate5cb5c332013-02-21 14:32:12 -0800589 }
590 }
Christopher Tate75a99702011-05-18 16:28:19 -0700591 }
592
Michal Karpinskib5e09312018-02-19 13:55:23 +0000593 private boolean areIncludeRequiredTransportFlagsSatisfied(int includeFlags,
594 int transportFlags) {
595 // all bits that are set in includeFlags must also be set in transportFlags
596 return (transportFlags & includeFlags) == includeFlags;
597 }
598
Christopher Tate75a99702011-05-18 16:28:19 -0700599 /**
Christopher Tate79ec80d2011-06-24 14:58:49 -0700600 * Write an entire file as part of a full-backup operation. The file's contents
601 * will be delivered to the backup destination along with the metadata necessary
602 * to place it with the proper location and permissions on the device where the
603 * data is restored.
Christopher Tate79ec80d2011-06-24 14:58:49 -0700604 *
Christopher Tatec5be8f82016-04-25 14:41:50 -0700605 * <p class="note">Attempting to back up files in directories that are ignored by
606 * the backup system will have no effect. For example, if the app calls this method
607 * with a file inside the {@link #getNoBackupFilesDir()} directory, it will be ignored.
Elliot Waite54de7742017-01-11 15:30:35 -0800608 * See {@link #onFullBackup(FullBackupDataOutput)} for details on what directories
Christopher Tatec5be8f82016-04-25 14:41:50 -0700609 * are excluded from backups.
Christopher Tatea7835b62014-07-11 17:25:57 -0700610 *
Christopher Tate79ec80d2011-06-24 14:58:49 -0700611 * @param file The file to be backed up. The file must exist and be readable by
612 * the caller.
613 * @param output The destination to which the backed-up file data will be sent.
Christopher Tate4a627c72011-04-01 14:43:32 -0700614 */
Christopher Tate79ec80d2011-06-24 14:58:49 -0700615 public final void fullBackupFile(File file, FullBackupDataOutput output) {
616 // Look up where all of our various well-defined dir trees live on this device
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700617 final String rootDir;
618 final String filesDir;
619 final String nbFilesDir;
620 final String dbDir;
621 final String spDir;
622 final String cacheDir;
623 final String codeCacheDir;
624 final String deviceRootDir;
625 final String deviceFilesDir;
626 final String deviceNbFilesDir;
627 final String deviceDbDir;
628 final String deviceSpDir;
629 final String deviceCacheDir;
630 final String deviceCodeCacheDir;
631 final String libDir;
632
Christopher Tate5cb5c332013-02-21 14:32:12 -0800633 String efDir = null;
Christopher Tate2efd2db2011-07-19 16:32:49 -0700634 String filePath;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700635
636 ApplicationInfo appInfo = getApplicationInfo();
637
Christopher Tate2efd2db2011-07-19 16:32:49 -0700638 try {
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700639 // System apps have control over where their default storage context
640 // is pointed, so we're always explicit when building paths.
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600641 final Context ceContext = createCredentialProtectedStorageContext();
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700642 rootDir = ceContext.getDataDir().getCanonicalPath();
643 filesDir = ceContext.getFilesDir().getCanonicalPath();
644 nbFilesDir = ceContext.getNoBackupFilesDir().getCanonicalPath();
645 dbDir = ceContext.getDatabasePath("foo").getParentFile().getCanonicalPath();
646 spDir = ceContext.getSharedPreferencesPath("foo").getParentFile().getCanonicalPath();
647 cacheDir = ceContext.getCacheDir().getCanonicalPath();
648 codeCacheDir = ceContext.getCodeCacheDir().getCanonicalPath();
649
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600650 final Context deContext = createDeviceProtectedStorageContext();
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700651 deviceRootDir = deContext.getDataDir().getCanonicalPath();
652 deviceFilesDir = deContext.getFilesDir().getCanonicalPath();
653 deviceNbFilesDir = deContext.getNoBackupFilesDir().getCanonicalPath();
654 deviceDbDir = deContext.getDatabasePath("foo").getParentFile().getCanonicalPath();
655 deviceSpDir = deContext.getSharedPreferencesPath("foo").getParentFile()
656 .getCanonicalPath();
657 deviceCacheDir = deContext.getCacheDir().getCanonicalPath();
658 deviceCodeCacheDir = deContext.getCodeCacheDir().getCanonicalPath();
659
Christopher Tate2efd2db2011-07-19 16:32:49 -0700660 libDir = (appInfo.nativeLibraryDir == null)
661 ? null
662 : new File(appInfo.nativeLibraryDir).getCanonicalPath();
Christopher Tate5cb5c332013-02-21 14:32:12 -0800663
664 // may or may not have external files access to attempt backup/restore there
665 if (Process.myUid() != Process.SYSTEM_UID) {
666 File efLocation = getExternalFilesDir(null);
667 if (efLocation != null) {
668 efDir = efLocation.getCanonicalPath();
669 }
670 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700671
Christopher Tate2efd2db2011-07-19 16:32:49 -0700672 // Now figure out which well-defined tree the file is placed in, working from
Christopher Tatea8a739f2015-03-05 18:31:38 -0800673 // most to least specific. We also specifically exclude the lib, cache,
674 // and code_cache dirs.
Christopher Tate2efd2db2011-07-19 16:32:49 -0700675 filePath = file.getCanonicalPath();
676 } catch (IOException e) {
677 Log.w(TAG, "Unable to obtain canonical paths");
678 return;
679 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700680
Christopher Tatea7835b62014-07-11 17:25:57 -0700681 if (filePath.startsWith(cacheDir)
Christopher Tatea8a739f2015-03-05 18:31:38 -0800682 || filePath.startsWith(codeCacheDir)
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700683 || filePath.startsWith(nbFilesDir)
684 || filePath.startsWith(deviceCacheDir)
685 || filePath.startsWith(deviceCodeCacheDir)
686 || filePath.startsWith(deviceNbFilesDir)
687 || filePath.startsWith(libDir)) {
Christopher Tatea8a739f2015-03-05 18:31:38 -0800688 Log.w(TAG, "lib, cache, code_cache, and no_backup files are not backed up");
Christopher Tate79ec80d2011-06-24 14:58:49 -0700689 return;
690 }
691
692 final String domain;
693 String rootpath = null;
694 if (filePath.startsWith(dbDir)) {
695 domain = FullBackup.DATABASE_TREE_TOKEN;
696 rootpath = dbDir;
697 } else if (filePath.startsWith(spDir)) {
698 domain = FullBackup.SHAREDPREFS_TREE_TOKEN;
699 rootpath = spDir;
700 } else if (filePath.startsWith(filesDir)) {
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700701 domain = FullBackup.FILES_TREE_TOKEN;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700702 rootpath = filesDir;
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700703 } else if (filePath.startsWith(rootDir)) {
Christopher Tate79ec80d2011-06-24 14:58:49 -0700704 domain = FullBackup.ROOT_TREE_TOKEN;
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700705 rootpath = rootDir;
706 } else if (filePath.startsWith(deviceDbDir)) {
707 domain = FullBackup.DEVICE_DATABASE_TREE_TOKEN;
708 rootpath = deviceDbDir;
709 } else if (filePath.startsWith(deviceSpDir)) {
710 domain = FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN;
711 rootpath = deviceSpDir;
712 } else if (filePath.startsWith(deviceFilesDir)) {
713 domain = FullBackup.DEVICE_FILES_TREE_TOKEN;
714 rootpath = deviceFilesDir;
715 } else if (filePath.startsWith(deviceRootDir)) {
716 domain = FullBackup.DEVICE_ROOT_TREE_TOKEN;
717 rootpath = deviceRootDir;
Christopher Tate5cb5c332013-02-21 14:32:12 -0800718 } else if ((efDir != null) && filePath.startsWith(efDir)) {
Christopher Tate416c39e2013-02-14 16:55:46 -0800719 domain = FullBackup.MANAGED_EXTERNAL_TREE_TOKEN;
720 rootpath = efDir;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700721 } else {
722 Log.w(TAG, "File " + filePath + " is in an unsupported location; skipping");
723 return;
724 }
725
726 // And now that we know where it lives, semantically, back it up appropriately
Christopher Tate11ae7682015-03-24 18:48:10 -0700727 // In the measurement case, backupToTar() updates the size in output and returns
728 // without transmitting any file data.
729 if (DEBUG) Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
Christopher Tate79ec80d2011-06-24 14:58:49 -0700730 + " rootpath=" + rootpath);
Matthew Williams303650c2015-04-17 18:22:51 -0700731
Christopher Tate11ae7682015-03-24 18:48:10 -0700732 FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath, output);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700733 }
734
735 /**
736 * Scan the dir tree (if it actually exists) and process each entry we find. If the
Matthew Williams303650c2015-04-17 18:22:51 -0700737 * 'excludes' parameters are non-null, they are consulted each time a new file system entity
Christopher Tate79ec80d2011-06-24 14:58:49 -0700738 * is visited to see whether that entity (and its subtree, if appropriate) should be
739 * omitted from the backup process.
740 *
Matthew Williams303650c2015-04-17 18:22:51 -0700741 * @param systemExcludes An optional list of excludes.
Christopher Tate79ec80d2011-06-24 14:58:49 -0700742 * @hide
743 */
Matthew Williams303650c2015-04-17 18:22:51 -0700744 protected final void fullBackupFileTree(String packageName, String domain, String startingPath,
Michal Karpinskib5e09312018-02-19 13:55:23 +0000745 ArraySet<PathWithRequiredFlags> manifestExcludes,
Matthew Williams303650c2015-04-17 18:22:51 -0700746 ArraySet<String> systemExcludes,
747 FullBackupDataOutput output) {
748 // Pull out the domain and set it aside to use when making the tarball.
749 String domainPath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
750 if (domainPath == null) {
751 // Should never happen.
752 return;
753 }
754
755 File rootFile = new File(startingPath);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700756 if (rootFile.exists()) {
757 LinkedList<File> scanQueue = new LinkedList<File>();
758 scanQueue.add(rootFile);
759
760 while (scanQueue.size() > 0) {
761 File file = scanQueue.remove(0);
Christopher Tate2efd2db2011-07-19 16:32:49 -0700762 String filePath;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700763 try {
Christopher Tateda2018e2016-10-13 12:12:29 -0700764 // Ignore things that aren't "real" files or dirs
Christopher Tate85192a12015-09-24 10:21:54 -0700765 StructStat stat = Os.lstat(file.getPath());
Christopher Tateda2018e2016-10-13 12:12:29 -0700766 if (!OsConstants.S_ISREG(stat.st_mode)
767 && !OsConstants.S_ISDIR(stat.st_mode)) {
768 if (DEBUG) Log.i(TAG, "Not a file/dir (skipping)!: " + file);
Christopher Tate85192a12015-09-24 10:21:54 -0700769 continue;
770 }
771
772 // For all other verification, look at the canonicalized path
Christopher Tate2efd2db2011-07-19 16:32:49 -0700773 filePath = file.getCanonicalPath();
774
775 // prune this subtree?
Michal Karpinskib5e09312018-02-19 13:55:23 +0000776 if (manifestExcludes != null
777 && manifestExcludesContainFilePath(manifestExcludes, filePath)) {
Matthew Williams303650c2015-04-17 18:22:51 -0700778 continue;
779 }
780 if (systemExcludes != null && systemExcludes.contains(filePath)) {
Christopher Tate2efd2db2011-07-19 16:32:49 -0700781 continue;
782 }
783
784 // If it's a directory, enqueue its contents for scanning.
Christopher Tate85192a12015-09-24 10:21:54 -0700785 if (OsConstants.S_ISDIR(stat.st_mode)) {
Christopher Tate79ec80d2011-06-24 14:58:49 -0700786 File[] contents = file.listFiles();
787 if (contents != null) {
788 for (File entry : contents) {
789 scanQueue.add(0, entry);
790 }
791 }
792 }
Christopher Tate2efd2db2011-07-19 16:32:49 -0700793 } catch (IOException e) {
794 if (DEBUG) Log.w(TAG, "Error canonicalizing path of " + file);
Matthew Williams303650c2015-04-17 18:22:51 -0700795 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
796 Log.v(FullBackup.TAG_XML_PARSER, "Error canonicalizing path of " + file);
797 }
Christopher Tate2efd2db2011-07-19 16:32:49 -0700798 continue;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700799 } catch (ErrnoException e) {
800 if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e);
Matthew Williams303650c2015-04-17 18:22:51 -0700801 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
802 Log.v(FullBackup.TAG_XML_PARSER, "Error scanning file " + file + " : " + e);
803 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700804 continue;
805 }
806
Christopher Tate11ae7682015-03-24 18:48:10 -0700807 // Finally, back this file up (or measure it) before proceeding
Matthew Williams303650c2015-04-17 18:22:51 -0700808 FullBackup.backupToTar(packageName, domain, null, domainPath, filePath, output);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700809 }
810 }
811 }
812
Michal Karpinskib5e09312018-02-19 13:55:23 +0000813 private boolean manifestExcludesContainFilePath(
814 ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath) {
815 for (PathWithRequiredFlags exclude : manifestExcludes) {
816 String excludePath = exclude.getPath();
817 if (excludePath != null && excludePath.equals(filePath)) {
818 return true;
819 }
820 }
821 return false;
822 }
823
Christopher Tate79ec80d2011-06-24 14:58:49 -0700824 /**
825 * Handle the data delivered via the given file descriptor during a full restore
826 * operation. The agent is given the path to the file's original location as well
827 * as its size and metadata.
828 * <p>
829 * The file descriptor can only be read for {@code size} bytes; attempting to read
830 * more data has undefined behavior.
831 * <p>
832 * The default implementation creates the destination file/directory and populates it
833 * with the data from the file descriptor, then sets the file's access mode and
834 * modification time to match the restore arguments.
835 *
836 * @param data A read-only file descriptor from which the agent can read {@code size}
837 * bytes of file data.
838 * @param size The number of bytes of file content to be restored to the given
839 * destination. If the file system object being restored is a directory, {@code size}
840 * will be zero.
841 * @param destination The File on disk to be restored with the given data.
842 * @param type The kind of file system object being restored. This will be either
843 * {@link BackupAgent#TYPE_FILE} or {@link BackupAgent#TYPE_DIRECTORY}.
844 * @param mode The access mode to be assigned to the destination after its data is
845 * written. This is in the standard format used by {@code chmod()}.
846 * @param mtime The modification time of the file when it was backed up, suitable to
847 * be assigned to the file after its data is written.
848 * @throws IOException
849 */
850 public void onRestoreFile(ParcelFileDescriptor data, long size,
851 File destination, int type, long mode, long mtime)
852 throws IOException {
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700853
854 final boolean accept = isFileEligibleForRestore(destination);
855 // If we don't accept the file, consume the bytes from the pipe anyway.
856 FullBackup.restoreFile(data, size, type, mode, mtime, accept ? destination : null);
857 }
858
859 private boolean isFileEligibleForRestore(File destination) throws IOException {
Matthew Williams303650c2015-04-17 18:22:51 -0700860 FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this);
861 if (!bs.isFullBackupContentEnabled()) {
862 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
863 Log.v(FullBackup.TAG_XML_PARSER,
864 "onRestoreFile \"" + destination.getCanonicalPath()
865 + "\" : fullBackupContent not enabled for " + getPackageName());
866 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700867 return false;
Matthew Williams303650c2015-04-17 18:22:51 -0700868 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700869
Michal Karpinskib5e09312018-02-19 13:55:23 +0000870 Map<String, Set<PathWithRequiredFlags>> includes = null;
871 ArraySet<PathWithRequiredFlags> excludes = null;
Matthew Williams303650c2015-04-17 18:22:51 -0700872 final String destinationCanonicalPath = destination.getCanonicalPath();
873 try {
874 includes = bs.maybeParseAndGetCanonicalIncludePaths();
875 excludes = bs.maybeParseAndGetCanonicalExcludePaths();
876 } catch (XmlPullParserException e) {
877 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
878 Log.v(FullBackup.TAG_XML_PARSER,
879 "onRestoreFile \"" + destinationCanonicalPath
880 + "\" : Exception trying to parse fullBackupContent xml file!"
881 + " Aborting onRestoreFile.", e);
882 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700883 return false;
Matthew Williams303650c2015-04-17 18:22:51 -0700884 }
885
886 if (excludes != null &&
Bernardo Rufino62863822018-06-27 12:50:44 +0100887 BackupUtils.isFileSpecifiedInPathList(destination, excludes)) {
Matthew Williams303650c2015-04-17 18:22:51 -0700888 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
889 Log.v(FullBackup.TAG_XML_PARSER,
890 "onRestoreFile: \"" + destinationCanonicalPath + "\": listed in"
891 + " excludes; skipping.");
892 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700893 return false;
Matthew Williams303650c2015-04-17 18:22:51 -0700894 }
895
896 if (includes != null && !includes.isEmpty()) {
897 // Rather than figure out the <include/> domain based on the path (a lot of code, and
898 // it's a small list), we'll go through and look for it.
899 boolean explicitlyIncluded = false;
Michal Karpinskib5e09312018-02-19 13:55:23 +0000900 for (Set<PathWithRequiredFlags> domainIncludes : includes.values()) {
Bernardo Rufino62863822018-06-27 12:50:44 +0100901 explicitlyIncluded |=
902 BackupUtils.isFileSpecifiedInPathList(destination, domainIncludes);
Matthew Williams303650c2015-04-17 18:22:51 -0700903 if (explicitlyIncluded) {
904 break;
905 }
906 }
907 if (!explicitlyIncluded) {
908 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
909 Log.v(FullBackup.TAG_XML_PARSER,
910 "onRestoreFile: Trying to restore \""
911 + destinationCanonicalPath + "\" but it isn't specified"
912 + " in the included files; skipping.");
913 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700914 return false;
Matthew Williams303650c2015-04-17 18:22:51 -0700915 }
916 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700917 return true;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700918 }
919
920 /**
921 * Only specialized platform agents should overload this entry point to support
922 * restores to crazy non-app locations.
923 * @hide
924 */
925 protected void onRestoreFile(ParcelFileDescriptor data, long size,
926 int type, String domain, String path, long mode, long mtime)
927 throws IOException {
928 String basePath = null;
929
930 if (DEBUG) Log.d(TAG, "onRestoreFile() size=" + size + " type=" + type
931 + " domain=" + domain + " relpath=" + path + " mode=" + mode
932 + " mtime=" + mtime);
933
Matthew Williams303650c2015-04-17 18:22:51 -0700934 basePath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
935 if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
936 mode = -1; // < 0 is a token to skip attempting a chmod()
Christopher Tate79ec80d2011-06-24 14:58:49 -0700937 }
938
939 // Now that we've figured out where the data goes, send it on its way
940 if (basePath != null) {
Christopher Tate73237652013-03-25 10:06:34 -0700941 // Canonicalize the nominal path and verify that it lies within the stated domain
Christopher Tate79ec80d2011-06-24 14:58:49 -0700942 File outFile = new File(basePath, path);
Christopher Tate73237652013-03-25 10:06:34 -0700943 String outPath = outFile.getCanonicalPath();
944 if (outPath.startsWith(basePath + File.separatorChar)) {
945 if (DEBUG) Log.i(TAG, "[" + domain + " : " + path + "] mapped to " + outPath);
946 onRestoreFile(data, size, outFile, type, mode, mtime);
947 return;
948 } else {
949 // Attempt to restore to a path outside the file's nominal domain.
950 if (DEBUG) {
951 Log.e(TAG, "Cross-domain restore attempt: " + outPath);
952 }
953 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700954 }
Christopher Tate73237652013-03-25 10:06:34 -0700955
956 // Not a supported output location, or bad path: we need to consume the data
957 // anyway, so just use the default "copy the data out" implementation
958 // with a null destination.
959 if (DEBUG) Log.i(TAG, "[ skipping file " + path + "]");
960 FullBackup.restoreFile(data, size, type, mode, mtime, null);
Christopher Tate4a627c72011-04-01 14:43:32 -0700961 }
Christopher Tate487529a2009-04-29 14:03:25 -0700962
Christopher Tate2e40d112014-07-15 12:37:38 -0700963 /**
964 * The application's restore operation has completed. This method is called after
965 * all available data has been delivered to the application for restore (via either
966 * the {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} or
967 * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()}
968 * callbacks). This provides the app with a stable end-of-restore opportunity to
969 * perform any appropriate post-processing on the data that was just delivered.
970 *
971 * @see #onRestore(BackupDataInput, int, ParcelFileDescriptor)
972 * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
973 */
974 public void onRestoreFinished() {
975 }
976
Christopher Tate487529a2009-04-29 14:03:25 -0700977 // ----- Core implementation -----
Christopher Tate44a27902010-01-27 17:15:49 -0800978
979 /** @hide */
980 public final IBinder onBind() {
Christopher Tate181fafa2009-05-14 11:12:14 -0700981 return mBinder;
Christopher Tate487529a2009-04-29 14:03:25 -0700982 }
983
984 private final IBinder mBinder = new BackupServiceBinder().asBinder();
985
Christopher Tate181fafa2009-05-14 11:12:14 -0700986 /** @hide */
987 public void attach(Context context) {
988 attachBaseContext(context);
989 }
990
Christopher Tate487529a2009-04-29 14:03:25 -0700991 // ----- IBackupService binder interface -----
Christopher Tate181fafa2009-05-14 11:12:14 -0700992 private class BackupServiceBinder extends IBackupAgent.Stub {
993 private static final String TAG = "BackupServiceBinder";
994
Christopher Tate75a99702011-05-18 16:28:19 -0700995 @Override
Bernardo Rufino2d87f452018-06-22 11:47:49 +0100996 public void doBackup(
997 ParcelFileDescriptor oldState,
Christopher Tate22b87872009-05-04 16:41:53 -0700998 ParcelFileDescriptor data,
Christopher Tate44a27902010-01-27 17:15:49 -0800999 ParcelFileDescriptor newState,
Bernardo Rufino2d87f452018-06-22 11:47:49 +01001000 long quotaBytes,
1001 IBackupCallback callbackBinder,
1002 int transportFlags) throws RemoteException {
Christopher Tate19024922010-01-22 16:39:53 -08001003 // Ensure that we're running with the app's normal permission level
Christopher Tate44a27902010-01-27 17:15:49 -08001004 long ident = Binder.clearCallingIdentity();
Christopher Tate19024922010-01-22 16:39:53 -08001005
Christopher Tate436344a2009-09-30 16:17:37 -07001006 if (DEBUG) Log.v(TAG, "doBackup() invoked");
Robert Berry39194c02018-01-11 13:50:56 +00001007 BackupDataOutput output = new BackupDataOutput(
1008 data.getFileDescriptor(), quotaBytes, transportFlags);
Christopher Tate4a627c72011-04-01 14:43:32 -07001009
Bernardo Rufino6a422d62018-08-28 13:19:20 +01001010 long result = RESULT_ERROR;
Joe Onorato290bb012009-05-13 18:57:29 -04001011 try {
Christopher Tate181fafa2009-05-14 11:12:14 -07001012 BackupAgent.this.onBackup(oldState, output, newState);
Bernardo Rufino6a422d62018-08-28 13:19:20 +01001013 result = RESULT_SUCCESS;
Joe Onorato4ababd92009-06-25 18:29:18 -04001014 } catch (IOException ex) {
1015 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
1016 throw new RuntimeException(ex);
Joe Onorato290bb012009-05-13 18:57:29 -04001017 } catch (RuntimeException ex) {
Joe Onorato83248c42009-06-17 17:55:20 -07001018 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
Joe Onorato290bb012009-05-13 18:57:29 -04001019 throw ex;
Christopher Tate19024922010-01-22 16:39:53 -08001020 } finally {
Christopher Tatef85f5b22013-04-18 16:57:43 -07001021 // Ensure that any SharedPreferences writes have landed after the backup,
1022 // in case the app code has side effects (since apps cannot provide this
1023 // guarantee themselves).
1024 waitForSharedPrefs();
1025
Christopher Tate44a27902010-01-27 17:15:49 -08001026 Binder.restoreCallingIdentity(ident);
1027 try {
Bernardo Rufino6a422d62018-08-28 13:19:20 +01001028 callbackBinder.operationComplete(result);
Christopher Tate44a27902010-01-27 17:15:49 -08001029 } catch (RemoteException e) {
Bernardo Rufino74eee102018-08-29 16:01:49 +01001030 // We will time out anyway.
Christopher Tate44a27902010-01-27 17:15:49 -08001031 }
Christopher Tate91bb0e52016-09-30 17:52:19 -07001032
1033 // Don't close the fd out from under the system service if this was local
1034 if (Binder.getCallingPid() != Process.myPid()) {
1035 IoUtils.closeQuietly(oldState);
1036 IoUtils.closeQuietly(data);
1037 IoUtils.closeQuietly(newState);
1038 }
Joe Onorato290bb012009-05-13 18:57:29 -04001039 }
Christopher Tate487529a2009-04-29 14:03:25 -07001040 }
1041
Christopher Tate75a99702011-05-18 16:28:19 -07001042 @Override
Dianne Hackborn3accca02013-09-20 09:32:11 -07001043 public void doRestore(ParcelFileDescriptor data, long appVersionCode,
Ruslan Tkhakokhovd7f5fdb42019-12-10 11:15:45 +00001044 ParcelFileDescriptor newState, int token, IBackupManager callbackBinder)
1045 throws RemoteException {
1046 doRestoreInternal(data, appVersionCode, newState, token, callbackBinder,
1047 /* excludedKeys */ null);
1048 }
1049
1050 @Override
1051 public void doRestoreWithExcludedKeys(ParcelFileDescriptor data, long appVersionCode,
1052 ParcelFileDescriptor newState, int token, IBackupManager callbackBinder,
1053 List<String> excludedKeys) throws RemoteException {
1054 doRestoreInternal(data, appVersionCode, newState, token, callbackBinder, excludedKeys);
1055 }
1056
1057 private void doRestoreInternal(ParcelFileDescriptor data, long appVersionCode,
1058 ParcelFileDescriptor newState, int token, IBackupManager callbackBinder,
1059 List<String> excludedKeys) throws RemoteException {
Christopher Tate19024922010-01-22 16:39:53 -08001060 // Ensure that we're running with the app's normal permission level
Christopher Tate44a27902010-01-27 17:15:49 -08001061 long ident = Binder.clearCallingIdentity();
Christopher Tate19024922010-01-22 16:39:53 -08001062
Christopher Tate436344a2009-09-30 16:17:37 -07001063 if (DEBUG) Log.v(TAG, "doRestore() invoked");
Christopher Tatefe2368c2017-05-17 15:42:35 -07001064
1065 // Ensure that any side-effect SharedPreferences writes have landed *before*
1066 // we may be about to rewrite the file out from underneath
1067 waitForSharedPrefs();
1068
Joe Onorato83248c42009-06-17 17:55:20 -07001069 BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
1070 try {
Ruslan Tkhakokhovd7f5fdb42019-12-10 11:15:45 +00001071 BackupAgent.this.onRestore(input, appVersionCode, newState,
1072 excludedKeys != null ? new HashSet<>(excludedKeys)
1073 : Collections.emptySet());
Joe Onorato83248c42009-06-17 17:55:20 -07001074 } catch (IOException ex) {
1075 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
1076 throw new RuntimeException(ex);
1077 } catch (RuntimeException ex) {
1078 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
1079 throw ex;
Christopher Tate19024922010-01-22 16:39:53 -08001080 } finally {
Christopher Tatefe2368c2017-05-17 15:42:35 -07001081 // And bring live SharedPreferences instances up to date
1082 reloadSharedPreferences();
Christopher Tatef85f5b22013-04-18 16:57:43 -07001083
Christopher Tate44a27902010-01-27 17:15:49 -08001084 Binder.restoreCallingIdentity(ident);
1085 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001086 callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
Christopher Tate44a27902010-01-27 17:15:49 -08001087 } catch (RemoteException e) {
1088 // we'll time out anyway, so we're safe
1089 }
Christopher Tate91bb0e52016-09-30 17:52:19 -07001090
1091 if (Binder.getCallingPid() != Process.myPid()) {
1092 IoUtils.closeQuietly(data);
1093 IoUtils.closeQuietly(newState);
1094 }
Joe Onorato83248c42009-06-17 17:55:20 -07001095 }
Christopher Tate487529a2009-04-29 14:03:25 -07001096 }
Christopher Tate75a99702011-05-18 16:28:19 -07001097
1098 @Override
Christopher Tate79ec80d2011-06-24 14:58:49 -07001099 public void doFullBackup(ParcelFileDescriptor data,
Robert Berry39194c02018-01-11 13:50:56 +00001100 long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags) {
Christopher Tate79ec80d2011-06-24 14:58:49 -07001101 // Ensure that we're running with the app's normal permission level
1102 long ident = Binder.clearCallingIdentity();
1103
1104 if (DEBUG) Log.v(TAG, "doFullBackup() invoked");
Christopher Tate79ec80d2011-06-24 14:58:49 -07001105
Christopher Tatef85f5b22013-04-18 16:57:43 -07001106 // Ensure that any SharedPreferences writes have landed *before*
1107 // we potentially try to back up the underlying files directly.
1108 waitForSharedPrefs();
1109
Christopher Tate79ec80d2011-06-24 14:58:49 -07001110 try {
Robert Berry39194c02018-01-11 13:50:56 +00001111 BackupAgent.this.onFullBackup(new FullBackupDataOutput(
1112 data, quotaBytes, transportFlags));
Christopher Tate79ec80d2011-06-24 14:58:49 -07001113 } catch (IOException ex) {
Christopher Tate11ae7682015-03-24 18:48:10 -07001114 Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001115 throw new RuntimeException(ex);
1116 } catch (RuntimeException ex) {
Christopher Tate11ae7682015-03-24 18:48:10 -07001117 Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001118 throw ex;
1119 } finally {
Christopher Tatef85f5b22013-04-18 16:57:43 -07001120 // ... and then again after, as in the doBackup() case
1121 waitForSharedPrefs();
1122
Christopher Tate7926a692011-07-11 11:31:57 -07001123 // Send the EOD marker indicating that there is no more data
1124 // forthcoming from this agent.
1125 try {
1126 FileOutputStream out = new FileOutputStream(data.getFileDescriptor());
1127 byte[] buf = new byte[4];
1128 out.write(buf);
1129 } catch (IOException e) {
1130 Log.e(TAG, "Unable to finalize backup stream!");
1131 }
1132
Christopher Tate79ec80d2011-06-24 14:58:49 -07001133 Binder.restoreCallingIdentity(ident);
1134 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001135 callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001136 } catch (RemoteException e) {
1137 // we'll time out anyway, so we're safe
1138 }
Christopher Tate91bb0e52016-09-30 17:52:19 -07001139
1140 if (Binder.getCallingPid() != Process.myPid()) {
1141 IoUtils.closeQuietly(data);
1142 }
Christopher Tate79ec80d2011-06-24 14:58:49 -07001143 }
1144 }
1145
Robert Berry39194c02018-01-11 13:50:56 +00001146 public void doMeasureFullBackup(long quotaBytes, int token, IBackupManager callbackBinder,
1147 int transportFlags) {
Christopher Tate11ae7682015-03-24 18:48:10 -07001148 // Ensure that we're running with the app's normal permission level
1149 final long ident = Binder.clearCallingIdentity();
Robert Berry39194c02018-01-11 13:50:56 +00001150 FullBackupDataOutput measureOutput =
1151 new FullBackupDataOutput(quotaBytes, transportFlags);
Shreyas Basargeb6e73c92017-01-31 20:13:43 +00001152
Christopher Tate11ae7682015-03-24 18:48:10 -07001153 waitForSharedPrefs();
1154 try {
1155 BackupAgent.this.onFullBackup(measureOutput);
1156 } catch (IOException ex) {
1157 Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
1158 throw new RuntimeException(ex);
1159 } catch (RuntimeException ex) {
1160 Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
1161 throw ex;
1162 } finally {
1163 Binder.restoreCallingIdentity(ident);
1164 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001165 callbackBinder.opCompleteForUser(getBackupUserId(), token,
1166 measureOutput.getSize());
Christopher Tate11ae7682015-03-24 18:48:10 -07001167 } catch (RemoteException e) {
1168 // timeout, so we're safe
1169 }
1170 }
1171 }
1172
Christopher Tate79ec80d2011-06-24 14:58:49 -07001173 @Override
Christopher Tate75a99702011-05-18 16:28:19 -07001174 public void doRestoreFile(ParcelFileDescriptor data, long size,
1175 int type, String domain, String path, long mode, long mtime,
1176 int token, IBackupManager callbackBinder) throws RemoteException {
1177 long ident = Binder.clearCallingIdentity();
1178 try {
Christopher Tate75a99702011-05-18 16:28:19 -07001179 BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
1180 } catch (IOException e) {
Christopher Tate11ae7682015-03-24 18:48:10 -07001181 Log.d(TAG, "onRestoreFile (" + BackupAgent.this.getClass().getName() + ") threw", e);
Christopher Tate75a99702011-05-18 16:28:19 -07001182 throw new RuntimeException(e);
1183 } finally {
Christopher Tatef85f5b22013-04-18 16:57:43 -07001184 // Ensure that any side-effect SharedPreferences writes have landed
1185 waitForSharedPrefs();
Christopher Tatefe2368c2017-05-17 15:42:35 -07001186 // And bring live SharedPreferences instances up to date
1187 reloadSharedPreferences();
Christopher Tatef85f5b22013-04-18 16:57:43 -07001188
Christopher Tate75a99702011-05-18 16:28:19 -07001189 Binder.restoreCallingIdentity(ident);
1190 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001191 callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
Christopher Tate75a99702011-05-18 16:28:19 -07001192 } catch (RemoteException e) {
1193 // we'll time out anyway, so we're safe
1194 }
Christopher Tate91bb0e52016-09-30 17:52:19 -07001195
1196 if (Binder.getCallingPid() != Process.myPid()) {
1197 IoUtils.closeQuietly(data);
1198 }
Christopher Tate75a99702011-05-18 16:28:19 -07001199 }
1200 }
Christopher Tatecba59412014-04-01 10:38:29 -07001201
1202 @Override
Christopher Tate2e40d112014-07-15 12:37:38 -07001203 public void doRestoreFinished(int token, IBackupManager callbackBinder) {
1204 long ident = Binder.clearCallingIdentity();
1205 try {
1206 BackupAgent.this.onRestoreFinished();
Christopher Tate11ae7682015-03-24 18:48:10 -07001207 } catch (Exception e) {
1208 Log.d(TAG, "onRestoreFinished (" + BackupAgent.this.getClass().getName() + ") threw", e);
1209 throw e;
Christopher Tate2e40d112014-07-15 12:37:38 -07001210 } finally {
1211 // Ensure that any side-effect SharedPreferences writes have landed
1212 waitForSharedPrefs();
1213
1214 Binder.restoreCallingIdentity(ident);
1215 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001216 callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
Christopher Tate2e40d112014-07-15 12:37:38 -07001217 } catch (RemoteException e) {
1218 // we'll time out anyway, so we're safe
1219 }
1220 }
1221 }
1222
1223 @Override
Christopher Tatecba59412014-04-01 10:38:29 -07001224 public void fail(String message) {
1225 getHandler().post(new FailRunnable(message));
1226 }
Sergey Poromov872d3b62016-01-12 15:48:08 +01001227
1228 @Override
Bernardo Rufino74eee102018-08-29 16:01:49 +01001229 public void doQuotaExceeded(
1230 long backupDataBytes,
1231 long quotaBytes,
1232 IBackupCallback callbackBinder) {
Sergey Poromov872d3b62016-01-12 15:48:08 +01001233 long ident = Binder.clearCallingIdentity();
Bernardo Rufino74eee102018-08-29 16:01:49 +01001234
1235 long result = RESULT_ERROR;
Sergey Poromov872d3b62016-01-12 15:48:08 +01001236 try {
1237 BackupAgent.this.onQuotaExceeded(backupDataBytes, quotaBytes);
Bernardo Rufino74eee102018-08-29 16:01:49 +01001238 result = RESULT_SUCCESS;
Sergey Poromov872d3b62016-01-12 15:48:08 +01001239 } catch (Exception e) {
1240 Log.d(TAG, "onQuotaExceeded(" + BackupAgent.this.getClass().getName() + ") threw",
1241 e);
1242 throw e;
1243 } finally {
1244 waitForSharedPrefs();
1245 Binder.restoreCallingIdentity(ident);
Bernardo Rufino74eee102018-08-29 16:01:49 +01001246
1247 try {
1248 callbackBinder.operationComplete(result);
1249 } catch (RemoteException e) {
1250 // We will time out anyway.
1251 }
Sergey Poromov872d3b62016-01-12 15:48:08 +01001252 }
1253 }
Christopher Tatecba59412014-04-01 10:38:29 -07001254 }
1255
1256 static class FailRunnable implements Runnable {
1257 private String mMessage;
1258
1259 FailRunnable(String message) {
1260 mMessage = message;
1261 }
1262
1263 @Override
1264 public void run() {
1265 throw new IllegalStateException(mMessage);
1266 }
Christopher Tate487529a2009-04-29 14:03:25 -07001267 }
1268}