blob: 24580b40aa29863cfebdbdd4658dad5401703d46 [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;
Christopher Tate79ec80d2011-06-24 14:58:49 -070048import java.util.LinkedList;
Matthew Williams303650c2015-04-17 18:22:51 -070049import java.util.Map;
50import java.util.Set;
Christopher Tatef85f5b22013-04-18 16:57:43 -070051import java.util.concurrent.CountDownLatch;
Christopher Tate79ec80d2011-06-24 14:58:49 -070052
Christopher Tate487529a2009-04-29 14:03:25 -070053/**
Scott Maind17da432010-04-29 21:42:58 -070054 * Provides the central interface between an
Christopher Tate4e14a822010-04-08 12:54:23 -070055 * application and Android's data backup infrastructure. An application that wishes
56 * to participate in the backup and restore mechanism will declare a subclass of
57 * {@link android.app.backup.BackupAgent}, implement the
Scott Maind17da432010-04-29 21:42:58 -070058 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()}
59 * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} methods,
60 * and provide the name of its backup agent class in its {@code AndroidManifest.xml} file via
Joe Fernandez61fd1e82011-10-26 13:39:11 -070061 * the <code>
62 * <a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code>
Scott Maind17da432010-04-29 21:42:58 -070063 * tag's {@code android:backupAgent} attribute.
Joe Fernandez61fd1e82011-10-26 13:39:11 -070064 *
65 * <div class="special reference">
66 * <h3>Developer Guides</h3>
67 * <p>For more information about using BackupAgent, read the
68 * <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p></div>
69 *
Scott Maind17da432010-04-29 21:42:58 -070070 * <h3>Basic Operation</h3>
Kenny Root5a20ea12010-02-23 18:49:11 -080071 * <p>
Christopher Tate4e14a822010-04-08 12:54:23 -070072 * When the application makes changes to data that it wishes to keep backed up,
73 * it should call the
74 * {@link android.app.backup.BackupManager#dataChanged() BackupManager.dataChanged()} method.
Scott Maind17da432010-04-29 21:42:58 -070075 * This notifies the Android Backup Manager that the application needs an opportunity
76 * to update its backup image. The Backup Manager, in turn, schedules a
Christopher Tate4e14a822010-04-08 12:54:23 -070077 * backup pass to be performed at an opportune time.
78 * <p>
Scott Maind17da432010-04-29 21:42:58 -070079 * Restore operations are typically performed only when applications are first
Christopher Tate4e14a822010-04-08 12:54:23 -070080 * installed on a device. At that time, the operating system checks to see whether
Scott Maind17da432010-04-29 21:42:58 -070081 * there is a previously-saved data set available for the application being installed, and if so,
82 * begins an immediate restore pass to deliver the backup data as part of the installation
Christopher Tate4e14a822010-04-08 12:54:23 -070083 * process.
84 * <p>
Scott Maind17da432010-04-29 21:42:58 -070085 * When a backup or restore pass is run, the application's process is launched
86 * (if not already running), the manifest-declared backup agent class (in the {@code
87 * android:backupAgent} attribute) is instantiated within
88 * that process, and the agent's {@link #onCreate()} method is invoked. This prepares the
Christopher Tate4e14a822010-04-08 12:54:23 -070089 * agent instance to run the actual backup or restore logic. At this point the
90 * agent's
91 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()} or
92 * {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} method will be
93 * invoked as appropriate for the operation being performed.
94 * <p>
Scott Maind17da432010-04-29 21:42:58 -070095 * A backup data set consists of one or more "entities," flattened binary data
96 * records that are each identified with a key string unique within the data set. Adding a
97 * record to the active data set or updating an existing record is done by simply
Christopher Tate4e14a822010-04-08 12:54:23 -070098 * writing new entity data under the desired key. Deleting an entity from the data set
99 * is done by writing an entity under that key with header specifying a negative data
100 * size, and no actual entity data.
101 * <p>
102 * <b>Helper Classes</b>
103 * <p>
104 * An extensible agent based on convenient helper classes is available in
105 * {@link android.app.backup.BackupAgentHelper}. That class is particularly
106 * suited to handling of simple file or {@link android.content.SharedPreferences}
107 * backup and restore.
Michal Karpinski6aa05b82017-08-09 17:24:59 +0100108 * <p>
109 * <b>Threading</b>
110 * <p>
111 * The constructor, as well as {@link #onCreate()} and {@link #onDestroy()} lifecycle callbacks run
112 * on the main thread (UI thread) of the application that implements the BackupAgent.
113 * The data-handling callbacks:
114 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()},
115 * {@link #onFullBackup(FullBackupDataOutput)},
116 * {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()},
117 * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()},
118 * {@link #onRestoreFinished()}, and {@link #onQuotaExceeded(long, long) onQuotaExceeded()}
119 * run on binder pool threads.
Christopher Tate4e14a822010-04-08 12:54:23 -0700120 *
121 * @see android.app.backup.BackupManager
122 * @see android.app.backup.BackupAgentHelper
123 * @see android.app.backup.BackupDataInput
124 * @see android.app.backup.BackupDataOutput
Christopher Tate487529a2009-04-29 14:03:25 -0700125 */
Christopher Tate181fafa2009-05-14 11:12:14 -0700126public abstract class BackupAgent extends ContextWrapper {
Joe Onorato83248c42009-06-17 17:55:20 -0700127 private static final String TAG = "BackupAgent";
Alan Jeonf0e32ee2014-12-13 22:44:53 +0900128 private static final boolean DEBUG = false;
Joe Onorato83248c42009-06-17 17:55:20 -0700129
Christopher Tate79ec80d2011-06-24 14:58:49 -0700130 /** @hide */
Bernardo Rufino6a422d62018-08-28 13:19:20 +0100131 public static final int RESULT_SUCCESS = 0;
132 /** @hide */
133 public static final int RESULT_ERROR = -1;
134
135 /** @hide */
Christopher Tate79ec80d2011-06-24 14:58:49 -0700136 public static final int TYPE_EOF = 0;
137
138 /**
139 * During a full restore, indicates that the file system object being restored
140 * is an ordinary file.
141 */
142 public static final int TYPE_FILE = 1;
143
144 /**
145 * During a full restore, indicates that the file system object being restored
146 * is a directory.
147 */
148 public static final int TYPE_DIRECTORY = 2;
149
150 /** @hide */
151 public static final int TYPE_SYMLINK = 3;
152
Robert Berry39194c02018-01-11 13:50:56 +0000153 /**
154 * Flag for {@link BackupDataOutput#getTransportFlags()} and
155 * {@link FullBackupDataOutput#getTransportFlags()} only.
156 *
Colin Cross4da17742018-04-11 17:27:24 +0000157 * <p>The transport has client-side encryption enabled. i.e., the user's backup has been
158 * encrypted with a key known only to the device, and not to the remote storage solution. Even
159 * if an attacker had root access to the remote storage provider they should not be able to
160 * decrypt the user's backup data.
Robert Berry39194c02018-01-11 13:50:56 +0000161 */
162 public static final int FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED = 1;
163
Robert Berry98a8e242018-01-25 13:29:16 +0000164 /**
165 * Flag for {@link BackupDataOutput#getTransportFlags()} and
166 * {@link FullBackupDataOutput#getTransportFlags()} only.
167 *
168 * <p>The transport is for a device-to-device transfer. There is no third party or intermediate
169 * storage. The user's backup data is sent directly to another device over e.g., USB or WiFi.
170 */
171 public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2;
172
Bernardo Rufinoeaa78b92018-01-26 11:25:37 +0000173 /**
174 * Flag for {@link BackupDataOutput#getTransportFlags()} and
175 * {@link FullBackupDataOutput#getTransportFlags()} only.
176 *
177 * <p>Used for internal testing only. Do not check this flag in production code.
178 *
179 * @hide
180 */
181 public static final int FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED = 1 << 31;
182
Christopher Tatef85f5b22013-04-18 16:57:43 -0700183 Handler mHandler = null;
184
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +0000185 @Nullable private UserHandle mUser;
186
Christopher Tatecba59412014-04-01 10:38:29 -0700187 Handler getHandler() {
188 if (mHandler == null) {
189 mHandler = new Handler(Looper.getMainLooper());
190 }
191 return mHandler;
192 }
193
Christopher Tatef85f5b22013-04-18 16:57:43 -0700194 class SharedPrefsSynchronizer implements Runnable {
195 public final CountDownLatch mLatch = new CountDownLatch(1);
196
197 @Override
198 public void run() {
199 QueuedWork.waitToFinish();
200 mLatch.countDown();
201 }
202 };
203
204 // Syncing shared preferences deferred writes needs to happen on the main looper thread
205 private void waitForSharedPrefs() {
Christopher Tatecba59412014-04-01 10:38:29 -0700206 Handler h = getHandler();
Christopher Tatef85f5b22013-04-18 16:57:43 -0700207 final SharedPrefsSynchronizer s = new SharedPrefsSynchronizer();
Christopher Tatecba59412014-04-01 10:38:29 -0700208 h.postAtFrontOfQueue(s);
Christopher Tatef85f5b22013-04-18 16:57:43 -0700209 try {
210 s.mLatch.await();
211 } catch (InterruptedException e) { /* ignored */ }
212 }
213
214
Christopher Tate181fafa2009-05-14 11:12:14 -0700215 public BackupAgent() {
216 super(null);
217 }
Christopher Tate487529a2009-04-29 14:03:25 -0700218
Christopher Tate4e14a822010-04-08 12:54:23 -0700219 /**
220 * Provided as a convenience for agent implementations that need an opportunity
221 * to do one-time initialization before the actual backup or restore operation
222 * is begun.
223 * <p>
Christopher Tate4e14a822010-04-08 12:54:23 -0700224 */
Christopher Tate181fafa2009-05-14 11:12:14 -0700225 public void onCreate() {
226 }
227
Christopher Tate4e14a822010-04-08 12:54:23 -0700228 /**
Stefanot14bbded2018-10-11 12:07:53 +0100229 * Provided as a convenience for agent implementations that need an opportunity
230 * to do one-time initialization before the actual backup or restore operation
231 * is begun with information about the calling user.
232 * <p>
233 *
234 * @hide
235 */
236 public void onCreate(UserHandle user) {
237 onCreate();
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +0000238
239 mUser = user;
Stefanot14bbded2018-10-11 12:07:53 +0100240 }
241
242 /**
Christopher Tate4e14a822010-04-08 12:54:23 -0700243 * Provided as a convenience for agent implementations that need to do some
244 * sort of shutdown process after backup or restore is completed.
245 * <p>
246 * Agents do not need to override this method.
247 */
Christopher Tate181fafa2009-05-14 11:12:14 -0700248 public void onDestroy() {
249 }
Christopher Tate487529a2009-04-29 14:03:25 -0700250
251 /**
Kenny Root5a20ea12010-02-23 18:49:11 -0800252 * The application is being asked to write any data changed since the last
253 * time it performed a backup operation. The state data recorded during the
254 * last backup pass is provided in the <code>oldState</code> file
255 * descriptor. If <code>oldState</code> is <code>null</code>, no old state
256 * is available and the application should perform a full backup. In both
257 * cases, a representation of the final backup state after this pass should
258 * be written to the file pointed to by the file descriptor wrapped in
259 * <code>newState</code>.
Christopher Tate4e14a822010-04-08 12:54:23 -0700260 * <p>
261 * Each entity written to the {@link android.app.backup.BackupDataOutput}
262 * <code>data</code> stream will be transmitted
263 * over the current backup transport and stored in the remote data set under
264 * the key supplied as part of the entity. Writing an entity with a negative
265 * data size instructs the transport to delete whatever entity currently exists
266 * under that key from the remote data set.
Elliott Hughes34385d32014-04-28 11:11:32 -0700267 *
Kenny Root5a20ea12010-02-23 18:49:11 -0800268 * @param oldState An open, read-only ParcelFileDescriptor pointing to the
269 * last backup state provided by the application. May be
270 * <code>null</code>, in which case no prior state is being
271 * provided and the application should perform a full backup.
272 * @param data A structured wrapper around an open, read/write
Christopher Tate4e14a822010-04-08 12:54:23 -0700273 * file descriptor pointing to the backup data destination.
Kenny Root5a20ea12010-02-23 18:49:11 -0800274 * Typically the application will use backup helper classes to
275 * write to this file.
276 * @param newState An open, read/write ParcelFileDescriptor pointing to an
277 * empty file. The application should record the final backup
Christopher Tate4e14a822010-04-08 12:54:23 -0700278 * state here after writing the requested data to the <code>data</code>
279 * output stream.
Christopher Tate487529a2009-04-29 14:03:25 -0700280 */
Joe Onorato290bb012009-05-13 18:57:29 -0400281 public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
Christopher Tate2e40d112014-07-15 12:37:38 -0700282 ParcelFileDescriptor newState) throws IOException;
Kenny Root5a20ea12010-02-23 18:49:11 -0800283
Christopher Tate487529a2009-04-29 14:03:25 -0700284 /**
Kenny Root5a20ea12010-02-23 18:49:11 -0800285 * The application is being restored from backup and should replace any
286 * existing data with the contents of the backup. The backup data is
Christopher Tate4e14a822010-04-08 12:54:23 -0700287 * provided through the <code>data</code> parameter. Once
Kenny Root5a20ea12010-02-23 18:49:11 -0800288 * the restore is finished, the application should write a representation of
289 * the final state to the <code>newState</code> file descriptor.
290 * <p>
291 * The application is responsible for properly erasing its old data and
292 * replacing it with the data supplied to this method. No "clear user data"
293 * operation will be performed automatically by the operating system. The
294 * exception to this is in the case of a failed restore attempt: if
295 * onRestore() throws an exception, the OS will assume that the
296 * application's data may now be in an incoherent state, and will clear it
297 * before proceeding.
Elliott Hughes34385d32014-04-28 11:11:32 -0700298 *
Kenny Root5a20ea12010-02-23 18:49:11 -0800299 * @param data A structured wrapper around an open, read-only
Christopher Tate4e14a822010-04-08 12:54:23 -0700300 * file descriptor pointing to a full snapshot of the
301 * application's data. The application should consume every
302 * entity represented in this data stream.
Scott Mainb83a2832010-04-29 13:26:53 -0700303 * @param appVersionCode The value of the <a
304 * href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code
305 * android:versionCode}</a> manifest attribute,
306 * from the application that backed up this particular data set. This
Christopher Tate4e14a822010-04-08 12:54:23 -0700307 * makes it possible for an application's agent to distinguish among any
Kenny Root5a20ea12010-02-23 18:49:11 -0800308 * possible older data versions when asked to perform the restore
309 * operation.
310 * @param newState An open, read/write ParcelFileDescriptor pointing to an
311 * empty file. The application should record the final backup
Christopher Tate4e14a822010-04-08 12:54:23 -0700312 * state here after restoring its data from the <code>data</code> stream.
Christopher Tate4a627c72011-04-01 14:43:32 -0700313 * When a full-backup dataset is being restored, this will be <code>null</code>.
Christopher Tate487529a2009-04-29 14:03:25 -0700314 */
Christopher Tate5cbbf562009-06-22 16:44:51 -0700315 public abstract void onRestore(BackupDataInput data, int appVersionCode,
Christopher Tate2e40d112014-07-15 12:37:38 -0700316 ParcelFileDescriptor newState) throws IOException;
Christopher Tate487529a2009-04-29 14:03:25 -0700317
Christopher Tate4a627c72011-04-01 14:43:32 -0700318 /**
Dianne Hackborn3accca02013-09-20 09:32:11 -0700319 * New version of {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}
320 * that handles a long app version code. Default implementation casts the version code to
321 * an int and calls {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}.
322 */
323 public void onRestore(BackupDataInput data, long appVersionCode,
324 ParcelFileDescriptor newState)
325 throws IOException {
326 onRestore(data, (int) appVersionCode, newState);
327 }
328
329 /**
Christopher Tatea7835b62014-07-11 17:25:57 -0700330 * The application is having its entire file system contents backed up. {@code data}
331 * points to the backup destination, and the app has the opportunity to choose which
332 * files are to be stored. To commit a file as part of the backup, call the
333 * {@link #fullBackupFile(File, FullBackupDataOutput)} helper method. After all file
334 * data is written to the output, the agent returns from this method and the backup
335 * operation concludes.
336 *
337 * <p>Certain parts of the app's data are never backed up even if the app explicitly
338 * sends them to the output:
339 *
340 * <ul>
341 * <li>The contents of the {@link #getCacheDir()} directory</li>
Christopher Tatea8a739f2015-03-05 18:31:38 -0800342 * <li>The contents of the {@link #getCodeCacheDir()} directory</li>
Christopher Tatea7835b62014-07-11 17:25:57 -0700343 * <li>The contents of the {@link #getNoBackupFilesDir()} directory</li>
344 * <li>The contents of the app's shared library directory</li>
345 * </ul>
346 *
347 * <p>The default implementation of this method backs up the entirety of the
348 * application's "owned" file system trees to the output other than the few exceptions
349 * listed above. Apps only need to override this method if they need to impose special
350 * limitations on which files are being stored beyond the control that
351 * {@link #getNoBackupFilesDir()} offers.
Matthew Williams303650c2015-04-17 18:22:51 -0700352 * Alternatively they can provide an xml resource to specify what data to include or exclude.
353 *
Christopher Tatea7835b62014-07-11 17:25:57 -0700354 *
355 * @param data A structured wrapper pointing to the backup destination.
356 * @throws IOException
357 *
358 * @see Context#getNoBackupFilesDir()
359 * @see #fullBackupFile(File, FullBackupDataOutput)
360 * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
Christopher Tate75a99702011-05-18 16:28:19 -0700361 */
Christopher Tate79ec80d2011-06-24 14:58:49 -0700362 public void onFullBackup(FullBackupDataOutput data) throws IOException {
Matthew Williams303650c2015-04-17 18:22:51 -0700363 FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this);
364 if (!backupScheme.isFullBackupContentEnabled()) {
365 return;
366 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700367
Michal Karpinskib5e09312018-02-19 13:55:23 +0000368 Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap;
369 ArraySet<PathWithRequiredFlags> manifestExcludeSet;
Matthew Williams303650c2015-04-17 18:22:51 -0700370 try {
371 manifestIncludeMap =
372 backupScheme.maybeParseAndGetCanonicalIncludePaths();
373 manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths();
374 } catch (IOException | XmlPullParserException e) {
375 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
376 Log.v(FullBackup.TAG_XML_PARSER,
377 "Exception trying to parse fullBackupContent xml file!"
378 + " Aborting full backup.", e);
379 }
380 return;
381 }
382
383 final String packageName = getPackageName();
384 final ApplicationInfo appInfo = getApplicationInfo();
385
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700386 // System apps have control over where their default storage context
387 // is pointed, so we're always explicit when building paths.
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600388 final Context ceContext = createCredentialProtectedStorageContext();
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700389 final String rootDir = ceContext.getDataDir().getCanonicalPath();
390 final String filesDir = ceContext.getFilesDir().getCanonicalPath();
391 final String noBackupDir = ceContext.getNoBackupFilesDir().getCanonicalPath();
392 final String databaseDir = ceContext.getDatabasePath("foo").getParentFile()
393 .getCanonicalPath();
394 final String sharedPrefsDir = ceContext.getSharedPreferencesPath("foo").getParentFile()
395 .getCanonicalPath();
396 final String cacheDir = ceContext.getCacheDir().getCanonicalPath();
397 final String codeCacheDir = ceContext.getCodeCacheDir().getCanonicalPath();
398
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600399 final Context deContext = createDeviceProtectedStorageContext();
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700400 final String deviceRootDir = deContext.getDataDir().getCanonicalPath();
401 final String deviceFilesDir = deContext.getFilesDir().getCanonicalPath();
402 final String deviceNoBackupDir = deContext.getNoBackupFilesDir().getCanonicalPath();
403 final String deviceDatabaseDir = deContext.getDatabasePath("foo").getParentFile()
404 .getCanonicalPath();
405 final String deviceSharedPrefsDir = deContext.getSharedPreferencesPath("foo")
406 .getParentFile().getCanonicalPath();
407 final String deviceCacheDir = deContext.getCacheDir().getCanonicalPath();
408 final String deviceCodeCacheDir = deContext.getCodeCacheDir().getCanonicalPath();
409
410 final String libDir = (appInfo.nativeLibraryDir != null)
Christopher Tate2efd2db2011-07-19 16:32:49 -0700411 ? new File(appInfo.nativeLibraryDir).getCanonicalPath()
Christopher Tate79ec80d2011-06-24 14:58:49 -0700412 : null;
413
Matthew Williams303650c2015-04-17 18:22:51 -0700414 // Maintain a set of excluded directories so that as we traverse the tree we know we're not
415 // going places we don't expect, and so the manifest includes can't take precedence over
416 // what the framework decides is not to be included.
417 final ArraySet<String> traversalExcludeSet = new ArraySet<String>();
Christopher Tate79ec80d2011-06-24 14:58:49 -0700418
Matthew Williams303650c2015-04-17 18:22:51 -0700419 // Add the directories we always exclude.
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700420 traversalExcludeSet.add(filesDir);
421 traversalExcludeSet.add(noBackupDir);
422 traversalExcludeSet.add(databaseDir);
423 traversalExcludeSet.add(sharedPrefsDir);
Matthew Williams303650c2015-04-17 18:22:51 -0700424 traversalExcludeSet.add(cacheDir);
425 traversalExcludeSet.add(codeCacheDir);
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700426
427 traversalExcludeSet.add(deviceFilesDir);
428 traversalExcludeSet.add(deviceNoBackupDir);
429 traversalExcludeSet.add(deviceDatabaseDir);
430 traversalExcludeSet.add(deviceSharedPrefsDir);
431 traversalExcludeSet.add(deviceCacheDir);
432 traversalExcludeSet.add(deviceCodeCacheDir);
433
Christopher Tate79ec80d2011-06-24 14:58:49 -0700434 if (libDir != null) {
Matthew Williams303650c2015-04-17 18:22:51 -0700435 traversalExcludeSet.add(libDir);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700436 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700437
Matthew Williams303650c2015-04-17 18:22:51 -0700438 // Root dir first.
439 applyXmlFiltersAndDoFullBackupForDomain(
440 packageName, FullBackup.ROOT_TREE_TOKEN, manifestIncludeMap,
441 manifestExcludeSet, traversalExcludeSet, data);
442 traversalExcludeSet.add(rootDir);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700443
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700444 applyXmlFiltersAndDoFullBackupForDomain(
445 packageName, FullBackup.DEVICE_ROOT_TREE_TOKEN, manifestIncludeMap,
446 manifestExcludeSet, traversalExcludeSet, data);
447 traversalExcludeSet.add(deviceRootDir);
448
Matthew Williams303650c2015-04-17 18:22:51 -0700449 // Data dir next.
450 traversalExcludeSet.remove(filesDir);
451 applyXmlFiltersAndDoFullBackupForDomain(
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700452 packageName, FullBackup.FILES_TREE_TOKEN, manifestIncludeMap,
Matthew Williams303650c2015-04-17 18:22:51 -0700453 manifestExcludeSet, traversalExcludeSet, data);
454 traversalExcludeSet.add(filesDir);
455
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700456 traversalExcludeSet.remove(deviceFilesDir);
457 applyXmlFiltersAndDoFullBackupForDomain(
458 packageName, FullBackup.DEVICE_FILES_TREE_TOKEN, manifestIncludeMap,
459 manifestExcludeSet, traversalExcludeSet, data);
460 traversalExcludeSet.add(deviceFilesDir);
461
Matthew Williams303650c2015-04-17 18:22:51 -0700462 // Database directory.
463 traversalExcludeSet.remove(databaseDir);
464 applyXmlFiltersAndDoFullBackupForDomain(
465 packageName, FullBackup.DATABASE_TREE_TOKEN, manifestIncludeMap,
466 manifestExcludeSet, traversalExcludeSet, data);
467 traversalExcludeSet.add(databaseDir);
468
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700469 traversalExcludeSet.remove(deviceDatabaseDir);
470 applyXmlFiltersAndDoFullBackupForDomain(
471 packageName, FullBackup.DEVICE_DATABASE_TREE_TOKEN, manifestIncludeMap,
472 manifestExcludeSet, traversalExcludeSet, data);
473 traversalExcludeSet.add(deviceDatabaseDir);
474
Matthew Williams303650c2015-04-17 18:22:51 -0700475 // SharedPrefs.
476 traversalExcludeSet.remove(sharedPrefsDir);
477 applyXmlFiltersAndDoFullBackupForDomain(
478 packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, manifestIncludeMap,
479 manifestExcludeSet, traversalExcludeSet, data);
480 traversalExcludeSet.add(sharedPrefsDir);
Christopher Tate416c39e2013-02-14 16:55:46 -0800481
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700482 traversalExcludeSet.remove(deviceSharedPrefsDir);
483 applyXmlFiltersAndDoFullBackupForDomain(
484 packageName, FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN, manifestIncludeMap,
485 manifestExcludeSet, traversalExcludeSet, data);
486 traversalExcludeSet.add(deviceSharedPrefsDir);
487
Christopher Tate416c39e2013-02-14 16:55:46 -0800488 // getExternalFilesDir() location associated with this app. Technically there should
489 // not be any files here if the app does not properly have permission to access
490 // external storage, but edge cases happen. fullBackupFileTree() catches
Christopher Tate5cb5c332013-02-21 14:32:12 -0800491 // IOExceptions and similar, and treats them as non-fatal, so we rely on that; and
492 // we know a priori that processes running as the system UID are not permitted to
493 // access external storage, so we check for that as well to avoid nastygrams in
494 // the log.
495 if (Process.myUid() != Process.SYSTEM_UID) {
496 File efLocation = getExternalFilesDir(null);
497 if (efLocation != null) {
Matthew Williams303650c2015-04-17 18:22:51 -0700498 applyXmlFiltersAndDoFullBackupForDomain(
499 packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN, manifestIncludeMap,
500 manifestExcludeSet, traversalExcludeSet, data);
501 }
502
503 }
504 }
505
506 /**
Christopher Tated43879c2016-04-04 12:54:43 -0700507 * Notification that the application's current backup operation causes it to exceed
508 * the maximum size permitted by the transport. The ongoing backup operation is
509 * halted and rolled back: any data that had been stored by a previous backup operation
510 * is still intact. Typically the quota-exceeded state will be detected before any data
511 * is actually transmitted over the network.
Sergey Poromov872d3b62016-01-12 15:48:08 +0100512 *
Christopher Tated43879c2016-04-04 12:54:43 -0700513 * <p>The {@code quotaBytes} value is the total data size currently permitted for this
514 * application. If desired, the application can use this as a hint for determining
515 * how much data to store. For example, a messaging application might choose to
516 * store only the newest messages, dropping enough older content to stay under
517 * the quota.
518 *
519 * <p class="note">Note that the maximum quota for the application can change over
520 * time. In particular, in the future the quota may grow. Applications that adapt
521 * to the quota when deciding what data to store should be aware of this and implement
522 * their data storage mechanisms in a way that can take advantage of additional
523 * quota.
524 *
525 * @param backupDataBytes The amount of data measured while initializing the backup
526 * operation, if the total exceeds the app's alloted quota. If initial measurement
527 * suggested that the data would fit but then too much data was actually submitted
528 * as part of the operation, then this value is the amount of data that had been
529 * streamed into the transport at the time the quota was reached.
530 * @param quotaBytes The maximum data size that the transport currently permits
531 * this application to store as a backup.
Sergey Poromov872d3b62016-01-12 15:48:08 +0100532 */
533 public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
534 }
535
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +0000536 private int getBackupUserId() {
537 return mUser == null ? super.getUserId() : mUser.getIdentifier();
538 }
539
Sergey Poromov872d3b62016-01-12 15:48:08 +0100540 /**
Matthew Williams303650c2015-04-17 18:22:51 -0700541 * Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>.
542 * If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path
Michal Karpinskib5e09312018-02-19 13:55:23 +0000543 * is a directory, but only if all the required flags of the include rule are satisfied by
544 * the transport.
Matthew Williams303650c2015-04-17 18:22:51 -0700545 */
546 private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken,
Michal Karpinskib5e09312018-02-19 13:55:23 +0000547 Map<String, Set<PathWithRequiredFlags>> includeMap,
548 ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet,
549 FullBackupDataOutput data) throws IOException {
Matthew Williams303650c2015-04-17 18:22:51 -0700550 if (includeMap == null || includeMap.size() == 0) {
551 // Do entire sub-tree for the provided token.
552 fullBackupFileTree(packageName, domainToken,
553 FullBackup.getBackupScheme(this).tokenToDirectoryPath(domainToken),
554 filterSet, traversalExcludeSet, data);
555 } else if (includeMap.get(domainToken) != null) {
556 // This will be null if the xml parsing didn't yield any rules for
557 // this domain (there may still be rules for other domains).
Michal Karpinskib5e09312018-02-19 13:55:23 +0000558 for (PathWithRequiredFlags includeFile : includeMap.get(domainToken)) {
559 if (areIncludeRequiredTransportFlagsSatisfied(includeFile.getRequiredFlags(),
560 data.getTransportFlags())) {
561 fullBackupFileTree(packageName, domainToken, includeFile.getPath(), filterSet,
562 traversalExcludeSet, data);
563 }
Christopher Tate5cb5c332013-02-21 14:32:12 -0800564 }
565 }
Christopher Tate75a99702011-05-18 16:28:19 -0700566 }
567
Michal Karpinskib5e09312018-02-19 13:55:23 +0000568 private boolean areIncludeRequiredTransportFlagsSatisfied(int includeFlags,
569 int transportFlags) {
570 // all bits that are set in includeFlags must also be set in transportFlags
571 return (transportFlags & includeFlags) == includeFlags;
572 }
573
Christopher Tate75a99702011-05-18 16:28:19 -0700574 /**
Christopher Tate79ec80d2011-06-24 14:58:49 -0700575 * Write an entire file as part of a full-backup operation. The file's contents
576 * will be delivered to the backup destination along with the metadata necessary
577 * to place it with the proper location and permissions on the device where the
578 * data is restored.
Christopher Tate79ec80d2011-06-24 14:58:49 -0700579 *
Christopher Tatec5be8f82016-04-25 14:41:50 -0700580 * <p class="note">Attempting to back up files in directories that are ignored by
581 * the backup system will have no effect. For example, if the app calls this method
582 * with a file inside the {@link #getNoBackupFilesDir()} directory, it will be ignored.
Elliot Waite54de7742017-01-11 15:30:35 -0800583 * See {@link #onFullBackup(FullBackupDataOutput)} for details on what directories
Christopher Tatec5be8f82016-04-25 14:41:50 -0700584 * are excluded from backups.
Christopher Tatea7835b62014-07-11 17:25:57 -0700585 *
Christopher Tate79ec80d2011-06-24 14:58:49 -0700586 * @param file The file to be backed up. The file must exist and be readable by
587 * the caller.
588 * @param output The destination to which the backed-up file data will be sent.
Christopher Tate4a627c72011-04-01 14:43:32 -0700589 */
Christopher Tate79ec80d2011-06-24 14:58:49 -0700590 public final void fullBackupFile(File file, FullBackupDataOutput output) {
591 // Look up where all of our various well-defined dir trees live on this device
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700592 final String rootDir;
593 final String filesDir;
594 final String nbFilesDir;
595 final String dbDir;
596 final String spDir;
597 final String cacheDir;
598 final String codeCacheDir;
599 final String deviceRootDir;
600 final String deviceFilesDir;
601 final String deviceNbFilesDir;
602 final String deviceDbDir;
603 final String deviceSpDir;
604 final String deviceCacheDir;
605 final String deviceCodeCacheDir;
606 final String libDir;
607
Christopher Tate5cb5c332013-02-21 14:32:12 -0800608 String efDir = null;
Christopher Tate2efd2db2011-07-19 16:32:49 -0700609 String filePath;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700610
611 ApplicationInfo appInfo = getApplicationInfo();
612
Christopher Tate2efd2db2011-07-19 16:32:49 -0700613 try {
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700614 // System apps have control over where their default storage context
615 // is pointed, so we're always explicit when building paths.
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600616 final Context ceContext = createCredentialProtectedStorageContext();
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700617 rootDir = ceContext.getDataDir().getCanonicalPath();
618 filesDir = ceContext.getFilesDir().getCanonicalPath();
619 nbFilesDir = ceContext.getNoBackupFilesDir().getCanonicalPath();
620 dbDir = ceContext.getDatabasePath("foo").getParentFile().getCanonicalPath();
621 spDir = ceContext.getSharedPreferencesPath("foo").getParentFile().getCanonicalPath();
622 cacheDir = ceContext.getCacheDir().getCanonicalPath();
623 codeCacheDir = ceContext.getCodeCacheDir().getCanonicalPath();
624
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600625 final Context deContext = createDeviceProtectedStorageContext();
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700626 deviceRootDir = deContext.getDataDir().getCanonicalPath();
627 deviceFilesDir = deContext.getFilesDir().getCanonicalPath();
628 deviceNbFilesDir = deContext.getNoBackupFilesDir().getCanonicalPath();
629 deviceDbDir = deContext.getDatabasePath("foo").getParentFile().getCanonicalPath();
630 deviceSpDir = deContext.getSharedPreferencesPath("foo").getParentFile()
631 .getCanonicalPath();
632 deviceCacheDir = deContext.getCacheDir().getCanonicalPath();
633 deviceCodeCacheDir = deContext.getCodeCacheDir().getCanonicalPath();
634
Christopher Tate2efd2db2011-07-19 16:32:49 -0700635 libDir = (appInfo.nativeLibraryDir == null)
636 ? null
637 : new File(appInfo.nativeLibraryDir).getCanonicalPath();
Christopher Tate5cb5c332013-02-21 14:32:12 -0800638
639 // may or may not have external files access to attempt backup/restore there
640 if (Process.myUid() != Process.SYSTEM_UID) {
641 File efLocation = getExternalFilesDir(null);
642 if (efLocation != null) {
643 efDir = efLocation.getCanonicalPath();
644 }
645 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700646
Christopher Tate2efd2db2011-07-19 16:32:49 -0700647 // Now figure out which well-defined tree the file is placed in, working from
Christopher Tatea8a739f2015-03-05 18:31:38 -0800648 // most to least specific. We also specifically exclude the lib, cache,
649 // and code_cache dirs.
Christopher Tate2efd2db2011-07-19 16:32:49 -0700650 filePath = file.getCanonicalPath();
651 } catch (IOException e) {
652 Log.w(TAG, "Unable to obtain canonical paths");
653 return;
654 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700655
Christopher Tatea7835b62014-07-11 17:25:57 -0700656 if (filePath.startsWith(cacheDir)
Christopher Tatea8a739f2015-03-05 18:31:38 -0800657 || filePath.startsWith(codeCacheDir)
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700658 || filePath.startsWith(nbFilesDir)
659 || filePath.startsWith(deviceCacheDir)
660 || filePath.startsWith(deviceCodeCacheDir)
661 || filePath.startsWith(deviceNbFilesDir)
662 || filePath.startsWith(libDir)) {
Christopher Tatea8a739f2015-03-05 18:31:38 -0800663 Log.w(TAG, "lib, cache, code_cache, and no_backup files are not backed up");
Christopher Tate79ec80d2011-06-24 14:58:49 -0700664 return;
665 }
666
667 final String domain;
668 String rootpath = null;
669 if (filePath.startsWith(dbDir)) {
670 domain = FullBackup.DATABASE_TREE_TOKEN;
671 rootpath = dbDir;
672 } else if (filePath.startsWith(spDir)) {
673 domain = FullBackup.SHAREDPREFS_TREE_TOKEN;
674 rootpath = spDir;
675 } else if (filePath.startsWith(filesDir)) {
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700676 domain = FullBackup.FILES_TREE_TOKEN;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700677 rootpath = filesDir;
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700678 } else if (filePath.startsWith(rootDir)) {
Christopher Tate79ec80d2011-06-24 14:58:49 -0700679 domain = FullBackup.ROOT_TREE_TOKEN;
Jeff Sharkey2c1ba9a2016-02-17 15:29:38 -0700680 rootpath = rootDir;
681 } else if (filePath.startsWith(deviceDbDir)) {
682 domain = FullBackup.DEVICE_DATABASE_TREE_TOKEN;
683 rootpath = deviceDbDir;
684 } else if (filePath.startsWith(deviceSpDir)) {
685 domain = FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN;
686 rootpath = deviceSpDir;
687 } else if (filePath.startsWith(deviceFilesDir)) {
688 domain = FullBackup.DEVICE_FILES_TREE_TOKEN;
689 rootpath = deviceFilesDir;
690 } else if (filePath.startsWith(deviceRootDir)) {
691 domain = FullBackup.DEVICE_ROOT_TREE_TOKEN;
692 rootpath = deviceRootDir;
Christopher Tate5cb5c332013-02-21 14:32:12 -0800693 } else if ((efDir != null) && filePath.startsWith(efDir)) {
Christopher Tate416c39e2013-02-14 16:55:46 -0800694 domain = FullBackup.MANAGED_EXTERNAL_TREE_TOKEN;
695 rootpath = efDir;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700696 } else {
697 Log.w(TAG, "File " + filePath + " is in an unsupported location; skipping");
698 return;
699 }
700
701 // And now that we know where it lives, semantically, back it up appropriately
Christopher Tate11ae7682015-03-24 18:48:10 -0700702 // In the measurement case, backupToTar() updates the size in output and returns
703 // without transmitting any file data.
704 if (DEBUG) Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
Christopher Tate79ec80d2011-06-24 14:58:49 -0700705 + " rootpath=" + rootpath);
Matthew Williams303650c2015-04-17 18:22:51 -0700706
Christopher Tate11ae7682015-03-24 18:48:10 -0700707 FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath, output);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700708 }
709
710 /**
711 * Scan the dir tree (if it actually exists) and process each entry we find. If the
Matthew Williams303650c2015-04-17 18:22:51 -0700712 * 'excludes' parameters are non-null, they are consulted each time a new file system entity
Christopher Tate79ec80d2011-06-24 14:58:49 -0700713 * is visited to see whether that entity (and its subtree, if appropriate) should be
714 * omitted from the backup process.
715 *
Matthew Williams303650c2015-04-17 18:22:51 -0700716 * @param systemExcludes An optional list of excludes.
Christopher Tate79ec80d2011-06-24 14:58:49 -0700717 * @hide
718 */
Matthew Williams303650c2015-04-17 18:22:51 -0700719 protected final void fullBackupFileTree(String packageName, String domain, String startingPath,
Michal Karpinskib5e09312018-02-19 13:55:23 +0000720 ArraySet<PathWithRequiredFlags> manifestExcludes,
Matthew Williams303650c2015-04-17 18:22:51 -0700721 ArraySet<String> systemExcludes,
722 FullBackupDataOutput output) {
723 // Pull out the domain and set it aside to use when making the tarball.
724 String domainPath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
725 if (domainPath == null) {
726 // Should never happen.
727 return;
728 }
729
730 File rootFile = new File(startingPath);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700731 if (rootFile.exists()) {
732 LinkedList<File> scanQueue = new LinkedList<File>();
733 scanQueue.add(rootFile);
734
735 while (scanQueue.size() > 0) {
736 File file = scanQueue.remove(0);
Christopher Tate2efd2db2011-07-19 16:32:49 -0700737 String filePath;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700738 try {
Christopher Tateda2018e2016-10-13 12:12:29 -0700739 // Ignore things that aren't "real" files or dirs
Christopher Tate85192a12015-09-24 10:21:54 -0700740 StructStat stat = Os.lstat(file.getPath());
Christopher Tateda2018e2016-10-13 12:12:29 -0700741 if (!OsConstants.S_ISREG(stat.st_mode)
742 && !OsConstants.S_ISDIR(stat.st_mode)) {
743 if (DEBUG) Log.i(TAG, "Not a file/dir (skipping)!: " + file);
Christopher Tate85192a12015-09-24 10:21:54 -0700744 continue;
745 }
746
747 // For all other verification, look at the canonicalized path
Christopher Tate2efd2db2011-07-19 16:32:49 -0700748 filePath = file.getCanonicalPath();
749
750 // prune this subtree?
Michal Karpinskib5e09312018-02-19 13:55:23 +0000751 if (manifestExcludes != null
752 && manifestExcludesContainFilePath(manifestExcludes, filePath)) {
Matthew Williams303650c2015-04-17 18:22:51 -0700753 continue;
754 }
755 if (systemExcludes != null && systemExcludes.contains(filePath)) {
Christopher Tate2efd2db2011-07-19 16:32:49 -0700756 continue;
757 }
758
759 // If it's a directory, enqueue its contents for scanning.
Christopher Tate85192a12015-09-24 10:21:54 -0700760 if (OsConstants.S_ISDIR(stat.st_mode)) {
Christopher Tate79ec80d2011-06-24 14:58:49 -0700761 File[] contents = file.listFiles();
762 if (contents != null) {
763 for (File entry : contents) {
764 scanQueue.add(0, entry);
765 }
766 }
767 }
Christopher Tate2efd2db2011-07-19 16:32:49 -0700768 } catch (IOException e) {
769 if (DEBUG) Log.w(TAG, "Error canonicalizing path of " + file);
Matthew Williams303650c2015-04-17 18:22:51 -0700770 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
771 Log.v(FullBackup.TAG_XML_PARSER, "Error canonicalizing path of " + file);
772 }
Christopher Tate2efd2db2011-07-19 16:32:49 -0700773 continue;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700774 } catch (ErrnoException e) {
775 if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e);
Matthew Williams303650c2015-04-17 18:22:51 -0700776 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
777 Log.v(FullBackup.TAG_XML_PARSER, "Error scanning file " + file + " : " + e);
778 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700779 continue;
780 }
781
Christopher Tate11ae7682015-03-24 18:48:10 -0700782 // Finally, back this file up (or measure it) before proceeding
Matthew Williams303650c2015-04-17 18:22:51 -0700783 FullBackup.backupToTar(packageName, domain, null, domainPath, filePath, output);
Christopher Tate79ec80d2011-06-24 14:58:49 -0700784 }
785 }
786 }
787
Michal Karpinskib5e09312018-02-19 13:55:23 +0000788 private boolean manifestExcludesContainFilePath(
789 ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath) {
790 for (PathWithRequiredFlags exclude : manifestExcludes) {
791 String excludePath = exclude.getPath();
792 if (excludePath != null && excludePath.equals(filePath)) {
793 return true;
794 }
795 }
796 return false;
797 }
798
Christopher Tate79ec80d2011-06-24 14:58:49 -0700799 /**
800 * Handle the data delivered via the given file descriptor during a full restore
801 * operation. The agent is given the path to the file's original location as well
802 * as its size and metadata.
803 * <p>
804 * The file descriptor can only be read for {@code size} bytes; attempting to read
805 * more data has undefined behavior.
806 * <p>
807 * The default implementation creates the destination file/directory and populates it
808 * with the data from the file descriptor, then sets the file's access mode and
809 * modification time to match the restore arguments.
810 *
811 * @param data A read-only file descriptor from which the agent can read {@code size}
812 * bytes of file data.
813 * @param size The number of bytes of file content to be restored to the given
814 * destination. If the file system object being restored is a directory, {@code size}
815 * will be zero.
816 * @param destination The File on disk to be restored with the given data.
817 * @param type The kind of file system object being restored. This will be either
818 * {@link BackupAgent#TYPE_FILE} or {@link BackupAgent#TYPE_DIRECTORY}.
819 * @param mode The access mode to be assigned to the destination after its data is
820 * written. This is in the standard format used by {@code chmod()}.
821 * @param mtime The modification time of the file when it was backed up, suitable to
822 * be assigned to the file after its data is written.
823 * @throws IOException
824 */
825 public void onRestoreFile(ParcelFileDescriptor data, long size,
826 File destination, int type, long mode, long mtime)
827 throws IOException {
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700828
829 final boolean accept = isFileEligibleForRestore(destination);
830 // If we don't accept the file, consume the bytes from the pipe anyway.
831 FullBackup.restoreFile(data, size, type, mode, mtime, accept ? destination : null);
832 }
833
834 private boolean isFileEligibleForRestore(File destination) throws IOException {
Matthew Williams303650c2015-04-17 18:22:51 -0700835 FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this);
836 if (!bs.isFullBackupContentEnabled()) {
837 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
838 Log.v(FullBackup.TAG_XML_PARSER,
839 "onRestoreFile \"" + destination.getCanonicalPath()
840 + "\" : fullBackupContent not enabled for " + getPackageName());
841 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700842 return false;
Matthew Williams303650c2015-04-17 18:22:51 -0700843 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700844
Michal Karpinskib5e09312018-02-19 13:55:23 +0000845 Map<String, Set<PathWithRequiredFlags>> includes = null;
846 ArraySet<PathWithRequiredFlags> excludes = null;
Matthew Williams303650c2015-04-17 18:22:51 -0700847 final String destinationCanonicalPath = destination.getCanonicalPath();
848 try {
849 includes = bs.maybeParseAndGetCanonicalIncludePaths();
850 excludes = bs.maybeParseAndGetCanonicalExcludePaths();
851 } catch (XmlPullParserException e) {
852 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
853 Log.v(FullBackup.TAG_XML_PARSER,
854 "onRestoreFile \"" + destinationCanonicalPath
855 + "\" : Exception trying to parse fullBackupContent xml file!"
856 + " Aborting onRestoreFile.", e);
857 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700858 return false;
Matthew Williams303650c2015-04-17 18:22:51 -0700859 }
860
861 if (excludes != null &&
Bernardo Rufino62863822018-06-27 12:50:44 +0100862 BackupUtils.isFileSpecifiedInPathList(destination, excludes)) {
Matthew Williams303650c2015-04-17 18:22:51 -0700863 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
864 Log.v(FullBackup.TAG_XML_PARSER,
865 "onRestoreFile: \"" + destinationCanonicalPath + "\": listed in"
866 + " excludes; skipping.");
867 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700868 return false;
Matthew Williams303650c2015-04-17 18:22:51 -0700869 }
870
871 if (includes != null && !includes.isEmpty()) {
872 // Rather than figure out the <include/> domain based on the path (a lot of code, and
873 // it's a small list), we'll go through and look for it.
874 boolean explicitlyIncluded = false;
Michal Karpinskib5e09312018-02-19 13:55:23 +0000875 for (Set<PathWithRequiredFlags> domainIncludes : includes.values()) {
Bernardo Rufino62863822018-06-27 12:50:44 +0100876 explicitlyIncluded |=
877 BackupUtils.isFileSpecifiedInPathList(destination, domainIncludes);
Matthew Williams303650c2015-04-17 18:22:51 -0700878 if (explicitlyIncluded) {
879 break;
880 }
881 }
882 if (!explicitlyIncluded) {
883 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
884 Log.v(FullBackup.TAG_XML_PARSER,
885 "onRestoreFile: Trying to restore \""
886 + destinationCanonicalPath + "\" but it isn't specified"
887 + " in the included files; skipping.");
888 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700889 return false;
Matthew Williams303650c2015-04-17 18:22:51 -0700890 }
891 }
Matthew Williamsb9ebed52015-08-05 18:27:44 -0700892 return true;
Christopher Tate79ec80d2011-06-24 14:58:49 -0700893 }
894
895 /**
896 * Only specialized platform agents should overload this entry point to support
897 * restores to crazy non-app locations.
898 * @hide
899 */
900 protected void onRestoreFile(ParcelFileDescriptor data, long size,
901 int type, String domain, String path, long mode, long mtime)
902 throws IOException {
903 String basePath = null;
904
905 if (DEBUG) Log.d(TAG, "onRestoreFile() size=" + size + " type=" + type
906 + " domain=" + domain + " relpath=" + path + " mode=" + mode
907 + " mtime=" + mtime);
908
Matthew Williams303650c2015-04-17 18:22:51 -0700909 basePath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
910 if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
911 mode = -1; // < 0 is a token to skip attempting a chmod()
Christopher Tate79ec80d2011-06-24 14:58:49 -0700912 }
913
914 // Now that we've figured out where the data goes, send it on its way
915 if (basePath != null) {
Christopher Tate73237652013-03-25 10:06:34 -0700916 // Canonicalize the nominal path and verify that it lies within the stated domain
Christopher Tate79ec80d2011-06-24 14:58:49 -0700917 File outFile = new File(basePath, path);
Christopher Tate73237652013-03-25 10:06:34 -0700918 String outPath = outFile.getCanonicalPath();
919 if (outPath.startsWith(basePath + File.separatorChar)) {
920 if (DEBUG) Log.i(TAG, "[" + domain + " : " + path + "] mapped to " + outPath);
921 onRestoreFile(data, size, outFile, type, mode, mtime);
922 return;
923 } else {
924 // Attempt to restore to a path outside the file's nominal domain.
925 if (DEBUG) {
926 Log.e(TAG, "Cross-domain restore attempt: " + outPath);
927 }
928 }
Christopher Tate79ec80d2011-06-24 14:58:49 -0700929 }
Christopher Tate73237652013-03-25 10:06:34 -0700930
931 // Not a supported output location, or bad path: we need to consume the data
932 // anyway, so just use the default "copy the data out" implementation
933 // with a null destination.
934 if (DEBUG) Log.i(TAG, "[ skipping file " + path + "]");
935 FullBackup.restoreFile(data, size, type, mode, mtime, null);
Christopher Tate4a627c72011-04-01 14:43:32 -0700936 }
Christopher Tate487529a2009-04-29 14:03:25 -0700937
Christopher Tate2e40d112014-07-15 12:37:38 -0700938 /**
939 * The application's restore operation has completed. This method is called after
940 * all available data has been delivered to the application for restore (via either
941 * the {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} or
942 * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()}
943 * callbacks). This provides the app with a stable end-of-restore opportunity to
944 * perform any appropriate post-processing on the data that was just delivered.
945 *
946 * @see #onRestore(BackupDataInput, int, ParcelFileDescriptor)
947 * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
948 */
949 public void onRestoreFinished() {
950 }
951
Christopher Tate487529a2009-04-29 14:03:25 -0700952 // ----- Core implementation -----
Christopher Tate44a27902010-01-27 17:15:49 -0800953
954 /** @hide */
955 public final IBinder onBind() {
Christopher Tate181fafa2009-05-14 11:12:14 -0700956 return mBinder;
Christopher Tate487529a2009-04-29 14:03:25 -0700957 }
958
959 private final IBinder mBinder = new BackupServiceBinder().asBinder();
960
Christopher Tate181fafa2009-05-14 11:12:14 -0700961 /** @hide */
962 public void attach(Context context) {
963 attachBaseContext(context);
964 }
965
Christopher Tate487529a2009-04-29 14:03:25 -0700966 // ----- IBackupService binder interface -----
Christopher Tate181fafa2009-05-14 11:12:14 -0700967 private class BackupServiceBinder extends IBackupAgent.Stub {
968 private static final String TAG = "BackupServiceBinder";
969
Christopher Tate75a99702011-05-18 16:28:19 -0700970 @Override
Bernardo Rufino2d87f452018-06-22 11:47:49 +0100971 public void doBackup(
972 ParcelFileDescriptor oldState,
Christopher Tate22b87872009-05-04 16:41:53 -0700973 ParcelFileDescriptor data,
Christopher Tate44a27902010-01-27 17:15:49 -0800974 ParcelFileDescriptor newState,
Bernardo Rufino2d87f452018-06-22 11:47:49 +0100975 long quotaBytes,
976 IBackupCallback callbackBinder,
977 int transportFlags) throws RemoteException {
Christopher Tate19024922010-01-22 16:39:53 -0800978 // Ensure that we're running with the app's normal permission level
Christopher Tate44a27902010-01-27 17:15:49 -0800979 long ident = Binder.clearCallingIdentity();
Christopher Tate19024922010-01-22 16:39:53 -0800980
Christopher Tate436344a2009-09-30 16:17:37 -0700981 if (DEBUG) Log.v(TAG, "doBackup() invoked");
Robert Berry39194c02018-01-11 13:50:56 +0000982 BackupDataOutput output = new BackupDataOutput(
983 data.getFileDescriptor(), quotaBytes, transportFlags);
Christopher Tate4a627c72011-04-01 14:43:32 -0700984
Bernardo Rufino6a422d62018-08-28 13:19:20 +0100985 long result = RESULT_ERROR;
Joe Onorato290bb012009-05-13 18:57:29 -0400986 try {
Christopher Tate181fafa2009-05-14 11:12:14 -0700987 BackupAgent.this.onBackup(oldState, output, newState);
Bernardo Rufino6a422d62018-08-28 13:19:20 +0100988 result = RESULT_SUCCESS;
Joe Onorato4ababd92009-06-25 18:29:18 -0400989 } catch (IOException ex) {
990 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
991 throw new RuntimeException(ex);
Joe Onorato290bb012009-05-13 18:57:29 -0400992 } catch (RuntimeException ex) {
Joe Onorato83248c42009-06-17 17:55:20 -0700993 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
Joe Onorato290bb012009-05-13 18:57:29 -0400994 throw ex;
Christopher Tate19024922010-01-22 16:39:53 -0800995 } finally {
Christopher Tatef85f5b22013-04-18 16:57:43 -0700996 // Ensure that any SharedPreferences writes have landed after the backup,
997 // in case the app code has side effects (since apps cannot provide this
998 // guarantee themselves).
999 waitForSharedPrefs();
1000
Christopher Tate44a27902010-01-27 17:15:49 -08001001 Binder.restoreCallingIdentity(ident);
1002 try {
Bernardo Rufino6a422d62018-08-28 13:19:20 +01001003 callbackBinder.operationComplete(result);
Christopher Tate44a27902010-01-27 17:15:49 -08001004 } catch (RemoteException e) {
Bernardo Rufino74eee102018-08-29 16:01:49 +01001005 // We will time out anyway.
Christopher Tate44a27902010-01-27 17:15:49 -08001006 }
Christopher Tate91bb0e52016-09-30 17:52:19 -07001007
1008 // Don't close the fd out from under the system service if this was local
1009 if (Binder.getCallingPid() != Process.myPid()) {
1010 IoUtils.closeQuietly(oldState);
1011 IoUtils.closeQuietly(data);
1012 IoUtils.closeQuietly(newState);
1013 }
Joe Onorato290bb012009-05-13 18:57:29 -04001014 }
Christopher Tate487529a2009-04-29 14:03:25 -07001015 }
1016
Christopher Tate75a99702011-05-18 16:28:19 -07001017 @Override
Dianne Hackborn3accca02013-09-20 09:32:11 -07001018 public void doRestore(ParcelFileDescriptor data, long appVersionCode,
Christopher Tate44a27902010-01-27 17:15:49 -08001019 ParcelFileDescriptor newState,
1020 int token, IBackupManager callbackBinder) throws RemoteException {
Christopher Tate19024922010-01-22 16:39:53 -08001021 // Ensure that we're running with the app's normal permission level
Christopher Tate44a27902010-01-27 17:15:49 -08001022 long ident = Binder.clearCallingIdentity();
Christopher Tate19024922010-01-22 16:39:53 -08001023
Christopher Tate436344a2009-09-30 16:17:37 -07001024 if (DEBUG) Log.v(TAG, "doRestore() invoked");
Christopher Tatefe2368c2017-05-17 15:42:35 -07001025
1026 // Ensure that any side-effect SharedPreferences writes have landed *before*
1027 // we may be about to rewrite the file out from underneath
1028 waitForSharedPrefs();
1029
Joe Onorato83248c42009-06-17 17:55:20 -07001030 BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
1031 try {
Christopher Tate5cbbf562009-06-22 16:44:51 -07001032 BackupAgent.this.onRestore(input, appVersionCode, newState);
Joe Onorato83248c42009-06-17 17:55:20 -07001033 } catch (IOException ex) {
1034 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
1035 throw new RuntimeException(ex);
1036 } catch (RuntimeException ex) {
1037 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
1038 throw ex;
Christopher Tate19024922010-01-22 16:39:53 -08001039 } finally {
Christopher Tatefe2368c2017-05-17 15:42:35 -07001040 // And bring live SharedPreferences instances up to date
1041 reloadSharedPreferences();
Christopher Tatef85f5b22013-04-18 16:57:43 -07001042
Christopher Tate44a27902010-01-27 17:15:49 -08001043 Binder.restoreCallingIdentity(ident);
1044 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001045 callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
Christopher Tate44a27902010-01-27 17:15:49 -08001046 } catch (RemoteException e) {
1047 // we'll time out anyway, so we're safe
1048 }
Christopher Tate91bb0e52016-09-30 17:52:19 -07001049
1050 if (Binder.getCallingPid() != Process.myPid()) {
1051 IoUtils.closeQuietly(data);
1052 IoUtils.closeQuietly(newState);
1053 }
Joe Onorato83248c42009-06-17 17:55:20 -07001054 }
Christopher Tate487529a2009-04-29 14:03:25 -07001055 }
Christopher Tate75a99702011-05-18 16:28:19 -07001056
1057 @Override
Christopher Tate79ec80d2011-06-24 14:58:49 -07001058 public void doFullBackup(ParcelFileDescriptor data,
Robert Berry39194c02018-01-11 13:50:56 +00001059 long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags) {
Christopher Tate79ec80d2011-06-24 14:58:49 -07001060 // Ensure that we're running with the app's normal permission level
1061 long ident = Binder.clearCallingIdentity();
1062
1063 if (DEBUG) Log.v(TAG, "doFullBackup() invoked");
Christopher Tate79ec80d2011-06-24 14:58:49 -07001064
Christopher Tatef85f5b22013-04-18 16:57:43 -07001065 // Ensure that any SharedPreferences writes have landed *before*
1066 // we potentially try to back up the underlying files directly.
1067 waitForSharedPrefs();
1068
Christopher Tate79ec80d2011-06-24 14:58:49 -07001069 try {
Robert Berry39194c02018-01-11 13:50:56 +00001070 BackupAgent.this.onFullBackup(new FullBackupDataOutput(
1071 data, quotaBytes, transportFlags));
Christopher Tate79ec80d2011-06-24 14:58:49 -07001072 } catch (IOException ex) {
Christopher Tate11ae7682015-03-24 18:48:10 -07001073 Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001074 throw new RuntimeException(ex);
1075 } catch (RuntimeException ex) {
Christopher Tate11ae7682015-03-24 18:48:10 -07001076 Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001077 throw ex;
1078 } finally {
Christopher Tatef85f5b22013-04-18 16:57:43 -07001079 // ... and then again after, as in the doBackup() case
1080 waitForSharedPrefs();
1081
Christopher Tate7926a692011-07-11 11:31:57 -07001082 // Send the EOD marker indicating that there is no more data
1083 // forthcoming from this agent.
1084 try {
1085 FileOutputStream out = new FileOutputStream(data.getFileDescriptor());
1086 byte[] buf = new byte[4];
1087 out.write(buf);
1088 } catch (IOException e) {
1089 Log.e(TAG, "Unable to finalize backup stream!");
1090 }
1091
Christopher Tate79ec80d2011-06-24 14:58:49 -07001092 Binder.restoreCallingIdentity(ident);
1093 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001094 callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001095 } catch (RemoteException e) {
1096 // we'll time out anyway, so we're safe
1097 }
Christopher Tate91bb0e52016-09-30 17:52:19 -07001098
1099 if (Binder.getCallingPid() != Process.myPid()) {
1100 IoUtils.closeQuietly(data);
1101 }
Christopher Tate79ec80d2011-06-24 14:58:49 -07001102 }
1103 }
1104
Robert Berry39194c02018-01-11 13:50:56 +00001105 public void doMeasureFullBackup(long quotaBytes, int token, IBackupManager callbackBinder,
1106 int transportFlags) {
Christopher Tate11ae7682015-03-24 18:48:10 -07001107 // Ensure that we're running with the app's normal permission level
1108 final long ident = Binder.clearCallingIdentity();
Robert Berry39194c02018-01-11 13:50:56 +00001109 FullBackupDataOutput measureOutput =
1110 new FullBackupDataOutput(quotaBytes, transportFlags);
Shreyas Basargeb6e73c92017-01-31 20:13:43 +00001111
Christopher Tate11ae7682015-03-24 18:48:10 -07001112 waitForSharedPrefs();
1113 try {
1114 BackupAgent.this.onFullBackup(measureOutput);
1115 } catch (IOException ex) {
1116 Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
1117 throw new RuntimeException(ex);
1118 } catch (RuntimeException ex) {
1119 Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
1120 throw ex;
1121 } finally {
1122 Binder.restoreCallingIdentity(ident);
1123 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001124 callbackBinder.opCompleteForUser(getBackupUserId(), token,
1125 measureOutput.getSize());
Christopher Tate11ae7682015-03-24 18:48:10 -07001126 } catch (RemoteException e) {
1127 // timeout, so we're safe
1128 }
1129 }
1130 }
1131
Christopher Tate79ec80d2011-06-24 14:58:49 -07001132 @Override
Christopher Tate75a99702011-05-18 16:28:19 -07001133 public void doRestoreFile(ParcelFileDescriptor data, long size,
1134 int type, String domain, String path, long mode, long mtime,
1135 int token, IBackupManager callbackBinder) throws RemoteException {
1136 long ident = Binder.clearCallingIdentity();
1137 try {
Christopher Tate75a99702011-05-18 16:28:19 -07001138 BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
1139 } catch (IOException e) {
Christopher Tate11ae7682015-03-24 18:48:10 -07001140 Log.d(TAG, "onRestoreFile (" + BackupAgent.this.getClass().getName() + ") threw", e);
Christopher Tate75a99702011-05-18 16:28:19 -07001141 throw new RuntimeException(e);
1142 } finally {
Christopher Tatef85f5b22013-04-18 16:57:43 -07001143 // Ensure that any side-effect SharedPreferences writes have landed
1144 waitForSharedPrefs();
Christopher Tatefe2368c2017-05-17 15:42:35 -07001145 // And bring live SharedPreferences instances up to date
1146 reloadSharedPreferences();
Christopher Tatef85f5b22013-04-18 16:57:43 -07001147
Christopher Tate75a99702011-05-18 16:28:19 -07001148 Binder.restoreCallingIdentity(ident);
1149 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001150 callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
Christopher Tate75a99702011-05-18 16:28:19 -07001151 } catch (RemoteException e) {
1152 // we'll time out anyway, so we're safe
1153 }
Christopher Tate91bb0e52016-09-30 17:52:19 -07001154
1155 if (Binder.getCallingPid() != Process.myPid()) {
1156 IoUtils.closeQuietly(data);
1157 }
Christopher Tate75a99702011-05-18 16:28:19 -07001158 }
1159 }
Christopher Tatecba59412014-04-01 10:38:29 -07001160
1161 @Override
Christopher Tate2e40d112014-07-15 12:37:38 -07001162 public void doRestoreFinished(int token, IBackupManager callbackBinder) {
1163 long ident = Binder.clearCallingIdentity();
1164 try {
1165 BackupAgent.this.onRestoreFinished();
Christopher Tate11ae7682015-03-24 18:48:10 -07001166 } catch (Exception e) {
1167 Log.d(TAG, "onRestoreFinished (" + BackupAgent.this.getClass().getName() + ") threw", e);
1168 throw e;
Christopher Tate2e40d112014-07-15 12:37:38 -07001169 } finally {
1170 // Ensure that any side-effect SharedPreferences writes have landed
1171 waitForSharedPrefs();
1172
1173 Binder.restoreCallingIdentity(ident);
1174 try {
Ruslan Tkhakokhov6712b722019-01-19 21:35:32 +00001175 callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
Christopher Tate2e40d112014-07-15 12:37:38 -07001176 } catch (RemoteException e) {
1177 // we'll time out anyway, so we're safe
1178 }
1179 }
1180 }
1181
1182 @Override
Christopher Tatecba59412014-04-01 10:38:29 -07001183 public void fail(String message) {
1184 getHandler().post(new FailRunnable(message));
1185 }
Sergey Poromov872d3b62016-01-12 15:48:08 +01001186
1187 @Override
Bernardo Rufino74eee102018-08-29 16:01:49 +01001188 public void doQuotaExceeded(
1189 long backupDataBytes,
1190 long quotaBytes,
1191 IBackupCallback callbackBinder) {
Sergey Poromov872d3b62016-01-12 15:48:08 +01001192 long ident = Binder.clearCallingIdentity();
Bernardo Rufino74eee102018-08-29 16:01:49 +01001193
1194 long result = RESULT_ERROR;
Sergey Poromov872d3b62016-01-12 15:48:08 +01001195 try {
1196 BackupAgent.this.onQuotaExceeded(backupDataBytes, quotaBytes);
Bernardo Rufino74eee102018-08-29 16:01:49 +01001197 result = RESULT_SUCCESS;
Sergey Poromov872d3b62016-01-12 15:48:08 +01001198 } catch (Exception e) {
1199 Log.d(TAG, "onQuotaExceeded(" + BackupAgent.this.getClass().getName() + ") threw",
1200 e);
1201 throw e;
1202 } finally {
1203 waitForSharedPrefs();
1204 Binder.restoreCallingIdentity(ident);
Bernardo Rufino74eee102018-08-29 16:01:49 +01001205
1206 try {
1207 callbackBinder.operationComplete(result);
1208 } catch (RemoteException e) {
1209 // We will time out anyway.
1210 }
Sergey Poromov872d3b62016-01-12 15:48:08 +01001211 }
1212 }
Christopher Tatecba59412014-04-01 10:38:29 -07001213 }
1214
1215 static class FailRunnable implements Runnable {
1216 private String mMessage;
1217
1218 FailRunnable(String message) {
1219 mMessage = message;
1220 }
1221
1222 @Override
1223 public void run() {
1224 throw new IllegalStateException(mMessage);
1225 }
Christopher Tate487529a2009-04-29 14:03:25 -07001226 }
1227}