blob: 6afccec4df04f94eb17c7855a275a48636545a51 [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
17package com.android.server;
18
Christopher Tate181fafa2009-05-14 11:12:14 -070019import android.app.ActivityManagerNative;
Christopher Tateb6787f22009-07-02 17:40:45 -070020import android.app.AlarmManager;
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -070021import android.app.AppGlobals;
Christopher Tate181fafa2009-05-14 11:12:14 -070022import android.app.IActivityManager;
23import android.app.IApplicationThread;
24import android.app.IBackupAgent;
Christopher Tateb6787f22009-07-02 17:40:45 -070025import android.app.PendingIntent;
Christopher Tate79ec80d2011-06-24 14:58:49 -070026import android.app.backup.BackupAgent;
Christopher Tate4a627c72011-04-01 14:43:32 -070027import android.app.backup.BackupDataOutput;
28import android.app.backup.FullBackup;
Jason parksa3cdaa52011-01-13 14:15:43 -060029import android.app.backup.RestoreSet;
Christopher Tate45281862010-03-05 15:46:30 -080030import android.app.backup.IBackupManager;
Christopher Tate4a627c72011-04-01 14:43:32 -070031import android.app.backup.IFullBackupRestoreObserver;
Christopher Tate45281862010-03-05 15:46:30 -080032import android.app.backup.IRestoreObserver;
33import android.app.backup.IRestoreSession;
Christopher Tate4a627c72011-04-01 14:43:32 -070034import android.content.ActivityNotFoundException;
Christopher Tate3799bc22009-05-06 16:13:56 -070035import android.content.BroadcastReceiver;
Dan Egnor87a02bc2009-06-17 02:30:10 -070036import android.content.ComponentName;
Christopher Tate487529a2009-04-29 14:03:25 -070037import android.content.Context;
38import android.content.Intent;
Christopher Tate3799bc22009-05-06 16:13:56 -070039import android.content.IntentFilter;
Dan Egnor87a02bc2009-06-17 02:30:10 -070040import android.content.ServiceConnection;
Christopher Tate181fafa2009-05-14 11:12:14 -070041import android.content.pm.ApplicationInfo;
Christopher Tatec7b31e32009-06-10 15:49:30 -070042import android.content.pm.IPackageDataObserver;
Christopher Tatea858cb02011-06-03 12:27:51 -070043import android.content.pm.IPackageDeleteObserver;
Christopher Tate75a99702011-05-18 16:28:19 -070044import android.content.pm.IPackageInstallObserver;
Christopher Tate1bb69062010-02-19 17:02:12 -080045import android.content.pm.IPackageManager;
Christopher Tate7b881282009-06-07 13:52:37 -070046import android.content.pm.PackageInfo;
Dan Egnor87a02bc2009-06-17 02:30:10 -070047import android.content.pm.PackageManager;
Jason parks1125d782011-01-12 09:47:26 -060048import android.content.pm.Signature;
Jason parksa3cdaa52011-01-13 14:15:43 -060049import android.content.pm.PackageManager.NameNotFoundException;
Christopher Tate3799bc22009-05-06 16:13:56 -070050import android.net.Uri;
Christopher Tate487529a2009-04-29 14:03:25 -070051import android.os.Binder;
Christopher Tate75a99702011-05-18 16:28:19 -070052import android.os.Build;
Christopher Tate3799bc22009-05-06 16:13:56 -070053import android.os.Bundle;
Christopher Tate22b87872009-05-04 16:41:53 -070054import android.os.Environment;
Christopher Tate487529a2009-04-29 14:03:25 -070055import android.os.Handler;
Christopher Tate44a27902010-01-27 17:15:49 -080056import android.os.HandlerThread;
Christopher Tate487529a2009-04-29 14:03:25 -070057import android.os.IBinder;
Christopher Tate44a27902010-01-27 17:15:49 -080058import android.os.Looper;
Christopher Tate487529a2009-04-29 14:03:25 -070059import android.os.Message;
Christopher Tate22b87872009-05-04 16:41:53 -070060import android.os.ParcelFileDescriptor;
Christopher Tateb6787f22009-07-02 17:40:45 -070061import android.os.PowerManager;
Christopher Tate043dadc2009-06-02 16:11:00 -070062import android.os.Process;
Christopher Tate487529a2009-04-29 14:03:25 -070063import android.os.RemoteException;
Dan Egnorbb9001c2009-07-27 12:20:13 -070064import android.os.SystemClock;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070065import android.os.WorkSource;
Oscar Montemayora8529f62009-11-18 10:14:20 -080066import android.provider.Settings;
Dan Egnorbb9001c2009-07-27 12:20:13 -070067import android.util.EventLog;
Christopher Tate79ec80d2011-06-24 14:58:49 -070068import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080069import android.util.Slog;
Christopher Tate487529a2009-04-29 14:03:25 -070070import android.util.SparseArray;
Christopher Tate44a27902010-01-27 17:15:49 -080071import android.util.SparseIntArray;
Christopher Tate4a627c72011-04-01 14:43:32 -070072import android.util.StringBuilderPrinter;
73
Jason parksa3cdaa52011-01-13 14:15:43 -060074import com.android.internal.backup.BackupConstants;
75import com.android.internal.backup.IBackupTransport;
76import com.android.internal.backup.LocalTransport;
77import com.android.server.PackageManagerBackupAgent.Metadata;
78
Christopher Tatecde87f42009-06-12 12:55:53 -070079import java.io.EOFException;
Christopher Tate22b87872009-05-04 16:41:53 -070080import java.io.File;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070081import java.io.FileDescriptor;
Christopher Tate75a99702011-05-18 16:28:19 -070082import java.io.FileInputStream;
Christopher Tate1168baa2010-02-17 13:03:40 -080083import java.io.FileNotFoundException;
Christopher Tate4cc86e12009-09-21 19:36:51 -070084import java.io.FileOutputStream;
Christopher Tatec7b31e32009-06-10 15:49:30 -070085import java.io.IOException;
Christopher Tate75a99702011-05-18 16:28:19 -070086import java.io.InputStream;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070087import java.io.PrintWriter;
Christopher Tatecde87f42009-06-12 12:55:53 -070088import java.io.RandomAccessFile;
Christopher Tate75a99702011-05-18 16:28:19 -070089import java.text.SimpleDateFormat;
Joe Onorato8ad02812009-05-13 01:41:44 -040090import java.util.ArrayList;
Christopher Tate75a99702011-05-18 16:28:19 -070091import java.util.Date;
Joe Onorato8ad02812009-05-13 01:41:44 -040092import java.util.HashMap;
Christopher Tate487529a2009-04-29 14:03:25 -070093import java.util.HashSet;
94import java.util.List;
Christopher Tate91717492009-06-26 21:07:13 -070095import java.util.Map;
Dan Egnorc1c49c02009-10-30 17:35:39 -070096import java.util.Random;
Christopher Tateb49ceb32010-02-08 16:22:24 -080097import java.util.Set;
Christopher Tate4a627c72011-04-01 14:43:32 -070098import java.util.concurrent.atomic.AtomicBoolean;
Christopher Tate487529a2009-04-29 14:03:25 -070099
100class BackupManagerService extends IBackupManager.Stub {
101 private static final String TAG = "BackupManagerService";
Christopher Tate4a627c72011-04-01 14:43:32 -0700102 private static final boolean DEBUG = true;
103
104 // Name and current contents version of the full-backup manifest file
105 static final String BACKUP_MANIFEST_FILENAME = "_manifest";
106 static final int BACKUP_MANIFEST_VERSION = 1;
Christopher Tateaa088442009-06-16 18:25:46 -0700107
Christopher Tate49401dd2009-07-01 12:34:29 -0700108 // How often we perform a backup pass. Privileged external callers can
109 // trigger an immediate pass.
Christopher Tateb6787f22009-07-02 17:40:45 -0700110 private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
Christopher Tate487529a2009-04-29 14:03:25 -0700111
Dan Egnorc1c49c02009-10-30 17:35:39 -0700112 // Random variation in backup scheduling time to avoid server load spikes
113 private static final int FUZZ_MILLIS = 5 * 60 * 1000;
114
Christopher Tate8031a3d2009-07-06 16:36:05 -0700115 // The amount of time between the initial provisioning of the device and
116 // the first backup pass.
117 private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
118
Christopher Tate45281862010-03-05 15:46:30 -0800119 private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
120 private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
121 private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
Christopher Tate487529a2009-04-29 14:03:25 -0700122 private static final int MSG_RUN_BACKUP = 1;
Christopher Tate043dadc2009-06-02 16:11:00 -0700123 private static final int MSG_RUN_FULL_BACKUP = 2;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700124 private static final int MSG_RUN_RESTORE = 3;
Christopher Tateee0e78a2009-07-02 11:17:03 -0700125 private static final int MSG_RUN_CLEAR = 4;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700126 private static final int MSG_RUN_INITIALIZE = 5;
Christopher Tate2d449afe2010-03-29 19:14:24 -0700127 private static final int MSG_RUN_GET_RESTORE_SETS = 6;
128 private static final int MSG_TIMEOUT = 7;
Christopher Tate73a3cb32010-12-13 18:27:26 -0800129 private static final int MSG_RESTORE_TIMEOUT = 8;
Christopher Tate4a627c72011-04-01 14:43:32 -0700130 private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
131 private static final int MSG_RUN_FULL_RESTORE = 10;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700132
133 // Timeout interval for deciding that a bind or clear-data has taken too long
134 static final long TIMEOUT_INTERVAL = 10 * 1000;
135
Christopher Tate44a27902010-01-27 17:15:49 -0800136 // Timeout intervals for agent backup & restore operations
137 static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
Christopher Tate4a627c72011-04-01 14:43:32 -0700138 static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
Christopher Tateb0628bf2011-06-02 15:08:13 -0700139 static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
Christopher Tate44a27902010-01-27 17:15:49 -0800140 static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
141
Christopher Tate4a627c72011-04-01 14:43:32 -0700142 // User confirmation timeout for a full backup/restore operation
143 static final long TIMEOUT_FULL_CONFIRMATION = 30 * 1000;
144
Christopher Tate487529a2009-04-29 14:03:25 -0700145 private Context mContext;
146 private PackageManager mPackageManager;
Christopher Tate1bb69062010-02-19 17:02:12 -0800147 IPackageManager mPackageManagerBinder;
Christopher Tate6ef58a12009-06-29 14:56:28 -0700148 private IActivityManager mActivityManager;
Christopher Tateb6787f22009-07-02 17:40:45 -0700149 private PowerManager mPowerManager;
150 private AlarmManager mAlarmManager;
Christopher Tate44a27902010-01-27 17:15:49 -0800151 IBackupManager mBackupManagerBinder;
Christopher Tateb6787f22009-07-02 17:40:45 -0700152
Christopher Tate73e02522009-07-15 14:18:26 -0700153 boolean mEnabled; // access to this is synchronized on 'this'
154 boolean mProvisioned;
Christopher Tatecce9da52010-02-03 15:11:15 -0800155 boolean mAutoRestore;
Christopher Tate73e02522009-07-15 14:18:26 -0700156 PowerManager.WakeLock mWakelock;
Christopher Tate44a27902010-01-27 17:15:49 -0800157 HandlerThread mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
158 BackupHandler mBackupHandler;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700159 PendingIntent mRunBackupIntent, mRunInitIntent;
160 BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
Christopher Tate487529a2009-04-29 14:03:25 -0700161 // map UIDs to the set of backup client services within that UID's app set
Christopher Tate73e02522009-07-15 14:18:26 -0700162 final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
Christopher Tate181fafa2009-05-14 11:12:14 -0700163 = new SparseArray<HashSet<ApplicationInfo>>();
Christopher Tate487529a2009-04-29 14:03:25 -0700164 // set of backup services that have pending changes
Christopher Tate73e02522009-07-15 14:18:26 -0700165 class BackupRequest {
Christopher Tate181fafa2009-05-14 11:12:14 -0700166 public ApplicationInfo appInfo;
Christopher Tateaa088442009-06-16 18:25:46 -0700167
Christopher Tate4a627c72011-04-01 14:43:32 -0700168 BackupRequest(ApplicationInfo app) {
Christopher Tate181fafa2009-05-14 11:12:14 -0700169 appInfo = app;
Christopher Tate46758122009-05-06 11:22:00 -0700170 }
Christopher Tate181fafa2009-05-14 11:12:14 -0700171
172 public String toString() {
Christopher Tate4a627c72011-04-01 14:43:32 -0700173 return "BackupRequest{app=" + appInfo + "}";
Christopher Tate181fafa2009-05-14 11:12:14 -0700174 }
Christopher Tate46758122009-05-06 11:22:00 -0700175 }
Christopher Tatec28083a2010-12-14 16:16:44 -0800176 // Backups that we haven't started yet. Keys are package names.
177 HashMap<String,BackupRequest> mPendingBackups
178 = new HashMap<String,BackupRequest>();
Christopher Tate5cb400b2009-06-25 16:03:14 -0700179
180 // Pseudoname that we use for the Package Manager metadata "package"
Christopher Tate73e02522009-07-15 14:18:26 -0700181 static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
Christopher Tate6aa41f42009-06-19 14:14:22 -0700182
183 // locking around the pending-backup management
Christopher Tate73e02522009-07-15 14:18:26 -0700184 final Object mQueueLock = new Object();
Christopher Tate487529a2009-04-29 14:03:25 -0700185
Christopher Tate043dadc2009-06-02 16:11:00 -0700186 // The thread performing the sequence of queued backups binds to each app's agent
187 // in succession. Bind notifications are asynchronously delivered through the
188 // Activity Manager; use this lock object to signal when a requested binding has
189 // completed.
Christopher Tate73e02522009-07-15 14:18:26 -0700190 final Object mAgentConnectLock = new Object();
191 IBackupAgent mConnectedAgent;
192 volatile boolean mConnecting;
Christopher Tate55f931a2009-09-29 17:17:34 -0700193 volatile long mLastBackupPass;
194 volatile long mNextBackupPass;
Christopher Tate043dadc2009-06-02 16:11:00 -0700195
Christopher Tate55f931a2009-09-29 17:17:34 -0700196 // A similar synchronization mechanism around clearing apps' data for restore
Christopher Tate73e02522009-07-15 14:18:26 -0700197 final Object mClearDataLock = new Object();
198 volatile boolean mClearingData;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700199
Christopher Tate91717492009-06-26 21:07:13 -0700200 // Transport bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700201 final HashMap<String,IBackupTransport> mTransports
Christopher Tate91717492009-06-26 21:07:13 -0700202 = new HashMap<String,IBackupTransport>();
Christopher Tate73e02522009-07-15 14:18:26 -0700203 String mCurrentTransport;
204 IBackupTransport mLocalTransport, mGoogleTransport;
Christopher Tate80202c82010-01-25 19:37:47 -0800205 ActiveRestoreSession mActiveRestoreSession;
Christopher Tate043dadc2009-06-02 16:11:00 -0700206
Christopher Tate2d449afe2010-03-29 19:14:24 -0700207 class RestoreGetSetsParams {
208 public IBackupTransport transport;
209 public ActiveRestoreSession session;
210 public IRestoreObserver observer;
211
212 RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
213 IRestoreObserver _observer) {
214 transport = _transport;
215 session = _session;
216 observer = _observer;
217 }
218 }
219
Christopher Tate73e02522009-07-15 14:18:26 -0700220 class RestoreParams {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700221 public IBackupTransport transport;
222 public IRestoreObserver observer;
Dan Egnor156411d2009-06-26 13:20:02 -0700223 public long token;
Christopher Tate84725812010-02-04 15:52:40 -0800224 public PackageInfo pkgInfo;
Christopher Tate1bb69062010-02-19 17:02:12 -0800225 public int pmToken; // in post-install restore, the PM's token for this transaction
Chris Tate249345b2010-10-29 12:57:04 -0700226 public boolean needFullBackup;
Christopher Tate84725812010-02-04 15:52:40 -0800227
228 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
Chris Tate249345b2010-10-29 12:57:04 -0700229 long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
Christopher Tate84725812010-02-04 15:52:40 -0800230 transport = _transport;
231 observer = _obs;
232 token = _token;
233 pkgInfo = _pkg;
Christopher Tate1bb69062010-02-19 17:02:12 -0800234 pmToken = _pmToken;
Chris Tate249345b2010-10-29 12:57:04 -0700235 needFullBackup = _needFullBackup;
Christopher Tate84725812010-02-04 15:52:40 -0800236 }
Christopher Tate7d562ec2009-06-25 18:03:43 -0700237
Chris Tate249345b2010-10-29 12:57:04 -0700238 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
239 boolean _needFullBackup) {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700240 transport = _transport;
241 observer = _obs;
Dan Egnor156411d2009-06-26 13:20:02 -0700242 token = _token;
Christopher Tate84725812010-02-04 15:52:40 -0800243 pkgInfo = null;
Christopher Tate1bb69062010-02-19 17:02:12 -0800244 pmToken = 0;
Chris Tate249345b2010-10-29 12:57:04 -0700245 needFullBackup = _needFullBackup;
Christopher Tate7d562ec2009-06-25 18:03:43 -0700246 }
247 }
248
Christopher Tate73e02522009-07-15 14:18:26 -0700249 class ClearParams {
Christopher Tateee0e78a2009-07-02 11:17:03 -0700250 public IBackupTransport transport;
251 public PackageInfo packageInfo;
252
253 ClearParams(IBackupTransport _transport, PackageInfo _info) {
254 transport = _transport;
255 packageInfo = _info;
256 }
257 }
258
Christopher Tate4a627c72011-04-01 14:43:32 -0700259 class FullParams {
260 public ParcelFileDescriptor fd;
261 public final AtomicBoolean latch;
262 public IFullBackupRestoreObserver observer;
263
264 FullParams() {
265 latch = new AtomicBoolean(false);
266 }
267 }
268
269 class FullBackupParams extends FullParams {
270 public boolean includeApks;
271 public boolean includeShared;
272 public boolean allApps;
273 public String[] packages;
274
275 FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
276 boolean doAllApps, String[] pkgList) {
277 fd = output;
278 includeApks = saveApks;
279 includeShared = saveShared;
280 allApps = doAllApps;
281 packages = pkgList;
282 }
283 }
284
285 class FullRestoreParams extends FullParams {
286 FullRestoreParams(ParcelFileDescriptor input) {
287 fd = input;
288 }
289 }
290
Christopher Tate44a27902010-01-27 17:15:49 -0800291 // Bookkeeping of in-flight operations for timeout etc. purposes. The operation
292 // token is the index of the entry in the pending-operations list.
293 static final int OP_PENDING = 0;
294 static final int OP_ACKNOWLEDGED = 1;
295 static final int OP_TIMEOUT = -1;
296
297 final SparseIntArray mCurrentOperations = new SparseIntArray();
298 final Object mCurrentOpLock = new Object();
299 final Random mTokenGenerator = new Random();
300
Christopher Tate4a627c72011-04-01 14:43:32 -0700301 final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
302
Christopher Tate5cb400b2009-06-25 16:03:14 -0700303 // Where we keep our journal files and other bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700304 File mBaseStateDir;
305 File mDataDir;
306 File mJournalDir;
307 File mJournal;
Christopher Tate73e02522009-07-15 14:18:26 -0700308
Christopher Tate84725812010-02-04 15:52:40 -0800309 // Keep a log of all the apps we've ever backed up, and what the
310 // dataset tokens are for both the current backup dataset and
311 // the ancestral dataset.
Christopher Tate73e02522009-07-15 14:18:26 -0700312 private File mEverStored;
Christopher Tate73e02522009-07-15 14:18:26 -0700313 HashSet<String> mEverStoredApps = new HashSet<String>();
314
Christopher Tateb49ceb32010-02-08 16:22:24 -0800315 static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes
Christopher Tate84725812010-02-04 15:52:40 -0800316 File mTokenFile;
Christopher Tateb49ceb32010-02-08 16:22:24 -0800317 Set<String> mAncestralPackages = null;
Christopher Tate84725812010-02-04 15:52:40 -0800318 long mAncestralToken = 0;
319 long mCurrentToken = 0;
320
Christopher Tate4cc86e12009-09-21 19:36:51 -0700321 // Persistently track the need to do a full init
322 static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
323 HashSet<String> mPendingInits = new HashSet<String>(); // transport names
Christopher Tateaa088442009-06-16 18:25:46 -0700324
Christopher Tate4a627c72011-04-01 14:43:32 -0700325 // Utility: build a new random integer token
326 int generateToken() {
327 int token;
328 do {
329 synchronized (mTokenGenerator) {
330 token = mTokenGenerator.nextInt();
331 }
332 } while (token < 0);
333 return token;
334 }
335
Christopher Tate44a27902010-01-27 17:15:49 -0800336 // ----- Asynchronous backup/restore handler thread -----
337
338 private class BackupHandler extends Handler {
339 public BackupHandler(Looper looper) {
340 super(looper);
341 }
342
343 public void handleMessage(Message msg) {
344
345 switch (msg.what) {
346 case MSG_RUN_BACKUP:
347 {
348 mLastBackupPass = System.currentTimeMillis();
349 mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
350
351 IBackupTransport transport = getTransport(mCurrentTransport);
352 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800353 Slog.v(TAG, "Backup requested but no transport available");
Christopher Tate44a27902010-01-27 17:15:49 -0800354 mWakelock.release();
355 break;
356 }
357
358 // snapshot the pending-backup set and work on that
359 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
Christopher Tatec61da312010-02-05 10:41:27 -0800360 File oldJournal = mJournal;
Christopher Tate44a27902010-01-27 17:15:49 -0800361 synchronized (mQueueLock) {
Christopher Tatec61da312010-02-05 10:41:27 -0800362 // Do we have any work to do? Construct the work queue
363 // then release the synchronization lock to actually run
364 // the backup.
Christopher Tate44a27902010-01-27 17:15:49 -0800365 if (mPendingBackups.size() > 0) {
366 for (BackupRequest b: mPendingBackups.values()) {
367 queue.add(b);
368 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800369 if (DEBUG) Slog.v(TAG, "clearing pending backups");
Christopher Tate44a27902010-01-27 17:15:49 -0800370 mPendingBackups.clear();
371
372 // Start a new backup-queue journal file too
Christopher Tate44a27902010-01-27 17:15:49 -0800373 mJournal = null;
374
Christopher Tate44a27902010-01-27 17:15:49 -0800375 }
376 }
Christopher Tatec61da312010-02-05 10:41:27 -0800377
378 if (queue.size() > 0) {
379 // At this point, we have started a new journal file, and the old
380 // file identity is being passed to the backup processing thread.
381 // When it completes successfully, that old journal file will be
382 // deleted. If we crash prior to that, the old journal is parsed
383 // at next boot and the journaled requests fulfilled.
384 (new PerformBackupTask(transport, queue, oldJournal)).run();
385 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800386 Slog.v(TAG, "Backup requested but nothing pending");
Christopher Tatec61da312010-02-05 10:41:27 -0800387 mWakelock.release();
388 }
Christopher Tate44a27902010-01-27 17:15:49 -0800389 break;
390 }
391
392 case MSG_RUN_FULL_BACKUP:
Christopher Tate4a627c72011-04-01 14:43:32 -0700393 {
394 FullBackupParams params = (FullBackupParams)msg.obj;
395 (new PerformFullBackupTask(params.fd, params.observer, params.includeApks,
396 params.includeShared, params.allApps, params.packages,
397 params.latch)).run();
Christopher Tate44a27902010-01-27 17:15:49 -0800398 break;
Christopher Tate4a627c72011-04-01 14:43:32 -0700399 }
Christopher Tate44a27902010-01-27 17:15:49 -0800400
401 case MSG_RUN_RESTORE:
402 {
403 RestoreParams params = (RestoreParams)msg.obj;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800404 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
Christopher Tate44a27902010-01-27 17:15:49 -0800405 (new PerformRestoreTask(params.transport, params.observer,
Chris Tate249345b2010-10-29 12:57:04 -0700406 params.token, params.pkgInfo, params.pmToken,
407 params.needFullBackup)).run();
Christopher Tate44a27902010-01-27 17:15:49 -0800408 break;
409 }
410
Christopher Tate75a99702011-05-18 16:28:19 -0700411 case MSG_RUN_FULL_RESTORE:
412 {
413 FullRestoreParams params = (FullRestoreParams)msg.obj;
414 (new PerformFullRestoreTask(params.fd, params.observer, params.latch)).run();
415 break;
416 }
417
Christopher Tate44a27902010-01-27 17:15:49 -0800418 case MSG_RUN_CLEAR:
419 {
420 ClearParams params = (ClearParams)msg.obj;
421 (new PerformClearTask(params.transport, params.packageInfo)).run();
422 break;
423 }
424
425 case MSG_RUN_INITIALIZE:
426 {
427 HashSet<String> queue;
428
429 // Snapshot the pending-init queue and work on that
430 synchronized (mQueueLock) {
431 queue = new HashSet<String>(mPendingInits);
432 mPendingInits.clear();
433 }
434
435 (new PerformInitializeTask(queue)).run();
436 break;
437 }
438
Christopher Tate2d449afe2010-03-29 19:14:24 -0700439 case MSG_RUN_GET_RESTORE_SETS:
440 {
441 // Like other async operations, this is entered with the wakelock held
442 RestoreSet[] sets = null;
443 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
444 try {
445 sets = params.transport.getAvailableRestoreSets();
446 // cache the result in the active session
447 synchronized (params.session) {
448 params.session.mRestoreSets = sets;
449 }
450 if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
451 } catch (Exception e) {
452 Slog.e(TAG, "Error from transport getting set list");
453 } finally {
454 if (params.observer != null) {
455 try {
456 params.observer.restoreSetsAvailable(sets);
457 } catch (RemoteException re) {
458 Slog.e(TAG, "Unable to report listing to observer");
459 } catch (Exception e) {
460 Slog.e(TAG, "Restore observer threw", e);
461 }
462 }
463
Christopher Tate2a935092011-03-03 17:30:32 -0800464 // Done: reset the session timeout clock
465 removeMessages(MSG_RESTORE_TIMEOUT);
466 sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
467
Christopher Tate2d449afe2010-03-29 19:14:24 -0700468 mWakelock.release();
469 }
470 break;
471 }
472
Christopher Tate44a27902010-01-27 17:15:49 -0800473 case MSG_TIMEOUT:
474 {
475 synchronized (mCurrentOpLock) {
476 final int token = msg.arg1;
477 int state = mCurrentOperations.get(token, OP_TIMEOUT);
478 if (state == OP_PENDING) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800479 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + token);
Christopher Tate44a27902010-01-27 17:15:49 -0800480 mCurrentOperations.put(token, OP_TIMEOUT);
481 }
482 mCurrentOpLock.notifyAll();
483 }
484 break;
485 }
Christopher Tate73a3cb32010-12-13 18:27:26 -0800486
487 case MSG_RESTORE_TIMEOUT:
488 {
489 synchronized (BackupManagerService.this) {
490 if (mActiveRestoreSession != null) {
491 // Client app left the restore session dangling. We know that it
492 // can't be in the middle of an actual restore operation because
493 // those are executed serially on this same handler thread. Clean
494 // up now.
495 Slog.w(TAG, "Restore session timed out; aborting");
496 post(mActiveRestoreSession.new EndRestoreRunnable(
497 BackupManagerService.this, mActiveRestoreSession));
498 }
499 }
500 }
Christopher Tate4a627c72011-04-01 14:43:32 -0700501
502 case MSG_FULL_CONFIRMATION_TIMEOUT:
503 {
504 synchronized (mFullConfirmations) {
505 FullParams params = mFullConfirmations.get(msg.arg1);
506 if (params != null) {
507 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
508
509 // Release the waiter; timeout == completion
510 signalFullBackupRestoreCompletion(params);
511
512 // Remove the token from the set
513 mFullConfirmations.delete(msg.arg1);
514
515 // Report a timeout to the observer, if any
516 if (params.observer != null) {
517 try {
518 params.observer.onTimeout();
519 } catch (RemoteException e) {
520 /* don't care if the app has gone away */
521 }
522 }
523 } else {
524 Slog.d(TAG, "couldn't find params for token " + msg.arg1);
525 }
526 }
527 break;
528 }
Christopher Tate44a27902010-01-27 17:15:49 -0800529 }
530 }
531 }
532
533 // ----- Main service implementation -----
534
Christopher Tate487529a2009-04-29 14:03:25 -0700535 public BackupManagerService(Context context) {
536 mContext = context;
537 mPackageManager = context.getPackageManager();
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -0700538 mPackageManagerBinder = AppGlobals.getPackageManager();
Christopher Tate181fafa2009-05-14 11:12:14 -0700539 mActivityManager = ActivityManagerNative.getDefault();
Christopher Tate487529a2009-04-29 14:03:25 -0700540
Christopher Tateb6787f22009-07-02 17:40:45 -0700541 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
542 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
543
Christopher Tate44a27902010-01-27 17:15:49 -0800544 mBackupManagerBinder = asInterface(asBinder());
545
546 // spin up the backup/restore handler thread
547 mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
548 mHandlerThread.start();
549 mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
550
Christopher Tate22b87872009-05-04 16:41:53 -0700551 // Set up our bookkeeping
Christopher Tateb6787f22009-07-02 17:40:45 -0700552 boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
Dianne Hackborncf098292009-07-01 19:55:20 -0700553 Settings.Secure.BACKUP_ENABLED, 0) != 0;
Christopher Tate8031a3d2009-07-06 16:36:05 -0700554 mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
Joe Onoratoab9a2a52009-07-27 08:56:39 -0700555 Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
Christopher Tatecce9da52010-02-03 15:11:15 -0800556 mAutoRestore = Settings.Secure.getInt(context.getContentResolver(),
Christopher Tate5035fda2010-02-25 18:01:14 -0800557 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
Oscar Montemayora8529f62009-11-18 10:14:20 -0800558 // If Encrypted file systems is enabled or disabled, this call will return the
559 // correct directory.
Jason parksa3cdaa52011-01-13 14:15:43 -0600560 mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
Oscar Montemayora8529f62009-11-18 10:14:20 -0800561 mBaseStateDir.mkdirs();
Christopher Tatef4172472009-05-05 15:50:03 -0700562 mDataDir = Environment.getDownloadCacheDirectory();
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700563
Christopher Tate4cc86e12009-09-21 19:36:51 -0700564 // Alarm receivers for scheduled backups & initialization operations
Christopher Tateb6787f22009-07-02 17:40:45 -0700565 mRunBackupReceiver = new RunBackupReceiver();
Christopher Tate4cc86e12009-09-21 19:36:51 -0700566 IntentFilter filter = new IntentFilter();
567 filter.addAction(RUN_BACKUP_ACTION);
568 context.registerReceiver(mRunBackupReceiver, filter,
569 android.Manifest.permission.BACKUP, null);
570
571 mRunInitReceiver = new RunInitializeReceiver();
572 filter = new IntentFilter();
573 filter.addAction(RUN_INITIALIZE_ACTION);
574 context.registerReceiver(mRunInitReceiver, filter,
575 android.Manifest.permission.BACKUP, null);
Christopher Tateb6787f22009-07-02 17:40:45 -0700576
577 Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
Christopher Tateb6787f22009-07-02 17:40:45 -0700578 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
579 mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
580
Christopher Tate4cc86e12009-09-21 19:36:51 -0700581 Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
582 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
583 mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
584
Christopher Tatecde87f42009-06-12 12:55:53 -0700585 // Set up the backup-request journaling
Christopher Tate5cb400b2009-06-25 16:03:14 -0700586 mJournalDir = new File(mBaseStateDir, "pending");
587 mJournalDir.mkdirs(); // creates mBaseStateDir along the way
Dan Egnor852f8e42009-09-30 11:20:45 -0700588 mJournal = null; // will be created on first use
Christopher Tatecde87f42009-06-12 12:55:53 -0700589
Christopher Tate73e02522009-07-15 14:18:26 -0700590 // Set up the various sorts of package tracking we do
591 initPackageTracking();
592
Christopher Tateabce4e82009-06-18 18:35:32 -0700593 // Build our mapping of uid to backup client services. This implicitly
594 // schedules a backup pass on the Package Manager metadata the first
595 // time anything needs to be backed up.
Christopher Tate3799bc22009-05-06 16:13:56 -0700596 synchronized (mBackupParticipants) {
597 addPackageParticipantsLocked(null);
Christopher Tate487529a2009-04-29 14:03:25 -0700598 }
599
Dan Egnor87a02bc2009-06-17 02:30:10 -0700600 // Set up our transport options and initialize the default transport
601 // TODO: Have transports register themselves somehow?
602 // TODO: Don't create transports that we don't need to?
Dan Egnor87a02bc2009-06-17 02:30:10 -0700603 mLocalTransport = new LocalTransport(context); // This is actually pretty cheap
Christopher Tate91717492009-06-26 21:07:13 -0700604 ComponentName localName = new ComponentName(context, LocalTransport.class);
605 registerTransport(localName.flattenToShortString(), mLocalTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700606
Christopher Tate91717492009-06-26 21:07:13 -0700607 mGoogleTransport = null;
Dianne Hackborncf098292009-07-01 19:55:20 -0700608 mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
609 Settings.Secure.BACKUP_TRANSPORT);
610 if ("".equals(mCurrentTransport)) {
611 mCurrentTransport = null;
Christopher Tatece0bf062009-07-01 11:43:53 -0700612 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800613 if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -0700614
615 // Attach to the Google backup transport. When this comes up, it will set
616 // itself as the current transport because we explicitly reset mCurrentTransport
617 // to null.
Christopher Tatea32504f2010-04-21 17:58:07 -0700618 ComponentName transportComponent = new ComponentName("com.google.android.backup",
619 "com.google.android.backup.BackupTransportService");
620 try {
621 // If there's something out there that is supposed to be the Google
622 // backup transport, make sure it's legitimately part of the OS build
623 // and not an app lying about its package name.
624 ApplicationInfo info = mPackageManager.getApplicationInfo(
625 transportComponent.getPackageName(), 0);
626 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
627 if (DEBUG) Slog.v(TAG, "Binding to Google transport");
628 Intent intent = new Intent().setComponent(transportComponent);
629 context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
630 } else {
631 Slog.w(TAG, "Possible Google transport spoof: ignoring " + info);
632 }
633 } catch (PackageManager.NameNotFoundException nnf) {
634 // No such package? No binding.
635 if (DEBUG) Slog.v(TAG, "Google transport not present");
636 }
Christopher Tateaa088442009-06-16 18:25:46 -0700637
Christopher Tatecde87f42009-06-12 12:55:53 -0700638 // Now that we know about valid backup participants, parse any
Christopher Tate49401dd2009-07-01 12:34:29 -0700639 // leftover journal files into the pending backup set
Christopher Tatecde87f42009-06-12 12:55:53 -0700640 parseLeftoverJournals();
641
Christopher Tateb6787f22009-07-02 17:40:45 -0700642 // Power management
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700643 mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
Christopher Tateb6787f22009-07-02 17:40:45 -0700644
645 // Start the backup passes going
646 setBackupEnabled(areEnabled);
647 }
648
649 private class RunBackupReceiver extends BroadcastReceiver {
650 public void onReceive(Context context, Intent intent) {
651 if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
Christopher Tateb6787f22009-07-02 17:40:45 -0700652 synchronized (mQueueLock) {
Christopher Tate4cc86e12009-09-21 19:36:51 -0700653 if (mPendingInits.size() > 0) {
654 // If there are pending init operations, we process those
655 // and then settle into the usual periodic backup schedule.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800656 if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700657 try {
658 mAlarmManager.cancel(mRunInitIntent);
659 mRunInitIntent.send();
660 } catch (PendingIntent.CanceledException ce) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800661 Slog.e(TAG, "Run init intent cancelled");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700662 // can't really do more than bail here
663 }
664 } else {
Christopher Tatec2af5d32010-02-02 15:18:58 -0800665 // Don't run backups now if we're disabled or not yet
666 // fully set up.
667 if (mEnabled && mProvisioned) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800668 if (DEBUG) Slog.v(TAG, "Running a backup pass");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700669
670 // Acquire the wakelock and pass it to the backup thread. it will
671 // be released once backup concludes.
672 mWakelock.acquire();
673
674 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
675 mBackupHandler.sendMessage(msg);
676 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800677 Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
Christopher Tate4cc86e12009-09-21 19:36:51 -0700678 }
679 }
680 }
681 }
682 }
683 }
684
685 private class RunInitializeReceiver extends BroadcastReceiver {
686 public void onReceive(Context context, Intent intent) {
687 if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
688 synchronized (mQueueLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800689 if (DEBUG) Slog.v(TAG, "Running a device init");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700690
691 // Acquire the wakelock and pass it to the init thread. it will
692 // be released once init concludes.
Christopher Tateb6787f22009-07-02 17:40:45 -0700693 mWakelock.acquire();
694
Christopher Tate4cc86e12009-09-21 19:36:51 -0700695 Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
Christopher Tateb6787f22009-07-02 17:40:45 -0700696 mBackupHandler.sendMessage(msg);
697 }
698 }
Christopher Tate49401dd2009-07-01 12:34:29 -0700699 }
Christopher Tateb6787f22009-07-02 17:40:45 -0700700 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700701
Christopher Tate73e02522009-07-15 14:18:26 -0700702 private void initPackageTracking() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800703 if (DEBUG) Slog.v(TAG, "Initializing package tracking");
Christopher Tate73e02522009-07-15 14:18:26 -0700704
Christopher Tate84725812010-02-04 15:52:40 -0800705 // Remember our ancestral dataset
706 mTokenFile = new File(mBaseStateDir, "ancestral");
707 try {
708 RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
Christopher Tateb49ceb32010-02-08 16:22:24 -0800709 int version = tf.readInt();
710 if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
711 mAncestralToken = tf.readLong();
712 mCurrentToken = tf.readLong();
713
714 int numPackages = tf.readInt();
715 if (numPackages >= 0) {
716 mAncestralPackages = new HashSet<String>();
717 for (int i = 0; i < numPackages; i++) {
718 String pkgName = tf.readUTF();
719 mAncestralPackages.add(pkgName);
720 }
721 }
722 }
Brad Fitzpatrick725d8f02010-11-15 11:12:42 -0800723 tf.close();
Christopher Tate1168baa2010-02-17 13:03:40 -0800724 } catch (FileNotFoundException fnf) {
725 // Probably innocuous
Joe Onorato8a9b2202010-02-26 18:56:32 -0800726 Slog.v(TAG, "No ancestral data");
Christopher Tate84725812010-02-04 15:52:40 -0800727 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800728 Slog.w(TAG, "Unable to read token file", e);
Christopher Tate84725812010-02-04 15:52:40 -0800729 }
730
Christopher Tatee97e8072009-07-15 16:45:50 -0700731 // Keep a log of what apps we've ever backed up. Because we might have
732 // rebooted in the middle of an operation that was removing something from
733 // this log, we sanity-check its contents here and reconstruct it.
Christopher Tate73e02522009-07-15 14:18:26 -0700734 mEverStored = new File(mBaseStateDir, "processed");
Christopher Tatee97e8072009-07-15 16:45:50 -0700735 File tempProcessedFile = new File(mBaseStateDir, "processed.new");
Christopher Tate73e02522009-07-15 14:18:26 -0700736
Christopher Tatee97e8072009-07-15 16:45:50 -0700737 // If we were in the middle of removing something from the ever-backed-up
738 // file, there might be a transient "processed.new" file still present.
Dan Egnor852f8e42009-09-30 11:20:45 -0700739 // Ignore it -- we'll validate "processed" against the current package set.
Christopher Tatee97e8072009-07-15 16:45:50 -0700740 if (tempProcessedFile.exists()) {
741 tempProcessedFile.delete();
742 }
743
Dan Egnor852f8e42009-09-30 11:20:45 -0700744 // If there are previous contents, parse them out then start a new
745 // file to continue the recordkeeping.
746 if (mEverStored.exists()) {
747 RandomAccessFile temp = null;
748 RandomAccessFile in = null;
749
750 try {
751 temp = new RandomAccessFile(tempProcessedFile, "rws");
752 in = new RandomAccessFile(mEverStored, "r");
753
754 while (true) {
755 PackageInfo info;
756 String pkg = in.readUTF();
757 try {
758 info = mPackageManager.getPackageInfo(pkg, 0);
759 mEverStoredApps.add(pkg);
760 temp.writeUTF(pkg);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800761 if (DEBUG) Slog.v(TAG, " + " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700762 } catch (NameNotFoundException e) {
763 // nope, this package was uninstalled; don't include it
Joe Onorato8a9b2202010-02-26 18:56:32 -0800764 if (DEBUG) Slog.v(TAG, " - " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700765 }
766 }
767 } catch (EOFException e) {
768 // Once we've rewritten the backup history log, atomically replace the
769 // old one with the new one then reopen the file for continuing use.
770 if (!tempProcessedFile.renameTo(mEverStored)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800771 Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -0700772 }
773 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800774 Slog.e(TAG, "Error in processed file", e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700775 } finally {
776 try { if (temp != null) temp.close(); } catch (IOException e) {}
777 try { if (in != null) in.close(); } catch (IOException e) {}
778 }
779 }
780
Christopher Tate73e02522009-07-15 14:18:26 -0700781 // Register for broadcasts about package install, etc., so we can
782 // update the provider list.
783 IntentFilter filter = new IntentFilter();
784 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
785 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
786 filter.addDataScheme("package");
787 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800788 // Register for events related to sdcard installation.
789 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800790 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
791 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800792 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
Christopher Tate73e02522009-07-15 14:18:26 -0700793 }
794
Christopher Tatecde87f42009-06-12 12:55:53 -0700795 private void parseLeftoverJournals() {
Dan Egnor852f8e42009-09-30 11:20:45 -0700796 for (File f : mJournalDir.listFiles()) {
797 if (mJournal == null || f.compareTo(mJournal) != 0) {
798 // This isn't the current journal, so it must be a leftover. Read
799 // out the package names mentioned there and schedule them for
800 // backup.
801 RandomAccessFile in = null;
802 try {
Joe Onorato431bb222010-10-18 19:13:23 -0400803 Slog.i(TAG, "Found stale backup journal, scheduling");
Dan Egnor852f8e42009-09-30 11:20:45 -0700804 in = new RandomAccessFile(f, "r");
805 while (true) {
806 String packageName = in.readUTF();
Joe Onorato431bb222010-10-18 19:13:23 -0400807 Slog.i(TAG, " " + packageName);
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -0700808 dataChangedImpl(packageName);
Christopher Tatecde87f42009-06-12 12:55:53 -0700809 }
Dan Egnor852f8e42009-09-30 11:20:45 -0700810 } catch (EOFException e) {
811 // no more data; we're done
812 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800813 Slog.e(TAG, "Can't read " + f, e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700814 } finally {
815 // close/delete the file
816 try { if (in != null) in.close(); } catch (IOException e) {}
817 f.delete();
Christopher Tatecde87f42009-06-12 12:55:53 -0700818 }
819 }
820 }
821 }
822
Christopher Tate4cc86e12009-09-21 19:36:51 -0700823 // Maintain persistent state around whether need to do an initialize operation.
824 // Must be called with the queue lock held.
825 void recordInitPendingLocked(boolean isPending, String transportName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800826 if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
Christopher Tate4cc86e12009-09-21 19:36:51 -0700827 + " on transport " + transportName);
828 try {
829 IBackupTransport transport = getTransport(transportName);
830 String transportDirName = transport.transportDirName();
831 File stateDir = new File(mBaseStateDir, transportDirName);
832 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
833
834 if (isPending) {
835 // We need an init before we can proceed with sending backup data.
836 // Record that with an entry in our set of pending inits, as well as
837 // journaling it via creation of a sentinel file.
838 mPendingInits.add(transportName);
839 try {
840 (new FileOutputStream(initPendingFile)).close();
841 } catch (IOException ioe) {
842 // Something is badly wrong with our permissions; just try to move on
843 }
844 } else {
845 // No more initialization needed; wipe the journal and reset our state.
846 initPendingFile.delete();
847 mPendingInits.remove(transportName);
848 }
849 } catch (RemoteException e) {
850 // can't happen; the transport is local
851 }
852 }
853
Christopher Tated55e18a2009-09-21 10:12:59 -0700854 // Reset all of our bookkeeping, in response to having been told that
855 // the backend data has been wiped [due to idle expiry, for example],
856 // so we must re-upload all saved settings.
857 void resetBackupState(File stateFileDir) {
858 synchronized (mQueueLock) {
859 // Wipe the "what we've ever backed up" tracking
Christopher Tated55e18a2009-09-21 10:12:59 -0700860 mEverStoredApps.clear();
Dan Egnor852f8e42009-09-30 11:20:45 -0700861 mEverStored.delete();
Christopher Tated55e18a2009-09-21 10:12:59 -0700862
Christopher Tate84725812010-02-04 15:52:40 -0800863 mCurrentToken = 0;
864 writeRestoreTokens();
865
Christopher Tated55e18a2009-09-21 10:12:59 -0700866 // Remove all the state files
867 for (File sf : stateFileDir.listFiles()) {
Christopher Tate4cc86e12009-09-21 19:36:51 -0700868 // ... but don't touch the needs-init sentinel
869 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
870 sf.delete();
871 }
Christopher Tated55e18a2009-09-21 10:12:59 -0700872 }
Christopher Tate45597642011-04-04 16:59:21 -0700873 }
Christopher Tated55e18a2009-09-21 10:12:59 -0700874
Christopher Tate45597642011-04-04 16:59:21 -0700875 // Enqueue a new backup of every participant
876 int N = mBackupParticipants.size();
877 for (int i=0; i<N; i++) {
878 int uid = mBackupParticipants.keyAt(i);
879 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
880 for (ApplicationInfo app: participants) {
881 dataChangedImpl(app.packageName);
Christopher Tated55e18a2009-09-21 10:12:59 -0700882 }
883 }
884 }
885
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800886 // Add a transport to our set of available backends. If 'transport' is null, this
887 // is an unregistration, and the transport's entry is removed from our bookkeeping.
Christopher Tate91717492009-06-26 21:07:13 -0700888 private void registerTransport(String name, IBackupTransport transport) {
889 synchronized (mTransports) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800890 if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport);
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800891 if (transport != null) {
892 mTransports.put(name, transport);
893 } else {
894 mTransports.remove(name);
Christopher Tateb0dcaaf2010-01-29 16:27:04 -0800895 if ((mCurrentTransport != null) && mCurrentTransport.equals(name)) {
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800896 mCurrentTransport = null;
897 }
898 // Nothing further to do in the unregistration case
899 return;
900 }
Christopher Tate91717492009-06-26 21:07:13 -0700901 }
Christopher Tate4cc86e12009-09-21 19:36:51 -0700902
903 // If the init sentinel file exists, we need to be sure to perform the init
904 // as soon as practical. We also create the state directory at registration
905 // time to ensure it's present from the outset.
906 try {
907 String transportName = transport.transportDirName();
908 File stateDir = new File(mBaseStateDir, transportName);
909 stateDir.mkdirs();
910
911 File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
912 if (initSentinel.exists()) {
913 synchronized (mQueueLock) {
914 mPendingInits.add(transportName);
915
916 // TODO: pick a better starting time than now + 1 minute
917 long delay = 1000 * 60; // one minute, in milliseconds
918 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
919 System.currentTimeMillis() + delay, mRunInitIntent);
920 }
921 }
922 } catch (RemoteException e) {
923 // can't happen, the transport is local
924 }
Christopher Tate91717492009-06-26 21:07:13 -0700925 }
926
Christopher Tate3799bc22009-05-06 16:13:56 -0700927 // ----- Track installation/removal of packages -----
928 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
929 public void onReceive(Context context, Intent intent) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800930 if (DEBUG) Slog.d(TAG, "Received broadcast " + intent);
Christopher Tate3799bc22009-05-06 16:13:56 -0700931
Christopher Tate3799bc22009-05-06 16:13:56 -0700932 String action = intent.getAction();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800933 boolean replacing = false;
934 boolean added = false;
935 Bundle extras = intent.getExtras();
936 String pkgList[] = null;
937 if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
938 Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
939 Uri uri = intent.getData();
940 if (uri == null) {
941 return;
942 }
943 String pkgName = uri.getSchemeSpecificPart();
944 if (pkgName != null) {
945 pkgList = new String[] { pkgName };
946 }
947 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
948 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800949 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800950 added = true;
951 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800952 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800953 added = false;
954 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
955 }
956 if (pkgList == null || pkgList.length == 0) {
957 return;
958 }
959 if (added) {
Christopher Tate3799bc22009-05-06 16:13:56 -0700960 synchronized (mBackupParticipants) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800961 for (String pkgName : pkgList) {
962 if (replacing) {
963 // The package was just upgraded
964 updatePackageParticipantsLocked(pkgName);
965 } else {
966 // The package was just added
967 addPackageParticipantsLocked(pkgName);
968 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700969 }
970 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800971 } else {
972 if (replacing) {
Christopher Tate3799bc22009-05-06 16:13:56 -0700973 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
974 } else {
975 synchronized (mBackupParticipants) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800976 for (String pkgName : pkgList) {
977 removePackageParticipantsLocked(pkgName);
978 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700979 }
980 }
981 }
982 }
983 };
984
Dan Egnor87a02bc2009-06-17 02:30:10 -0700985 // ----- Track connection to GoogleBackupTransport service -----
986 ServiceConnection mGoogleConnection = new ServiceConnection() {
987 public void onServiceConnected(ComponentName name, IBinder service) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800988 if (DEBUG) Slog.v(TAG, "Connected to Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -0700989 mGoogleTransport = IBackupTransport.Stub.asInterface(service);
Christopher Tate91717492009-06-26 21:07:13 -0700990 registerTransport(name.flattenToShortString(), mGoogleTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700991 }
992
993 public void onServiceDisconnected(ComponentName name) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800994 if (DEBUG) Slog.v(TAG, "Disconnected from Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -0700995 mGoogleTransport = null;
Christopher Tate91717492009-06-26 21:07:13 -0700996 registerTransport(name.flattenToShortString(), null);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700997 }
998 };
999
Christopher Tate181fafa2009-05-14 11:12:14 -07001000 // Add the backup agents in the given package to our set of known backup participants.
1001 // If 'packageName' is null, adds all backup agents in the whole system.
Christopher Tate3799bc22009-05-06 16:13:56 -07001002 void addPackageParticipantsLocked(String packageName) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001003 // Look for apps that define the android:backupAgent attribute
Joe Onorato8a9b2202010-02-26 18:56:32 -08001004 if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: " + packageName);
Dan Egnorefe52642009-06-24 00:16:33 -07001005 List<PackageInfo> targetApps = allAgentPackages();
Christopher Tate181fafa2009-05-14 11:12:14 -07001006 addPackageParticipantsLockedInner(packageName, targetApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001007 }
1008
Christopher Tate181fafa2009-05-14 11:12:14 -07001009 private void addPackageParticipantsLockedInner(String packageName,
Dan Egnorefe52642009-06-24 00:16:33 -07001010 List<PackageInfo> targetPkgs) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001011 if (DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001012 Slog.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
Dan Egnorefe52642009-06-24 00:16:33 -07001013 for (PackageInfo p : targetPkgs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001014 Slog.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName
Christopher Tate5e1ab332009-09-01 20:32:49 -07001015 + " uid=" + p.applicationInfo.uid
1016 + " killAfterRestore="
1017 + (((p.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) ? "true" : "false")
Christopher Tate5e1ab332009-09-01 20:32:49 -07001018 );
Christopher Tate181fafa2009-05-14 11:12:14 -07001019 }
1020 }
1021
Dan Egnorefe52642009-06-24 00:16:33 -07001022 for (PackageInfo pkg : targetPkgs) {
1023 if (packageName == null || pkg.packageName.equals(packageName)) {
1024 int uid = pkg.applicationInfo.uid;
Christopher Tate181fafa2009-05-14 11:12:14 -07001025 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001026 if (set == null) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001027 set = new HashSet<ApplicationInfo>();
Christopher Tate3799bc22009-05-06 16:13:56 -07001028 mBackupParticipants.put(uid, set);
1029 }
Dan Egnorefe52642009-06-24 00:16:33 -07001030 set.add(pkg.applicationInfo);
Christopher Tate73e02522009-07-15 14:18:26 -07001031
1032 // If we've never seen this app before, schedule a backup for it
1033 if (!mEverStoredApps.contains(pkg.packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001034 if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName
Christopher Tate73e02522009-07-15 14:18:26 -07001035 + " never backed up; scheduling");
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07001036 dataChangedImpl(pkg.packageName);
Christopher Tate73e02522009-07-15 14:18:26 -07001037 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001038 }
Christopher Tate487529a2009-04-29 14:03:25 -07001039 }
1040 }
1041
Christopher Tate6785dd82009-06-18 15:58:25 -07001042 // Remove the given package's entry from our known active set. If
1043 // 'packageName' is null, *all* participating apps will be removed.
Christopher Tate3799bc22009-05-06 16:13:56 -07001044 void removePackageParticipantsLocked(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001045 if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: " + packageName);
Christopher Tatec28083a2010-12-14 16:16:44 -08001046 List<String> allApps = new ArrayList<String>();
Christopher Tate181fafa2009-05-14 11:12:14 -07001047 if (packageName != null) {
Christopher Tatec28083a2010-12-14 16:16:44 -08001048 allApps.add(packageName);
Christopher Tate181fafa2009-05-14 11:12:14 -07001049 } else {
1050 // all apps with agents
Christopher Tatec28083a2010-12-14 16:16:44 -08001051 List<PackageInfo> knownPackages = allAgentPackages();
1052 for (PackageInfo pkg : knownPackages) {
1053 allApps.add(pkg.packageName);
1054 }
Christopher Tate181fafa2009-05-14 11:12:14 -07001055 }
1056 removePackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001057 }
1058
Joe Onorato8ad02812009-05-13 01:41:44 -04001059 private void removePackageParticipantsLockedInner(String packageName,
Christopher Tatec28083a2010-12-14 16:16:44 -08001060 List<String> allPackageNames) {
Christopher Tate043dadc2009-06-02 16:11:00 -07001061 if (DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001062 Slog.v(TAG, "removePackageParticipantsLockedInner (" + packageName
Christopher Tatec28083a2010-12-14 16:16:44 -08001063 + ") removing " + allPackageNames.size() + " entries");
1064 for (String p : allPackageNames) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001065 Slog.v(TAG, " - " + p);
Christopher Tate043dadc2009-06-02 16:11:00 -07001066 }
1067 }
Christopher Tatec28083a2010-12-14 16:16:44 -08001068 for (String pkg : allPackageNames) {
1069 if (packageName == null || pkg.equals(packageName)) {
1070 int uid = -1;
1071 try {
1072 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1073 uid = info.applicationInfo.uid;
1074 } catch (NameNotFoundException e) {
1075 // we don't know this package name, so just skip it for now
1076 continue;
1077 }
1078
Christopher Tate181fafa2009-05-14 11:12:14 -07001079 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001080 if (set != null) {
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001081 // Find the existing entry with the same package name, and remove it.
1082 // We can't just remove(app) because the instances are different.
1083 for (ApplicationInfo entry: set) {
Christopher Tatec28083a2010-12-14 16:16:44 -08001084 if (entry.packageName.equals(pkg)) {
1085 if (DEBUG) Slog.v(TAG, " removing participant " + pkg);
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001086 set.remove(entry);
Christopher Tatec28083a2010-12-14 16:16:44 -08001087 removeEverBackedUp(pkg);
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001088 break;
1089 }
1090 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001091 if (set.size() == 0) {
Dan Egnorefe52642009-06-24 00:16:33 -07001092 mBackupParticipants.delete(uid);
1093 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001094 }
1095 }
1096 }
1097 }
1098
Christopher Tate181fafa2009-05-14 11:12:14 -07001099 // Returns the set of all applications that define an android:backupAgent attribute
Christopher Tate73e02522009-07-15 14:18:26 -07001100 List<PackageInfo> allAgentPackages() {
Christopher Tate6785dd82009-06-18 15:58:25 -07001101 // !!! TODO: cache this and regenerate only when necessary
Dan Egnorefe52642009-06-24 00:16:33 -07001102 int flags = PackageManager.GET_SIGNATURES;
1103 List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
1104 int N = packages.size();
1105 for (int a = N-1; a >= 0; a--) {
Christopher Tate0749dcd2009-08-13 15:13:03 -07001106 PackageInfo pkg = packages.get(a);
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001107 try {
1108 ApplicationInfo app = pkg.applicationInfo;
1109 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
Christopher Tatea87240c2010-02-12 14:12:34 -08001110 || app.backupAgentName == null) {
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001111 packages.remove(a);
1112 }
1113 else {
1114 // we will need the shared library path, so look that up and store it here
1115 app = mPackageManager.getApplicationInfo(pkg.packageName,
1116 PackageManager.GET_SHARED_LIBRARY_FILES);
1117 pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
1118 }
1119 } catch (NameNotFoundException e) {
Dan Egnorefe52642009-06-24 00:16:33 -07001120 packages.remove(a);
Christopher Tate181fafa2009-05-14 11:12:14 -07001121 }
1122 }
Dan Egnorefe52642009-06-24 00:16:33 -07001123 return packages;
Christopher Tate181fafa2009-05-14 11:12:14 -07001124 }
Christopher Tateaa088442009-06-16 18:25:46 -07001125
Christopher Tate3799bc22009-05-06 16:13:56 -07001126 // Reset the given package's known backup participants. Unlike add/remove, the update
1127 // action cannot be passed a null package name.
1128 void updatePackageParticipantsLocked(String packageName) {
1129 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001130 Slog.e(TAG, "updatePackageParticipants called with null package name");
Christopher Tate3799bc22009-05-06 16:13:56 -07001131 return;
1132 }
Joe Onorato8a9b2202010-02-26 18:56:32 -08001133 if (DEBUG) Slog.v(TAG, "updatePackageParticipantsLocked: " + packageName);
Christopher Tate3799bc22009-05-06 16:13:56 -07001134
1135 // brute force but small code size
Dan Egnorefe52642009-06-24 00:16:33 -07001136 List<PackageInfo> allApps = allAgentPackages();
Christopher Tatec28083a2010-12-14 16:16:44 -08001137 List<String> allAppNames = new ArrayList<String>();
1138 for (PackageInfo pkg : allApps) {
1139 allAppNames.add(pkg.packageName);
1140 }
1141 removePackageParticipantsLockedInner(packageName, allAppNames);
Christopher Tate181fafa2009-05-14 11:12:14 -07001142 addPackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001143 }
1144
Christopher Tate84725812010-02-04 15:52:40 -08001145 // Called from the backup task: record that the given app has been successfully
Christopher Tate73e02522009-07-15 14:18:26 -07001146 // backed up at least once
1147 void logBackupComplete(String packageName) {
Dan Egnor852f8e42009-09-30 11:20:45 -07001148 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
1149
1150 synchronized (mEverStoredApps) {
1151 if (!mEverStoredApps.add(packageName)) return;
1152
1153 RandomAccessFile out = null;
1154 try {
1155 out = new RandomAccessFile(mEverStored, "rws");
1156 out.seek(out.length());
1157 out.writeUTF(packageName);
1158 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001159 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -07001160 } finally {
1161 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tate73e02522009-07-15 14:18:26 -07001162 }
1163 }
1164 }
1165
Christopher Tatee97e8072009-07-15 16:45:50 -07001166 // Remove our awareness of having ever backed up the given package
1167 void removeEverBackedUp(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001168 if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName + ", new set:");
Christopher Tatee97e8072009-07-15 16:45:50 -07001169
Dan Egnor852f8e42009-09-30 11:20:45 -07001170 synchronized (mEverStoredApps) {
1171 // Rewrite the file and rename to overwrite. If we reboot in the middle,
1172 // we'll recognize on initialization time that the package no longer
1173 // exists and fix it up then.
1174 File tempKnownFile = new File(mBaseStateDir, "processed.new");
1175 RandomAccessFile known = null;
1176 try {
1177 known = new RandomAccessFile(tempKnownFile, "rws");
1178 mEverStoredApps.remove(packageName);
1179 for (String s : mEverStoredApps) {
1180 known.writeUTF(s);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001181 if (DEBUG) Slog.v(TAG, " " + s);
Christopher Tatee97e8072009-07-15 16:45:50 -07001182 }
Dan Egnor852f8e42009-09-30 11:20:45 -07001183 known.close();
1184 known = null;
1185 if (!tempKnownFile.renameTo(mEverStored)) {
1186 throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
1187 }
1188 } catch (IOException e) {
1189 // Bad: we couldn't create the new copy. For safety's sake we
1190 // abandon the whole process and remove all what's-backed-up
1191 // state entirely, meaning we'll force a backup pass for every
1192 // participant on the next boot or [re]install.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001193 Slog.w(TAG, "Error rewriting " + mEverStored, e);
Dan Egnor852f8e42009-09-30 11:20:45 -07001194 mEverStoredApps.clear();
1195 tempKnownFile.delete();
1196 mEverStored.delete();
1197 } finally {
1198 try { if (known != null) known.close(); } catch (IOException e) {}
Christopher Tatee97e8072009-07-15 16:45:50 -07001199 }
1200 }
1201 }
1202
Christopher Tateb49ceb32010-02-08 16:22:24 -08001203 // Persistently record the current and ancestral backup tokens as well
1204 // as the set of packages with data [supposedly] available in the
1205 // ancestral dataset.
Christopher Tate84725812010-02-04 15:52:40 -08001206 void writeRestoreTokens() {
1207 try {
1208 RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
Christopher Tateb49ceb32010-02-08 16:22:24 -08001209
1210 // First, the version number of this record, for futureproofing
1211 af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
1212
1213 // Write the ancestral and current tokens
Christopher Tate84725812010-02-04 15:52:40 -08001214 af.writeLong(mAncestralToken);
1215 af.writeLong(mCurrentToken);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001216
1217 // Now write the set of ancestral packages
1218 if (mAncestralPackages == null) {
1219 af.writeInt(-1);
1220 } else {
1221 af.writeInt(mAncestralPackages.size());
Joe Onorato8a9b2202010-02-26 18:56:32 -08001222 if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
Christopher Tateb49ceb32010-02-08 16:22:24 -08001223 for (String pkgName : mAncestralPackages) {
1224 af.writeUTF(pkgName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001225 if (DEBUG) Slog.v(TAG, " " + pkgName);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001226 }
1227 }
Christopher Tate84725812010-02-04 15:52:40 -08001228 af.close();
1229 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001230 Slog.w(TAG, "Unable to write token file:", e);
Christopher Tate84725812010-02-04 15:52:40 -08001231 }
1232 }
1233
Dan Egnor87a02bc2009-06-17 02:30:10 -07001234 // Return the given transport
Christopher Tate91717492009-06-26 21:07:13 -07001235 private IBackupTransport getTransport(String transportName) {
1236 synchronized (mTransports) {
1237 IBackupTransport transport = mTransports.get(transportName);
1238 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001239 Slog.w(TAG, "Requested unavailable transport: " + transportName);
Christopher Tate91717492009-06-26 21:07:13 -07001240 }
1241 return transport;
Christopher Tate8c850b72009-06-07 19:33:20 -07001242 }
Christopher Tate8c850b72009-06-07 19:33:20 -07001243 }
1244
Christopher Tatedf01dea2009-06-09 20:45:02 -07001245 // fire off a backup agent, blocking until it attaches or times out
1246 IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
1247 IBackupAgent agent = null;
1248 synchronized(mAgentConnectLock) {
1249 mConnecting = true;
1250 mConnectedAgent = null;
1251 try {
1252 if (mActivityManager.bindBackupAgent(app, mode)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001253 Slog.d(TAG, "awaiting agent for " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001254
1255 // success; wait for the agent to arrive
Christopher Tate75a99702011-05-18 16:28:19 -07001256 // only wait 10 seconds for the bind to happen
Christopher Tatec7b31e32009-06-10 15:49:30 -07001257 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1258 while (mConnecting && mConnectedAgent == null
1259 && (System.currentTimeMillis() < timeoutMark)) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07001260 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001261 mAgentConnectLock.wait(5000);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001262 } catch (InterruptedException e) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001263 // just bail
Christopher Tatedf01dea2009-06-09 20:45:02 -07001264 return null;
1265 }
1266 }
1267
1268 // if we timed out with no connect, abort and move on
1269 if (mConnecting == true) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001270 Slog.w(TAG, "Timeout waiting for agent " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001271 return null;
1272 }
1273 agent = mConnectedAgent;
1274 }
1275 } catch (RemoteException e) {
1276 // can't happen
1277 }
1278 }
1279 return agent;
1280 }
1281
Christopher Tatec7b31e32009-06-10 15:49:30 -07001282 // clear an application's data, blocking until the operation completes or times out
1283 void clearApplicationDataSynchronous(String packageName) {
Christopher Tatef7c886b2009-06-26 15:34:09 -07001284 // Don't wipe packages marked allowClearUserData=false
1285 try {
1286 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1287 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001288 if (DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
Christopher Tatef7c886b2009-06-26 15:34:09 -07001289 + packageName);
1290 return;
1291 }
1292 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001293 Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
Christopher Tatef7c886b2009-06-26 15:34:09 -07001294 return;
1295 }
1296
Christopher Tatec7b31e32009-06-10 15:49:30 -07001297 ClearDataObserver observer = new ClearDataObserver();
1298
1299 synchronized(mClearDataLock) {
1300 mClearingData = true;
Christopher Tate9dfdac52009-08-06 14:57:53 -07001301 try {
1302 mActivityManager.clearApplicationUserData(packageName, observer);
1303 } catch (RemoteException e) {
1304 // can't happen because the activity manager is in this process
1305 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001306
1307 // only wait 10 seconds for the clear data to happen
1308 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1309 while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
1310 try {
1311 mClearDataLock.wait(5000);
1312 } catch (InterruptedException e) {
1313 // won't happen, but still.
1314 mClearingData = false;
1315 }
1316 }
1317 }
1318 }
1319
1320 class ClearDataObserver extends IPackageDataObserver.Stub {
Dan Egnor852f8e42009-09-30 11:20:45 -07001321 public void onRemoveCompleted(String packageName, boolean succeeded) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001322 synchronized(mClearDataLock) {
1323 mClearingData = false;
Christopher Tatef68eb502009-06-16 11:02:01 -07001324 mClearDataLock.notifyAll();
Christopher Tatec7b31e32009-06-10 15:49:30 -07001325 }
1326 }
1327 }
1328
Christopher Tate1bb69062010-02-19 17:02:12 -08001329 // Get the restore-set token for the best-available restore set for this package:
1330 // the active set if possible, else the ancestral one. Returns zero if none available.
1331 long getAvailableRestoreToken(String packageName) {
1332 long token = mAncestralToken;
1333 synchronized (mQueueLock) {
1334 if (mEverStoredApps.contains(packageName)) {
1335 token = mCurrentToken;
1336 }
1337 }
1338 return token;
1339 }
1340
Christopher Tate44a27902010-01-27 17:15:49 -08001341 // -----
1342 // Utility methods used by the asynchronous-with-timeout backup/restore operations
1343 boolean waitUntilOperationComplete(int token) {
1344 int finalState = OP_PENDING;
1345 synchronized (mCurrentOpLock) {
1346 try {
1347 while ((finalState = mCurrentOperations.get(token, OP_TIMEOUT)) == OP_PENDING) {
1348 try {
1349 mCurrentOpLock.wait();
1350 } catch (InterruptedException e) {}
1351 }
1352 } catch (IndexOutOfBoundsException e) {
1353 // the operation has been mysteriously cleared from our
1354 // bookkeeping -- consider this a success and ignore it.
1355 }
1356 }
1357 mBackupHandler.removeMessages(MSG_TIMEOUT);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001358 if (DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
Christopher Tate1bb69062010-02-19 17:02:12 -08001359 + " complete: finalState=" + finalState);
Christopher Tate44a27902010-01-27 17:15:49 -08001360 return finalState == OP_ACKNOWLEDGED;
1361 }
1362
1363 void prepareOperationTimeout(int token, long interval) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001364 if (DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
Christopher Tate1bb69062010-02-19 17:02:12 -08001365 + " interval=" + interval);
Christopher Tate4a627c72011-04-01 14:43:32 -07001366 synchronized (mCurrentOpLock) {
1367 mCurrentOperations.put(token, OP_PENDING);
1368 Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
1369 mBackupHandler.sendMessageDelayed(msg, interval);
1370 }
Christopher Tate44a27902010-01-27 17:15:49 -08001371 }
1372
Christopher Tate043dadc2009-06-02 16:11:00 -07001373 // ----- Back up a set of applications via a worker thread -----
1374
Christopher Tate44a27902010-01-27 17:15:49 -08001375 class PerformBackupTask implements Runnable {
Christopher Tate043dadc2009-06-02 16:11:00 -07001376 private static final String TAG = "PerformBackupThread";
Christopher Tateaa088442009-06-16 18:25:46 -07001377 IBackupTransport mTransport;
Christopher Tate043dadc2009-06-02 16:11:00 -07001378 ArrayList<BackupRequest> mQueue;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001379 File mStateDir;
Christopher Tatecde87f42009-06-12 12:55:53 -07001380 File mJournal;
Christopher Tate043dadc2009-06-02 16:11:00 -07001381
Christopher Tate44a27902010-01-27 17:15:49 -08001382 public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue,
Christopher Tatecde87f42009-06-12 12:55:53 -07001383 File journal) {
Christopher Tateaa088442009-06-16 18:25:46 -07001384 mTransport = transport;
Christopher Tate043dadc2009-06-02 16:11:00 -07001385 mQueue = queue;
Christopher Tatecde87f42009-06-12 12:55:53 -07001386 mJournal = journal;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001387
1388 try {
1389 mStateDir = new File(mBaseStateDir, transport.transportDirName());
1390 } catch (RemoteException e) {
1391 // can't happen; the transport is local
1392 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001393 }
1394
Christopher Tate043dadc2009-06-02 16:11:00 -07001395 public void run() {
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001396 int status = BackupConstants.TRANSPORT_OK;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001397 long startRealtime = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001398 if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
Christopher Tate043dadc2009-06-02 16:11:00 -07001399
Christopher Tate79588342009-06-30 16:11:49 -07001400 // Backups run at background priority
1401 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1402
Christopher Tate043dadc2009-06-02 16:11:00 -07001403 try {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001404 EventLog.writeEvent(EventLogTags.BACKUP_START, mTransport.transportDirName());
Dan Egnor01445162009-09-21 17:04:05 -07001405
Dan Egnor852f8e42009-09-30 11:20:45 -07001406 // If we haven't stored package manager metadata yet, we must init the transport.
1407 File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
1408 if (status == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001409 Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
Dan Egnor852f8e42009-09-30 11:20:45 -07001410 resetBackupState(mStateDir); // Just to make sure.
Dan Egnor01445162009-09-21 17:04:05 -07001411 status = mTransport.initializeDevice();
Dan Egnor726247c2009-09-29 19:12:31 -07001412 if (status == BackupConstants.TRANSPORT_OK) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001413 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07001414 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001415 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Joe Onorato8a9b2202010-02-26 18:56:32 -08001416 Slog.e(TAG, "Transport error in initializeDevice()");
Dan Egnor726247c2009-09-29 19:12:31 -07001417 }
Dan Egnor01445162009-09-21 17:04:05 -07001418 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001419
1420 // The package manager doesn't have a proper <application> etc, but since
1421 // it's running here in the system process we can just set up its agent
1422 // directly and use a synthetic BackupRequest. We always run this pass
1423 // because it's cheap and this way we guarantee that we don't get out of
1424 // step even if we're selecting among various transports at run time.
Dan Egnor01445162009-09-21 17:04:05 -07001425 if (status == BackupConstants.TRANSPORT_OK) {
1426 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
1427 mPackageManager, allAgentPackages());
Christopher Tate4a627c72011-04-01 14:43:32 -07001428 BackupRequest pmRequest = new BackupRequest(new ApplicationInfo());
Dan Egnor01445162009-09-21 17:04:05 -07001429 pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
1430 status = processOneBackup(pmRequest,
1431 IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
1432 }
Christopher Tate90967f42009-09-20 15:28:33 -07001433
Dan Egnor01445162009-09-21 17:04:05 -07001434 if (status == BackupConstants.TRANSPORT_OK) {
1435 // Now run all the backups in our queue
1436 status = doQueuedBackups(mTransport);
1437 }
1438
1439 if (status == BackupConstants.TRANSPORT_OK) {
1440 // Tell the transport to finish everything it has buffered
1441 status = mTransport.finishBackup();
1442 if (status == BackupConstants.TRANSPORT_OK) {
1443 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001444 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, mQueue.size(), millis);
Dan Egnor01445162009-09-21 17:04:05 -07001445 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001446 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(finish)");
Joe Onorato8a9b2202010-02-26 18:56:32 -08001447 Slog.e(TAG, "Transport error in finishBackup()");
Dan Egnor01445162009-09-21 17:04:05 -07001448 }
1449 }
1450
Dan Egnor01445162009-09-21 17:04:05 -07001451 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
Christopher Tated55e18a2009-09-21 10:12:59 -07001452 // The backend reports that our dataset has been wiped. We need to
1453 // reset all of our bookkeeping and instead run a new backup pass for
Christopher Tatec2af5d32010-02-02 15:18:58 -08001454 // everything.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001455 EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
Christopher Tated55e18a2009-09-21 10:12:59 -07001456 resetBackupState(mStateDir);
Dan Egnorbb9001c2009-07-27 12:20:13 -07001457 }
1458 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001459 Slog.e(TAG, "Error in backup thread", e);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001460 status = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001461 } finally {
Christopher Tate84725812010-02-04 15:52:40 -08001462 // If everything actually went through and this is the first time we've
1463 // done a backup, we can now record what the current backup dataset token
1464 // is.
Christopher Tate29505552010-06-24 15:58:01 -07001465 if ((mCurrentToken == 0) && (status == BackupConstants.TRANSPORT_OK)) {
Christopher Tate84725812010-02-04 15:52:40 -08001466 try {
1467 mCurrentToken = mTransport.getCurrentRestoreSet();
1468 } catch (RemoteException e) { /* cannot happen */ }
1469 writeRestoreTokens();
1470 }
1471
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001472 // If things went wrong, we need to re-stage the apps we had expected
1473 // to be backing up in this pass. This journals the package names in
1474 // the current active pending-backup file, not in the we are holding
1475 // here in mJournal.
1476 if (status != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001477 Slog.w(TAG, "Backup pass unsuccessful, restaging");
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001478 for (BackupRequest req : mQueue) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07001479 dataChangedImpl(req.appInfo.packageName);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001480 }
Christopher Tate21ab6a52009-09-24 18:01:46 -07001481
1482 // We also want to reset the backup schedule based on whatever
1483 // the transport suggests by way of retry/backoff time.
1484 try {
1485 startBackupAlarmsLocked(mTransport.requestBackupTime());
1486 } catch (RemoteException e) { /* cannot happen */ }
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001487 }
1488
1489 // Either backup was successful, in which case we of course do not need
1490 // this pass's journal any more; or it failed, in which case we just
1491 // re-enqueued all of these packages in the current active journal.
1492 // Either way, we no longer need this pass's journal.
Dan Egnor852f8e42009-09-30 11:20:45 -07001493 if (mJournal != null && !mJournal.delete()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001494 Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001495 }
1496
Christopher Tatec2af5d32010-02-02 15:18:58 -08001497 // Only once we're entirely finished do we release the wakelock
Dan Egnor852f8e42009-09-30 11:20:45 -07001498 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
Dan Egnor852f8e42009-09-30 11:20:45 -07001499 backupNow();
1500 }
1501
Dan Egnorbb9001c2009-07-27 12:20:13 -07001502 mWakelock.release();
Christopher Tatecde87f42009-06-12 12:55:53 -07001503 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001504 }
1505
Dan Egnor01445162009-09-21 17:04:05 -07001506 private int doQueuedBackups(IBackupTransport transport) {
Christopher Tate043dadc2009-06-02 16:11:00 -07001507 for (BackupRequest request : mQueue) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001508 Slog.d(TAG, "starting agent for backup of " + request);
Christopher Tate043dadc2009-06-02 16:11:00 -07001509
Christopher Tatec28083a2010-12-14 16:16:44 -08001510 // Verify that the requested app exists; it might be something that
1511 // requested a backup but was then uninstalled. The request was
1512 // journalled and rather than tamper with the journal it's safer
1513 // to sanity-check here.
1514 try {
1515 mPackageManager.getPackageInfo(request.appInfo.packageName, 0);
1516 } catch (NameNotFoundException e) {
1517 Slog.d(TAG, "Package does not exist; skipping");
1518 continue;
1519 }
1520
Christopher Tate043dadc2009-06-02 16:11:00 -07001521 IBackupAgent agent = null;
Christopher Tate043dadc2009-06-02 16:11:00 -07001522 try {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001523 mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
Christopher Tate4a627c72011-04-01 14:43:32 -07001524 agent = bindToAgentSynchronous(request.appInfo,
1525 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001526 if (agent != null) {
Dan Egnor01445162009-09-21 17:04:05 -07001527 int result = processOneBackup(request, agent, transport);
1528 if (result != BackupConstants.TRANSPORT_OK) return result;
Christopher Tate043dadc2009-06-02 16:11:00 -07001529 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001530 } catch (SecurityException ex) {
1531 // Try for the next one.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001532 Slog.d(TAG, "error in bind/backup", ex);
Dan Egnor01445162009-09-21 17:04:05 -07001533 } finally {
1534 try { // unbind even on timeout, just in case
1535 mActivityManager.unbindBackupAgent(request.appInfo);
1536 } catch (RemoteException e) {}
Christopher Tate043dadc2009-06-02 16:11:00 -07001537 }
1538 }
Dan Egnor01445162009-09-21 17:04:05 -07001539
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001540 mWakelock.setWorkSource(null);
1541
Dan Egnor01445162009-09-21 17:04:05 -07001542 return BackupConstants.TRANSPORT_OK;
Christopher Tate043dadc2009-06-02 16:11:00 -07001543 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001544
Dan Egnor01445162009-09-21 17:04:05 -07001545 private int processOneBackup(BackupRequest request, IBackupAgent agent,
1546 IBackupTransport transport) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001547 final String packageName = request.appInfo.packageName;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001548 if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001549
Dan Egnorbb9001c2009-07-27 12:20:13 -07001550 File savedStateName = new File(mStateDir, packageName);
1551 File backupDataName = new File(mDataDir, packageName + ".data");
1552 File newStateName = new File(mStateDir, packageName + ".new");
1553
1554 ParcelFileDescriptor savedState = null;
1555 ParcelFileDescriptor backupData = null;
1556 ParcelFileDescriptor newState = null;
1557
1558 PackageInfo packInfo;
Christopher Tate4a627c72011-04-01 14:43:32 -07001559 final int token = generateToken();
Christopher Tatec7b31e32009-06-10 15:49:30 -07001560 try {
1561 // Look up the package info & signatures. This is first so that if it
1562 // throws an exception, there's no file setup yet that would need to
1563 // be unraveled.
Christopher Tateabce4e82009-06-18 18:35:32 -07001564 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
1565 // The metadata 'package' is synthetic
1566 packInfo = new PackageInfo();
1567 packInfo.packageName = packageName;
1568 } else {
1569 packInfo = mPackageManager.getPackageInfo(packageName,
Christopher Tatec7b31e32009-06-10 15:49:30 -07001570 PackageManager.GET_SIGNATURES);
Christopher Tateabce4e82009-06-18 18:35:32 -07001571 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001572
Christopher Tatec7b31e32009-06-10 15:49:30 -07001573 // In a full backup, we pass a null ParcelFileDescriptor as
Christopher Tate4a627c72011-04-01 14:43:32 -07001574 // the saved-state "file". This is by definition an incremental,
1575 // so we build a saved state file to pass.
1576 savedState = ParcelFileDescriptor.open(savedStateName,
1577 ParcelFileDescriptor.MODE_READ_ONLY |
1578 ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
Christopher Tatec7b31e32009-06-10 15:49:30 -07001579
Dan Egnorbb9001c2009-07-27 12:20:13 -07001580 backupData = ParcelFileDescriptor.open(backupDataName,
1581 ParcelFileDescriptor.MODE_READ_WRITE |
1582 ParcelFileDescriptor.MODE_CREATE |
1583 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001584
Dan Egnorbb9001c2009-07-27 12:20:13 -07001585 newState = ParcelFileDescriptor.open(newStateName,
1586 ParcelFileDescriptor.MODE_READ_WRITE |
1587 ParcelFileDescriptor.MODE_CREATE |
1588 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001589
Christopher Tate44a27902010-01-27 17:15:49 -08001590 // Initiate the target's backup pass
1591 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001592 agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder);
Christopher Tate44a27902010-01-27 17:15:49 -08001593 boolean success = waitUntilOperationComplete(token);
1594
1595 if (!success) {
1596 // timeout -- bail out into the failed-transaction logic
1597 throw new RuntimeException("Backup timeout");
1598 }
1599
Dan Egnorbb9001c2009-07-27 12:20:13 -07001600 logBackupComplete(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001601 if (DEBUG) Slog.v(TAG, "doBackup() success");
Christopher Tatec7b31e32009-06-10 15:49:30 -07001602 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001603 Slog.e(TAG, "Error backing up " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001604 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString());
Dan Egnorbb9001c2009-07-27 12:20:13 -07001605 backupDataName.delete();
1606 newStateName.delete();
Christopher Tated55e18a2009-09-21 10:12:59 -07001607 return BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001608 } finally {
1609 try { if (savedState != null) savedState.close(); } catch (IOException e) {}
1610 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1611 try { if (newState != null) newState.close(); } catch (IOException e) {}
1612 savedState = backupData = newState = null;
Christopher Tate44a27902010-01-27 17:15:49 -08001613 synchronized (mCurrentOpLock) {
1614 mCurrentOperations.clear();
1615 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001616 }
1617
1618 // Now propagate the newly-backed-up data to the transport
Dan Egnor01445162009-09-21 17:04:05 -07001619 int result = BackupConstants.TRANSPORT_OK;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001620 try {
1621 int size = (int) backupDataName.length();
1622 if (size > 0) {
Dan Egnor01445162009-09-21 17:04:05 -07001623 if (result == BackupConstants.TRANSPORT_OK) {
1624 backupData = ParcelFileDescriptor.open(backupDataName,
1625 ParcelFileDescriptor.MODE_READ_ONLY);
1626 result = transport.performBackup(packInfo, backupData);
1627 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001628
Dan Egnor83861e72009-09-17 16:17:55 -07001629 // TODO - We call finishBackup() for each application backed up, because
1630 // we need to know now whether it succeeded or failed. Instead, we should
1631 // hold off on finishBackup() until the end, which implies holding off on
1632 // renaming *all* the output state files (see below) until that happens.
1633
Dan Egnor01445162009-09-21 17:04:05 -07001634 if (result == BackupConstants.TRANSPORT_OK) {
1635 result = transport.finishBackup();
Dan Egnor83861e72009-09-17 16:17:55 -07001636 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001637 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001638 if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
Dan Egnorbb9001c2009-07-27 12:20:13 -07001639 }
1640
1641 // After successful transport, delete the now-stale data
1642 // and juggle the files so that next time we supply the agent
1643 // with the new state file it just created.
Dan Egnor01445162009-09-21 17:04:05 -07001644 if (result == BackupConstants.TRANSPORT_OK) {
1645 backupDataName.delete();
1646 newStateName.renameTo(savedStateName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001647 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, packageName, size);
Dan Egnor01445162009-09-21 17:04:05 -07001648 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001649 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
Dan Egnor01445162009-09-21 17:04:05 -07001650 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001651 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001652 Slog.e(TAG, "Transport error backing up " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001653 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
Dan Egnor01445162009-09-21 17:04:05 -07001654 result = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001655 } finally {
1656 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
Christopher Tatec7b31e32009-06-10 15:49:30 -07001657 }
Christopher Tated55e18a2009-09-21 10:12:59 -07001658
Dan Egnor01445162009-09-21 17:04:05 -07001659 return result;
Christopher Tatec7b31e32009-06-10 15:49:30 -07001660 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001661 }
1662
Christopher Tatedf01dea2009-06-09 20:45:02 -07001663
Christopher Tate4a627c72011-04-01 14:43:32 -07001664 // ----- Full backup to a file/socket -----
1665
1666 class PerformFullBackupTask implements Runnable {
1667 ParcelFileDescriptor mOutputFile;
1668 IFullBackupRestoreObserver mObserver;
1669 boolean mIncludeApks;
1670 boolean mIncludeShared;
1671 boolean mAllApps;
1672 String[] mPackages;
1673 AtomicBoolean mLatchObject;
1674 File mFilesDir;
1675 File mManifestFile;
1676
1677 PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
1678 boolean includeApks, boolean includeShared,
1679 boolean doAllApps, String[] packages, AtomicBoolean latch) {
1680 mOutputFile = fd;
1681 mObserver = observer;
1682 mIncludeApks = includeApks;
1683 mIncludeShared = includeShared;
1684 mAllApps = doAllApps;
1685 mPackages = packages;
1686 mLatchObject = latch;
1687
1688 mFilesDir = new File("/data/system");
1689 mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
1690 }
1691
1692 @Override
1693 public void run() {
1694 final List<PackageInfo> packagesToBackup;
1695
Christopher Tateb0628bf2011-06-02 15:08:13 -07001696 Slog.i(TAG, "--- Performing full-dataset backup ---");
Christopher Tate4a627c72011-04-01 14:43:32 -07001697 sendStartBackup();
1698
1699 // doAllApps supersedes the package set if any
1700 if (mAllApps) {
1701 packagesToBackup = mPackageManager.getInstalledPackages(
1702 PackageManager.GET_SIGNATURES);
1703 } else {
1704 packagesToBackup = new ArrayList<PackageInfo>();
1705 for (String pkgName : mPackages) {
1706 try {
1707 packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
1708 PackageManager.GET_SIGNATURES));
1709 } catch (NameNotFoundException e) {
1710 Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
1711 }
1712 }
1713 }
1714
Christopher Tatea858cb02011-06-03 12:27:51 -07001715 // Cull any packages that have indicated that backups are not permitted.
1716 for (int i = 0; i < packagesToBackup.size(); ) {
1717 PackageInfo info = packagesToBackup.get(i);
1718 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
1719 packagesToBackup.remove(i);
1720 } else {
1721 i++;
1722 }
1723 }
1724
Christopher Tate4a627c72011-04-01 14:43:32 -07001725 PackageInfo pkg = null;
1726 try {
Christopher Tateb0628bf2011-06-02 15:08:13 -07001727 // Now back up the app data via the agent mechanism
Christopher Tate4a627c72011-04-01 14:43:32 -07001728 int N = packagesToBackup.size();
1729 for (int i = 0; i < N; i++) {
1730 pkg = packagesToBackup.get(i);
Christopher Tateb0628bf2011-06-02 15:08:13 -07001731 backupOnePackage(pkg);
1732 }
Christopher Tate4a627c72011-04-01 14:43:32 -07001733
Christopher Tateb0628bf2011-06-02 15:08:13 -07001734 // Finally, shared storage if requested
1735 if (mIncludeShared) {
1736 backupSharedStorage();
Christopher Tate4a627c72011-04-01 14:43:32 -07001737 }
1738 } catch (RemoteException e) {
1739 Slog.e(TAG, "App died during full backup");
1740 } finally {
Christopher Tateb0628bf2011-06-02 15:08:13 -07001741 tearDown(pkg);
Christopher Tate4a627c72011-04-01 14:43:32 -07001742 try {
1743 mOutputFile.close();
1744 } catch (IOException e) {
1745 /* nothing we can do about this */
1746 }
1747 synchronized (mCurrentOpLock) {
1748 mCurrentOperations.clear();
1749 }
1750 synchronized (mLatchObject) {
1751 mLatchObject.set(true);
1752 mLatchObject.notifyAll();
1753 }
1754 sendEndBackup();
1755 mWakelock.release();
1756 if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
1757 }
1758 }
1759
Christopher Tateb0628bf2011-06-02 15:08:13 -07001760 private void backupOnePackage(PackageInfo pkg) throws RemoteException {
1761 Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
1762
1763 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
1764 IApplicationThread.BACKUP_MODE_FULL);
1765 if (agent != null) {
1766 try {
1767 ApplicationInfo app = pkg.applicationInfo;
Christopher Tate79ec80d2011-06-24 14:58:49 -07001768 final boolean sendApk = mIncludeApks
Christopher Tateb0628bf2011-06-02 15:08:13 -07001769 && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
1770 && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
1771 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
1772
1773 sendOnBackupPackage(pkg.packageName);
1774
Christopher Tate79ec80d2011-06-24 14:58:49 -07001775 BackupDataOutput output = new BackupDataOutput(
1776 mOutputFile.getFileDescriptor());
Christopher Tateb0628bf2011-06-02 15:08:13 -07001777
Christopher Tate79ec80d2011-06-24 14:58:49 -07001778 if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
1779 writeAppManifest(pkg, mManifestFile, sendApk);
1780 FullBackup.backupToTar(pkg.packageName, null, null,
1781 mFilesDir.getAbsolutePath(),
1782 mManifestFile.getAbsolutePath(),
1783 output);
1784
1785 if (sendApk) {
1786 writeApkToBackup(pkg, output);
Christopher Tateb0628bf2011-06-02 15:08:13 -07001787 }
1788
Christopher Tate79ec80d2011-06-24 14:58:49 -07001789 if (DEBUG) Slog.d(TAG, "Calling doFullBackup()");
Christopher Tateb0628bf2011-06-02 15:08:13 -07001790 final int token = generateToken();
1791 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001792 agent.doFullBackup(mOutputFile, token, mBackupManagerBinder);
Christopher Tateb0628bf2011-06-02 15:08:13 -07001793 if (!waitUntilOperationComplete(token)) {
1794 Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
1795 } else {
1796 if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
1797 }
1798 } catch (IOException e) {
1799 Slog.e(TAG, "Error backing up " + pkg.packageName, e);
1800 }
1801 } else {
1802 Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
1803 }
1804 tearDown(pkg);
1805 }
1806
Christopher Tate79ec80d2011-06-24 14:58:49 -07001807 private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
1808 // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
1809 final String appSourceDir = pkg.applicationInfo.sourceDir;
1810 final String apkDir = new File(appSourceDir).getParent();
1811 FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
1812 apkDir, appSourceDir, output);
1813
1814 // Save associated .obb content if it exists and we did save the apk
1815 // check for .obb and save those too
1816 final File obbDir = Environment.getExternalStorageAppObbDirectory(pkg.packageName);
1817 if (obbDir != null) {
1818 if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
1819 File[] obbFiles = obbDir.listFiles();
1820 if (obbFiles != null) {
1821 final String obbDirName = obbDir.getAbsolutePath();
1822 for (File obb : obbFiles) {
1823 FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
1824 obbDirName, obb.getAbsolutePath(), output);
1825 }
1826 }
1827 }
1828 }
1829
Christopher Tateb0628bf2011-06-02 15:08:13 -07001830 private void backupSharedStorage() throws RemoteException {
1831 PackageInfo pkg = null;
1832 try {
1833 pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0);
1834 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
1835 IApplicationThread.BACKUP_MODE_FULL);
1836 if (agent != null) {
1837 sendOnBackupPackage("Shared storage");
1838
1839 final int token = generateToken();
1840 prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001841 agent.doFullBackup(mOutputFile, token, mBackupManagerBinder);
Christopher Tateb0628bf2011-06-02 15:08:13 -07001842 if (!waitUntilOperationComplete(token)) {
1843 Slog.e(TAG, "Full backup failed on shared storage");
1844 } else {
1845 if (DEBUG) Slog.d(TAG, "Full shared storage backup success");
1846 }
1847 } else {
1848 Slog.e(TAG, "Could not bind to shared storage backup agent");
1849 }
1850 } catch (NameNotFoundException e) {
1851 Slog.e(TAG, "Shared storage backup package not found");
1852 } finally {
1853 tearDown(pkg);
1854 }
1855 }
1856
Christopher Tate4a627c72011-04-01 14:43:32 -07001857 private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
1858 throws IOException {
1859 // Manifest format. All data are strings ending in LF:
1860 // BACKUP_MANIFEST_VERSION, currently 1
1861 //
1862 // Version 1:
1863 // package name
1864 // package's versionCode
Christopher Tate75a99702011-05-18 16:28:19 -07001865 // platform versionCode
1866 // getInstallerPackageName() for this package (maybe empty)
1867 // boolean: "1" if archive includes .apk; any other string means not
Christopher Tate4a627c72011-04-01 14:43:32 -07001868 // number of signatures == N
1869 // N*: signature byte array in ascii format per Signature.toCharsString()
1870 StringBuilder builder = new StringBuilder(4096);
1871 StringBuilderPrinter printer = new StringBuilderPrinter(builder);
1872
1873 printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
1874 printer.println(pkg.packageName);
1875 printer.println(Integer.toString(pkg.versionCode));
Christopher Tate75a99702011-05-18 16:28:19 -07001876 printer.println(Integer.toString(Build.VERSION.SDK_INT));
1877
1878 String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
1879 printer.println((installerName != null) ? installerName : "");
1880
Christopher Tate4a627c72011-04-01 14:43:32 -07001881 printer.println(withApk ? "1" : "0");
1882 if (pkg.signatures == null) {
1883 printer.println("0");
1884 } else {
1885 printer.println(Integer.toString(pkg.signatures.length));
1886 for (Signature sig : pkg.signatures) {
1887 printer.println(sig.toCharsString());
1888 }
1889 }
1890
1891 FileOutputStream outstream = new FileOutputStream(manifestFile);
Christopher Tate4a627c72011-04-01 14:43:32 -07001892 outstream.write(builder.toString().getBytes());
1893 outstream.close();
1894 }
1895
1896 private void tearDown(PackageInfo pkg) {
Christopher Tateb0628bf2011-06-02 15:08:13 -07001897 if (pkg != null) {
1898 final ApplicationInfo app = pkg.applicationInfo;
1899 if (app != null) {
1900 try {
1901 // unbind and tidy up even on timeout or failure, just in case
1902 mActivityManager.unbindBackupAgent(app);
Christopher Tate4a627c72011-04-01 14:43:32 -07001903
Christopher Tateb0628bf2011-06-02 15:08:13 -07001904 // The agent was running with a stub Application object, so shut it down.
1905 if (app.uid != Process.SYSTEM_UID) {
1906 if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
1907 mActivityManager.killApplicationProcess(app.processName, app.uid);
1908 } else {
1909 if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
1910 }
1911 } catch (RemoteException e) {
1912 Slog.d(TAG, "Lost app trying to shut down");
1913 }
Christopher Tate4a627c72011-04-01 14:43:32 -07001914 }
Christopher Tate4a627c72011-04-01 14:43:32 -07001915 }
1916 }
1917
1918 // wrappers for observer use
1919 void sendStartBackup() {
1920 if (mObserver != null) {
1921 try {
1922 mObserver.onStartBackup();
1923 } catch (RemoteException e) {
1924 Slog.w(TAG, "full backup observer went away: startBackup");
1925 mObserver = null;
1926 }
1927 }
1928 }
1929
1930 void sendOnBackupPackage(String name) {
1931 if (mObserver != null) {
1932 try {
1933 // TODO: use a more user-friendly name string
1934 mObserver.onBackupPackage(name);
1935 } catch (RemoteException e) {
1936 Slog.w(TAG, "full backup observer went away: backupPackage");
1937 mObserver = null;
1938 }
1939 }
1940 }
1941
1942 void sendEndBackup() {
1943 if (mObserver != null) {
1944 try {
1945 mObserver.onEndBackup();
1946 } catch (RemoteException e) {
1947 Slog.w(TAG, "full backup observer went away: endBackup");
1948 mObserver = null;
1949 }
1950 }
1951 }
1952 }
1953
1954
Christopher Tate75a99702011-05-18 16:28:19 -07001955 // ----- Full restore from a file/socket -----
1956
1957 // Description of a file in the restore datastream
1958 static class FileMetadata {
1959 String packageName; // name of the owning app
1960 String installerPackageName; // name of the market-type app that installed the owner
Christopher Tate79ec80d2011-06-24 14:58:49 -07001961 int type; // e.g. BackupAgent.TYPE_DIRECTORY
Christopher Tate75a99702011-05-18 16:28:19 -07001962 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN
1963 String path; // subpath within the semantic domain
1964 long mode; // e.g. 0666 (actually int)
1965 long mtime; // last mod time, UTC time_t (actually int)
1966 long size; // bytes of content
Christopher Tatee9e78ec2011-06-08 20:09:31 -07001967
1968 @Override
1969 public String toString() {
1970 StringBuilder sb = new StringBuilder(128);
1971 sb.append("FileMetadata{");
1972 sb.append(packageName); sb.append(',');
1973 sb.append(type); sb.append(',');
1974 sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
1975 sb.append(size);
1976 sb.append('}');
1977 return sb.toString();
1978 }
Christopher Tate75a99702011-05-18 16:28:19 -07001979 }
1980
1981 enum RestorePolicy {
1982 IGNORE,
1983 ACCEPT,
1984 ACCEPT_IF_APK
1985 }
1986
1987 class PerformFullRestoreTask implements Runnable {
1988 ParcelFileDescriptor mInputFile;
1989 IFullBackupRestoreObserver mObserver;
1990 AtomicBoolean mLatchObject;
1991 IBackupAgent mAgent;
1992 String mAgentPackage;
1993 ApplicationInfo mTargetApp;
1994 ParcelFileDescriptor[] mPipes = null;
1995
Christopher Tatee9e78ec2011-06-08 20:09:31 -07001996 long mBytes;
1997
Christopher Tate75a99702011-05-18 16:28:19 -07001998 // possible handling states for a given package in the restore dataset
1999 final HashMap<String, RestorePolicy> mPackagePolicies
2000 = new HashMap<String, RestorePolicy>();
2001
2002 // installer package names for each encountered app, derived from the manifests
2003 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
2004
2005 // Signatures for a given package found in its manifest file
2006 final HashMap<String, Signature[]> mManifestSignatures
2007 = new HashMap<String, Signature[]>();
2008
2009 // Packages we've already wiped data on when restoring their first file
2010 final HashSet<String> mClearedPackages = new HashSet<String>();
2011
2012 PerformFullRestoreTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
2013 AtomicBoolean latch) {
2014 mInputFile = fd;
2015 mObserver = observer;
2016 mLatchObject = latch;
2017 mAgent = null;
2018 mAgentPackage = null;
2019 mTargetApp = null;
2020
2021 // Which packages we've already wiped data on. We prepopulate this
2022 // with a whitelist of packages known to be unclearable.
2023 mClearedPackages.add("android");
Christopher Tate75a99702011-05-18 16:28:19 -07002024 mClearedPackages.add("com.android.providers.settings");
Christopher Tateb0628bf2011-06-02 15:08:13 -07002025
Christopher Tate75a99702011-05-18 16:28:19 -07002026 }
2027
2028 class RestoreFileRunnable implements Runnable {
2029 IBackupAgent mAgent;
2030 FileMetadata mInfo;
2031 ParcelFileDescriptor mSocket;
2032 int mToken;
2033
2034 RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
2035 ParcelFileDescriptor socket, int token) throws IOException {
2036 mAgent = agent;
2037 mInfo = info;
2038 mToken = token;
2039
2040 // This class is used strictly for process-local binder invocations. The
2041 // semantics of ParcelFileDescriptor differ in this case; in particular, we
2042 // do not automatically get a 'dup'ed descriptor that we can can continue
2043 // to use asynchronously from the caller. So, we make sure to dup it ourselves
2044 // before proceeding to do the restore.
2045 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
2046 }
2047
2048 @Override
2049 public void run() {
2050 try {
2051 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
2052 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
2053 mToken, mBackupManagerBinder);
2054 } catch (RemoteException e) {
2055 // never happens; this is used strictly for local binder calls
2056 }
2057 }
2058 }
2059
2060 @Override
2061 public void run() {
2062 Slog.i(TAG, "--- Performing full-dataset restore ---");
2063 sendStartRestore();
2064
Christopher Tateb0628bf2011-06-02 15:08:13 -07002065 // Are we able to restore shared-storage data?
2066 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
2067 mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
2068 }
2069
Christopher Tate75a99702011-05-18 16:28:19 -07002070 try {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002071 mBytes = 0;
Christopher Tate75a99702011-05-18 16:28:19 -07002072 byte[] buffer = new byte[32 * 1024];
2073 FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
2074
2075 boolean didRestore;
2076 do {
2077 didRestore = restoreOneFile(instream, buffer);
2078 } while (didRestore);
2079
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002080 if (DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
Christopher Tate75a99702011-05-18 16:28:19 -07002081 } finally {
2082 tearDownPipes();
2083 tearDownAgent(mTargetApp);
2084
2085 try {
2086 mInputFile.close();
2087 } catch (IOException e) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002088 Slog.w(TAG, "Close of restore data pipe threw", e);
Christopher Tate75a99702011-05-18 16:28:19 -07002089 /* nothing we can do about this */
2090 }
2091 synchronized (mCurrentOpLock) {
2092 mCurrentOperations.clear();
2093 }
2094 synchronized (mLatchObject) {
2095 mLatchObject.set(true);
2096 mLatchObject.notifyAll();
2097 }
2098 sendEndRestore();
2099 mWakelock.release();
2100 if (DEBUG) Slog.d(TAG, "Full restore pass complete.");
2101 }
2102 }
2103
2104 boolean restoreOneFile(InputStream instream, byte[] buffer) {
2105 FileMetadata info;
2106 try {
2107 info = readTarHeaders(instream);
2108 if (info != null) {
2109 if (DEBUG) {
2110 dumpFileMetadata(info);
2111 }
2112
2113 final String pkg = info.packageName;
2114 if (!pkg.equals(mAgentPackage)) {
2115 // okay, change in package; set up our various
2116 // bookkeeping if we haven't seen it yet
2117 if (!mPackagePolicies.containsKey(pkg)) {
2118 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2119 }
2120
2121 // Clean up the previous agent relationship if necessary,
2122 // and let the observer know we're considering a new app.
2123 if (mAgent != null) {
2124 if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
2125 tearDownPipes();
2126 tearDownAgent(mTargetApp);
2127 mTargetApp = null;
2128 mAgentPackage = null;
2129 }
2130 }
2131
2132 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2133 mPackagePolicies.put(pkg, readAppManifest(info, instream));
2134 mPackageInstallers.put(pkg, info.installerPackageName);
2135 // We've read only the manifest content itself at this point,
2136 // so consume the footer before looping around to the next
2137 // input file
2138 skipTarPadding(info.size, instream);
2139 sendOnRestorePackage(pkg);
2140 } else {
2141 // Non-manifest, so it's actual file data. Is this a package
2142 // we're ignoring?
2143 boolean okay = true;
2144 RestorePolicy policy = mPackagePolicies.get(pkg);
2145 switch (policy) {
2146 case IGNORE:
2147 okay = false;
2148 break;
2149
2150 case ACCEPT_IF_APK:
2151 // If we're in accept-if-apk state, then the first file we
2152 // see MUST be the apk.
2153 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2154 if (DEBUG) Slog.d(TAG, "APK file; installing");
2155 // Try to install the app.
2156 String installerName = mPackageInstallers.get(pkg);
2157 okay = installApk(info, installerName, instream);
2158 // good to go; promote to ACCEPT
2159 mPackagePolicies.put(pkg, (okay)
2160 ? RestorePolicy.ACCEPT
2161 : RestorePolicy.IGNORE);
2162 // At this point we've consumed this file entry
2163 // ourselves, so just strip the tar footer and
2164 // go on to the next file in the input stream
2165 skipTarPadding(info.size, instream);
2166 return true;
2167 } else {
2168 // File data before (or without) the apk. We can't
2169 // handle it coherently in this case so ignore it.
2170 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2171 okay = false;
2172 }
2173 break;
2174
2175 case ACCEPT:
2176 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2177 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
2178 // we can take the data without the apk, so we
2179 // *want* to do so. skip the apk by declaring this
2180 // one file not-okay without changing the restore
2181 // policy for the package.
2182 okay = false;
2183 }
2184 break;
2185
2186 default:
2187 // Something has gone dreadfully wrong when determining
2188 // the restore policy from the manifest. Ignore the
2189 // rest of this package's data.
2190 Slog.e(TAG, "Invalid policy from manifest");
2191 okay = false;
2192 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2193 break;
2194 }
2195
2196 // If the policy is satisfied, go ahead and set up to pipe the
2197 // data to the agent.
2198 if (DEBUG && okay && mAgent != null) {
2199 Slog.i(TAG, "Reusing existing agent instance");
2200 }
2201 if (okay && mAgent == null) {
2202 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
2203
2204 try {
2205 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
2206
2207 // If we haven't sent any data to this app yet, we probably
2208 // need to clear it first. Check that.
2209 if (!mClearedPackages.contains(pkg)) {
Christopher Tate79ec80d2011-06-24 14:58:49 -07002210 // apps with their own backup agents are
Christopher Tate75a99702011-05-18 16:28:19 -07002211 // responsible for coherently managing a full
2212 // restore.
Christopher Tate79ec80d2011-06-24 14:58:49 -07002213 if (mTargetApp.backupAgentName == null) {
Christopher Tate75a99702011-05-18 16:28:19 -07002214 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
2215 clearApplicationDataSynchronous(pkg);
2216 } else {
Christopher Tate79ec80d2011-06-24 14:58:49 -07002217 if (DEBUG) Slog.d(TAG, "backup agent ("
2218 + mTargetApp.backupAgentName + ") => no clear");
Christopher Tate75a99702011-05-18 16:28:19 -07002219 }
2220 mClearedPackages.add(pkg);
2221 } else {
2222 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
2223 }
2224
2225 // All set; now set up the IPC and launch the agent
2226 setUpPipes();
2227 mAgent = bindToAgentSynchronous(mTargetApp,
2228 IApplicationThread.BACKUP_MODE_RESTORE_FULL);
2229 mAgentPackage = pkg;
2230 } catch (IOException e) {
2231 // fall through to error handling
2232 } catch (NameNotFoundException e) {
2233 // fall through to error handling
2234 }
2235
2236 if (mAgent == null) {
2237 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
2238 okay = false;
2239 tearDownPipes();
2240 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2241 }
2242 }
2243
2244 // Sanity check: make sure we never give data to the wrong app. This
2245 // should never happen but a little paranoia here won't go amiss.
2246 if (okay && !pkg.equals(mAgentPackage)) {
2247 Slog.e(TAG, "Restoring data for " + pkg
2248 + " but agent is for " + mAgentPackage);
2249 okay = false;
2250 }
2251
2252 // At this point we have an agent ready to handle the full
2253 // restore data as well as a pipe for sending data to
2254 // that agent. Tell the agent to start reading from the
2255 // pipe.
2256 if (okay) {
2257 boolean agentSuccess = true;
2258 long toCopy = info.size;
2259 final int token = generateToken();
2260 try {
2261 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
2262 + info.path);
2263 prepareOperationTimeout(token,
2264 TIMEOUT_FULL_BACKUP_INTERVAL);
2265 // fire up the app's agent listening on the socket. If
2266 // the agent is running in the system process we can't
2267 // just invoke it asynchronously, so we provide a thread
2268 // for it here.
2269 if (mTargetApp.processName.equals("system")) {
2270 Slog.d(TAG, "system process agent - spinning a thread");
2271 RestoreFileRunnable runner = new RestoreFileRunnable(
2272 mAgent, info, mPipes[0], token);
2273 new Thread(runner).start();
2274 } else {
2275 mAgent.doRestoreFile(mPipes[0], info.size, info.type,
2276 info.domain, info.path, info.mode, info.mtime,
2277 token, mBackupManagerBinder);
2278 }
2279 } catch (IOException e) {
2280 // couldn't dup the socket for a process-local restore
2281 Slog.d(TAG, "Couldn't establish restore");
2282 agentSuccess = false;
2283 okay = false;
2284 } catch (RemoteException e) {
2285 // whoops, remote agent went away. We'll eat the content
2286 // ourselves, then, and not copy it over.
2287 Slog.e(TAG, "Agent crashed during full restore");
2288 agentSuccess = false;
2289 okay = false;
2290 }
2291
2292 // Copy over the data if the agent is still good
2293 if (okay) {
2294 boolean pipeOkay = true;
2295 FileOutputStream pipe = new FileOutputStream(
2296 mPipes[1].getFileDescriptor());
2297 if (DEBUG) Slog.d(TAG, "Piping data to agent");
2298 while (toCopy > 0) {
2299 int toRead = (toCopy > buffer.length)
2300 ? buffer.length : (int)toCopy;
2301 int nRead = instream.read(buffer, 0, toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002302 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002303 if (nRead <= 0) break;
2304 toCopy -= nRead;
2305
2306 // send it to the output pipe as long as things
2307 // are still good
2308 if (pipeOkay) {
2309 try {
2310 pipe.write(buffer, 0, nRead);
2311 } catch (IOException e) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002312 Slog.e(TAG, "Failed to write to restore pipe", e);
Christopher Tate75a99702011-05-18 16:28:19 -07002313 pipeOkay = false;
2314 }
2315 }
2316 }
2317
2318 // done sending that file! Now we just need to consume
2319 // the delta from info.size to the end of block.
2320 skipTarPadding(info.size, instream);
2321
2322 // and now that we've sent it all, wait for the remote
2323 // side to acknowledge receipt
2324 agentSuccess = waitUntilOperationComplete(token);
2325 }
2326
2327 // okay, if the remote end failed at any point, deal with
2328 // it by ignoring the rest of the restore on it
2329 if (!agentSuccess) {
2330 mBackupHandler.removeMessages(MSG_TIMEOUT);
2331 tearDownPipes();
2332 tearDownAgent(mTargetApp);
2333 mAgent = null;
2334 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2335 }
2336 }
2337
2338 // Problems setting up the agent communication, or an already-
2339 // ignored package: skip to the next tar stream entry by
2340 // reading and discarding this file.
2341 if (!okay) {
2342 if (DEBUG) Slog.d(TAG, "[discarding file content]");
2343 long bytesToConsume = (info.size + 511) & ~511;
2344 while (bytesToConsume > 0) {
2345 int toRead = (bytesToConsume > buffer.length)
2346 ? buffer.length : (int)bytesToConsume;
2347 long nRead = instream.read(buffer, 0, toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002348 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002349 if (nRead <= 0) break;
2350 bytesToConsume -= nRead;
2351 }
2352 }
2353 }
2354 }
2355 } catch (IOException e) {
2356 Slog.w(TAG, "io exception on restore socket read", e);
2357 // treat as EOF
2358 info = null;
2359 }
2360
2361 return (info != null);
2362 }
2363
2364 void setUpPipes() throws IOException {
2365 mPipes = ParcelFileDescriptor.createPipe();
2366 }
2367
2368 void tearDownPipes() {
2369 if (mPipes != null) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002370 try {
2371 mPipes[0].close();
2372 mPipes[0] = null;
2373 mPipes[1].close();
2374 mPipes[1] = null;
2375 } catch (IOException e) {
2376 Slog.w(TAG, "Couldn't close agent pipes", e);
Christopher Tate75a99702011-05-18 16:28:19 -07002377 }
2378 mPipes = null;
2379 }
2380 }
2381
2382 void tearDownAgent(ApplicationInfo app) {
2383 if (mAgent != null) {
2384 try {
2385 // unbind and tidy up even on timeout or failure, just in case
2386 mActivityManager.unbindBackupAgent(app);
2387
2388 // The agent was running with a stub Application object, so shut it down.
2389 // !!! We hardcode the confirmation UI's package name here rather than use a
2390 // manifest flag! TODO something less direct.
2391 if (app.uid != Process.SYSTEM_UID
2392 && !app.packageName.equals("com.android.backupconfirm")) {
2393 if (DEBUG) Slog.d(TAG, "Killing host process");
2394 mActivityManager.killApplicationProcess(app.processName, app.uid);
2395 } else {
2396 if (DEBUG) Slog.d(TAG, "Not killing after full restore");
2397 }
2398 } catch (RemoteException e) {
2399 Slog.d(TAG, "Lost app trying to shut down");
2400 }
2401 mAgent = null;
2402 }
2403 }
2404
2405 class RestoreInstallObserver extends IPackageInstallObserver.Stub {
2406 final AtomicBoolean mDone = new AtomicBoolean();
Christopher Tatea858cb02011-06-03 12:27:51 -07002407 String mPackageName;
Christopher Tate75a99702011-05-18 16:28:19 -07002408 int mResult;
2409
2410 public void reset() {
2411 synchronized (mDone) {
2412 mDone.set(false);
2413 }
2414 }
2415
2416 public void waitForCompletion() {
2417 synchronized (mDone) {
2418 while (mDone.get() == false) {
2419 try {
2420 mDone.wait();
2421 } catch (InterruptedException e) { }
2422 }
2423 }
2424 }
2425
2426 int getResult() {
2427 return mResult;
2428 }
2429
2430 @Override
2431 public void packageInstalled(String packageName, int returnCode)
2432 throws RemoteException {
2433 synchronized (mDone) {
2434 mResult = returnCode;
Christopher Tatea858cb02011-06-03 12:27:51 -07002435 mPackageName = packageName;
Christopher Tate75a99702011-05-18 16:28:19 -07002436 mDone.set(true);
2437 mDone.notifyAll();
2438 }
2439 }
2440 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002441
2442 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
2443 final AtomicBoolean mDone = new AtomicBoolean();
2444 int mResult;
2445
2446 public void reset() {
2447 synchronized (mDone) {
2448 mDone.set(false);
2449 }
2450 }
2451
2452 public void waitForCompletion() {
2453 synchronized (mDone) {
2454 while (mDone.get() == false) {
2455 try {
2456 mDone.wait();
2457 } catch (InterruptedException e) { }
2458 }
2459 }
2460 }
2461
2462 @Override
2463 public void packageDeleted(String packageName, int returnCode) throws RemoteException {
2464 synchronized (mDone) {
2465 mResult = returnCode;
2466 mDone.set(true);
2467 mDone.notifyAll();
2468 }
2469 }
2470 }
2471
Christopher Tate75a99702011-05-18 16:28:19 -07002472 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
Christopher Tatea858cb02011-06-03 12:27:51 -07002473 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
Christopher Tate75a99702011-05-18 16:28:19 -07002474
2475 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
2476 boolean okay = true;
2477
2478 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
2479
2480 // The file content is an .apk file. Copy it out to a staging location and
2481 // attempt to install it.
2482 File apkFile = new File(mDataDir, info.packageName);
2483 try {
2484 FileOutputStream apkStream = new FileOutputStream(apkFile);
2485 byte[] buffer = new byte[32 * 1024];
2486 long size = info.size;
2487 while (size > 0) {
2488 long toRead = (buffer.length < size) ? buffer.length : size;
2489 int didRead = instream.read(buffer, 0, (int)toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002490 if (didRead >= 0) mBytes += didRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002491 apkStream.write(buffer, 0, didRead);
2492 size -= didRead;
2493 }
2494 apkStream.close();
2495
2496 // make sure the installer can read it
2497 apkFile.setReadable(true, false);
2498
2499 // Now install it
2500 Uri packageUri = Uri.fromFile(apkFile);
2501 mInstallObserver.reset();
2502 mPackageManager.installPackage(packageUri, mInstallObserver,
2503 PackageManager.INSTALL_REPLACE_EXISTING, installerPackage);
2504 mInstallObserver.waitForCompletion();
2505
2506 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
2507 // The only time we continue to accept install of data even if the
2508 // apk install failed is if we had already determined that we could
2509 // accept the data regardless.
2510 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
2511 okay = false;
2512 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002513 } else {
2514 // Okay, the install succeeded. Make sure it was the right app.
2515 boolean uninstall = false;
2516 if (!mInstallObserver.mPackageName.equals(info.packageName)) {
2517 Slog.w(TAG, "Restore stream claimed to include apk for "
2518 + info.packageName + " but apk was really "
2519 + mInstallObserver.mPackageName);
2520 // delete the package we just put in place; it might be fraudulent
2521 okay = false;
2522 uninstall = true;
2523 } else {
2524 try {
2525 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
2526 PackageManager.GET_SIGNATURES);
2527 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
2528 Slog.w(TAG, "Restore stream contains apk of package "
2529 + info.packageName + " but it disallows backup/restore");
2530 okay = false;
2531 } else {
2532 // So far so good -- do the signatures match the manifest?
2533 Signature[] sigs = mManifestSignatures.get(info.packageName);
2534 if (!signaturesMatch(sigs, pkg)) {
2535 Slog.w(TAG, "Installed app " + info.packageName
2536 + " signatures do not match restore manifest");
2537 okay = false;
2538 uninstall = true;
2539 }
2540 }
2541 } catch (NameNotFoundException e) {
2542 Slog.w(TAG, "Install of package " + info.packageName
2543 + " succeeded but now not found");
2544 okay = false;
2545 }
2546 }
2547
2548 // If we're not okay at this point, we need to delete the package
2549 // that we just installed.
2550 if (uninstall) {
2551 mDeleteObserver.reset();
2552 mPackageManager.deletePackage(mInstallObserver.mPackageName,
2553 mDeleteObserver, 0);
2554 mDeleteObserver.waitForCompletion();
2555 }
Christopher Tate75a99702011-05-18 16:28:19 -07002556 }
2557 } catch (IOException e) {
2558 Slog.e(TAG, "Unable to transcribe restored apk for install");
2559 okay = false;
2560 } finally {
2561 apkFile.delete();
2562 }
2563
2564 return okay;
2565 }
2566
2567 // Given an actual file content size, consume the post-content padding mandated
2568 // by the tar format.
2569 void skipTarPadding(long size, InputStream instream) throws IOException {
2570 long partial = (size + 512) % 512;
2571 if (partial > 0) {
2572 byte[] buffer = new byte[512];
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002573 int nRead = instream.read(buffer, 0, 512 - (int)partial);
2574 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002575 }
2576 }
2577
2578 // Returns a policy constant; takes a buffer arg to reduce memory churn
2579 RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
2580 throws IOException {
2581 // Fail on suspiciously large manifest files
2582 if (info.size > 64 * 1024) {
2583 throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
2584 }
2585 byte[] buffer = new byte[(int) info.size];
2586 int nRead = 0;
2587 while (nRead < info.size) {
2588 nRead += instream.read(buffer, nRead, (int)info.size - nRead);
2589 }
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002590 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002591
2592 RestorePolicy policy = RestorePolicy.IGNORE;
2593 String[] str = new String[1];
2594 int offset = 0;
2595
2596 try {
2597 offset = extractLine(buffer, offset, str);
2598 int version = Integer.parseInt(str[0]);
2599 if (version == BACKUP_MANIFEST_VERSION) {
2600 offset = extractLine(buffer, offset, str);
2601 String manifestPackage = str[0];
2602 // TODO: handle <original-package>
2603 if (manifestPackage.equals(info.packageName)) {
2604 offset = extractLine(buffer, offset, str);
2605 version = Integer.parseInt(str[0]); // app version
2606 offset = extractLine(buffer, offset, str);
2607 int platformVersion = Integer.parseInt(str[0]);
2608 offset = extractLine(buffer, offset, str);
2609 info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
2610 offset = extractLine(buffer, offset, str);
2611 boolean hasApk = str[0].equals("1");
2612 offset = extractLine(buffer, offset, str);
2613 int numSigs = Integer.parseInt(str[0]);
Christopher Tate75a99702011-05-18 16:28:19 -07002614 if (numSigs > 0) {
Christopher Tatea858cb02011-06-03 12:27:51 -07002615 Signature[] sigs = new Signature[numSigs];
Christopher Tate75a99702011-05-18 16:28:19 -07002616 for (int i = 0; i < numSigs; i++) {
2617 offset = extractLine(buffer, offset, str);
2618 sigs[i] = new Signature(str[0]);
2619 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002620 mManifestSignatures.put(info.packageName, sigs);
Christopher Tate75a99702011-05-18 16:28:19 -07002621
2622 // Okay, got the manifest info we need...
2623 try {
Christopher Tate75a99702011-05-18 16:28:19 -07002624 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2625 info.packageName, PackageManager.GET_SIGNATURES);
Christopher Tatea858cb02011-06-03 12:27:51 -07002626 // Fall through to IGNORE if the app explicitly disallows backup
2627 final int flags = pkgInfo.applicationInfo.flags;
2628 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
2629 // Verify signatures against any installed version; if they
2630 // don't match, then we fall though and ignore the data. The
2631 // signatureMatch() method explicitly ignores the signature
2632 // check for packages installed on the system partition, because
2633 // such packages are signed with the platform cert instead of
2634 // the app developer's cert, so they're different on every
2635 // device.
2636 if (signaturesMatch(sigs, pkgInfo)) {
2637 if (pkgInfo.versionCode >= version) {
2638 Slog.i(TAG, "Sig + version match; taking data");
2639 policy = RestorePolicy.ACCEPT;
2640 } else {
2641 // The data is from a newer version of the app than
2642 // is presently installed. That means we can only
2643 // use it if the matching apk is also supplied.
2644 Slog.d(TAG, "Data version " + version
2645 + " is newer than installed version "
2646 + pkgInfo.versionCode + " - requiring apk");
2647 policy = RestorePolicy.ACCEPT_IF_APK;
2648 }
Christopher Tate75a99702011-05-18 16:28:19 -07002649 } else {
Christopher Tatea858cb02011-06-03 12:27:51 -07002650 Slog.w(TAG, "Restore manifest signatures do not match "
2651 + "installed application for " + info.packageName);
Christopher Tate75a99702011-05-18 16:28:19 -07002652 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002653 } else {
2654 if (DEBUG) Slog.i(TAG, "Restore manifest from "
2655 + info.packageName + " but allowBackup=false");
Christopher Tate75a99702011-05-18 16:28:19 -07002656 }
2657 } catch (NameNotFoundException e) {
2658 // Okay, the target app isn't installed. We can process
2659 // the restore properly only if the dataset provides the
2660 // apk file and we can successfully install it.
2661 if (DEBUG) Slog.i(TAG, "Package " + info.packageName
2662 + " not installed; requiring apk in dataset");
2663 policy = RestorePolicy.ACCEPT_IF_APK;
2664 }
2665
2666 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
2667 Slog.i(TAG, "Cannot restore package " + info.packageName
2668 + " without the matching .apk");
2669 }
2670 } else {
2671 Slog.i(TAG, "Missing signature on backed-up package "
2672 + info.packageName);
2673 }
2674 } else {
2675 Slog.i(TAG, "Expected package " + info.packageName
2676 + " but restore manifest claims " + manifestPackage);
2677 }
2678 } else {
2679 Slog.i(TAG, "Unknown restore manifest version " + version
2680 + " for package " + info.packageName);
2681 }
2682 } catch (NumberFormatException e) {
2683 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
2684 }
2685
2686 return policy;
2687 }
2688
2689 // Builds a line from a byte buffer starting at 'offset', and returns
2690 // the index of the next unconsumed data in the buffer.
2691 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
2692 final int end = buffer.length;
2693 if (offset >= end) throw new IOException("Incomplete data");
2694
2695 int pos;
2696 for (pos = offset; pos < end; pos++) {
2697 byte c = buffer[pos];
2698 // at LF we declare end of line, and return the next char as the
2699 // starting point for the next time through
2700 if (c == '\n') {
2701 break;
2702 }
2703 }
2704 outStr[0] = new String(buffer, offset, pos - offset);
2705 pos++; // may be pointing an extra byte past the end but that's okay
2706 return pos;
2707 }
2708
2709 void dumpFileMetadata(FileMetadata info) {
2710 if (DEBUG) {
2711 StringBuilder b = new StringBuilder(128);
2712
2713 // mode string
Christopher Tate79ec80d2011-06-24 14:58:49 -07002714 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
Christopher Tate75a99702011-05-18 16:28:19 -07002715 b.append(((info.mode & 0400) != 0) ? 'r' : '-');
2716 b.append(((info.mode & 0200) != 0) ? 'w' : '-');
2717 b.append(((info.mode & 0100) != 0) ? 'x' : '-');
2718 b.append(((info.mode & 0040) != 0) ? 'r' : '-');
2719 b.append(((info.mode & 0020) != 0) ? 'w' : '-');
2720 b.append(((info.mode & 0010) != 0) ? 'x' : '-');
2721 b.append(((info.mode & 0004) != 0) ? 'r' : '-');
2722 b.append(((info.mode & 0002) != 0) ? 'w' : '-');
2723 b.append(((info.mode & 0001) != 0) ? 'x' : '-');
2724 b.append(String.format(" %9d ", info.size));
2725
2726 Date stamp = new Date(info.mtime);
2727 b.append(new SimpleDateFormat("MMM dd kk:mm:ss ").format(stamp));
2728
2729 b.append(info.packageName);
2730 b.append(" :: ");
2731 b.append(info.domain);
2732 b.append(" :: ");
2733 b.append(info.path);
2734
2735 Slog.i(TAG, b.toString());
2736 }
2737 }
2738 // Consume a tar file header block [sequence] and accumulate the relevant metadata
2739 FileMetadata readTarHeaders(InputStream instream) throws IOException {
2740 byte[] block = new byte[512];
2741 FileMetadata info = null;
2742
2743 boolean gotHeader = readTarHeader(instream, block);
2744 if (gotHeader) {
2745 // okay, presume we're okay, and extract the various metadata
2746 info = new FileMetadata();
2747 info.size = extractRadix(block, 124, 12, 8);
2748 info.mtime = extractRadix(block, 136, 12, 8);
2749 info.mode = extractRadix(block, 100, 8, 8);
2750
2751 info.path = extractString(block, 345, 155); // prefix
2752 String path = extractString(block, 0, 100);
2753 if (path.length() > 0) {
2754 if (info.path.length() > 0) info.path += '/';
2755 info.path += path;
2756 }
2757
2758 // tar link indicator field: 1 byte at offset 156 in the header.
2759 int typeChar = block[156];
2760 if (typeChar == 'x') {
2761 // pax extended header, so we need to read that
2762 gotHeader = readPaxExtendedHeader(instream, info);
2763 if (gotHeader) {
2764 // and after a pax extended header comes another real header -- read
2765 // that to find the real file type
2766 gotHeader = readTarHeader(instream, block);
2767 }
2768 if (!gotHeader) throw new IOException("Bad or missing pax header");
2769
2770 typeChar = block[156];
2771 }
2772
2773 switch (typeChar) {
Christopher Tate79ec80d2011-06-24 14:58:49 -07002774 case '0': info.type = BackupAgent.TYPE_FILE; break;
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002775 case '5': {
Christopher Tate79ec80d2011-06-24 14:58:49 -07002776 info.type = BackupAgent.TYPE_DIRECTORY;
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002777 if (info.size != 0) {
2778 Slog.w(TAG, "Directory entry with nonzero size in header");
2779 info.size = 0;
2780 }
2781 break;
2782 }
Christopher Tate75a99702011-05-18 16:28:19 -07002783 case 0: {
2784 // presume EOF
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002785 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
Christopher Tate75a99702011-05-18 16:28:19 -07002786 return null;
2787 }
2788 default: {
2789 Slog.e(TAG, "Unknown tar entity type: " + typeChar);
2790 throw new IOException("Unknown entity type " + typeChar);
2791 }
2792 }
2793
2794 // Parse out the path
2795 //
2796 // first: apps/shared/unrecognized
2797 if (FullBackup.SHARED_PREFIX.regionMatches(0,
2798 info.path, 0, FullBackup.SHARED_PREFIX.length())) {
2799 // File in shared storage. !!! TODO: implement this.
2800 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
Christopher Tateb0628bf2011-06-02 15:08:13 -07002801 info.packageName = "com.android.sharedstoragebackup";
Christopher Tate75a99702011-05-18 16:28:19 -07002802 info.domain = FullBackup.SHARED_STORAGE_TOKEN;
Christopher Tateb0628bf2011-06-02 15:08:13 -07002803 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
Christopher Tate75a99702011-05-18 16:28:19 -07002804 } else if (FullBackup.APPS_PREFIX.regionMatches(0,
2805 info.path, 0, FullBackup.APPS_PREFIX.length())) {
2806 // App content! Parse out the package name and domain
2807
2808 // strip the apps/ prefix
2809 info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
2810
2811 // extract the package name
2812 int slash = info.path.indexOf('/');
2813 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
2814 info.packageName = info.path.substring(0, slash);
2815 info.path = info.path.substring(slash+1);
2816
2817 // if it's a manifest we're done, otherwise parse out the domains
2818 if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2819 slash = info.path.indexOf('/');
2820 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
2821 info.domain = info.path.substring(0, slash);
2822 // validate that it's one of the domains we understand
2823 if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
2824 && !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
2825 && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
2826 && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
2827 && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
2828 && !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
2829 && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
2830 throw new IOException("Unrecognized domain " + info.domain);
2831 }
2832
2833 info.path = info.path.substring(slash + 1);
2834 }
2835 }
2836 }
2837 return info;
2838 }
2839
2840 boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
2841 int nRead = instream.read(block, 0, 512);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002842 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002843 if (nRead > 0 && nRead != 512) {
2844 // if we read only a partial block, then things are
2845 // clearly screwed up. terminate the restore.
2846 throw new IOException("Partial header block: " + nRead);
2847 }
2848 return (nRead > 0);
2849 }
2850
2851 // overwrites 'info' fields based on the pax extended header
2852 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
2853 throws IOException {
2854 // We should never see a pax extended header larger than this
2855 if (info.size > 32*1024) {
2856 Slog.w(TAG, "Suspiciously large pax header size " + info.size
2857 + " - aborting");
2858 throw new IOException("Sanity failure: pax header size " + info.size);
2859 }
2860
2861 // read whole blocks, not just the content size
2862 int numBlocks = (int)((info.size + 511) >> 9);
2863 byte[] data = new byte[numBlocks * 512];
2864 int nRead = instream.read(data);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002865 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002866 if (nRead != data.length) {
2867 return false;
2868 }
2869
2870 final int contentSize = (int) info.size;
2871 int offset = 0;
2872 do {
2873 // extract the line at 'offset'
2874 int eol = offset+1;
2875 while (eol < contentSize && data[eol] != ' ') eol++;
2876 if (eol >= contentSize) {
2877 // error: we just hit EOD looking for the end of the size field
2878 throw new IOException("Invalid pax data");
2879 }
2880 // eol points to the space between the count and the key
2881 int linelen = (int) extractRadix(data, offset, eol - offset, 10);
2882 int key = eol + 1; // start of key=value
2883 eol = offset + linelen - 1; // trailing LF
2884 int value;
2885 for (value = key+1; data[value] != '=' && value <= eol; value++);
2886 if (value > eol) {
2887 throw new IOException("Invalid pax declaration");
2888 }
2889
2890 // pax requires that key/value strings be in UTF-8
2891 String keyStr = new String(data, key, value-key, "UTF-8");
2892 // -1 to strip the trailing LF
2893 String valStr = new String(data, value+1, eol-value-1, "UTF-8");
2894
2895 if ("path".equals(keyStr)) {
2896 info.path = valStr;
2897 } else if ("size".equals(keyStr)) {
2898 info.size = Long.parseLong(valStr);
2899 } else {
2900 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
2901 }
2902
2903 offset += linelen;
2904 } while (offset < contentSize);
2905
2906 return true;
2907 }
2908
2909 long extractRadix(byte[] data, int offset, int maxChars, int radix)
2910 throws IOException {
2911 long value = 0;
2912 final int end = offset + maxChars;
2913 for (int i = offset; i < end; i++) {
2914 final byte b = data[i];
Christopher Tate3f6c77b2011-06-07 13:17:17 -07002915 // Numeric fields in tar can terminate with either NUL or SPC
Christopher Tate75a99702011-05-18 16:28:19 -07002916 if (b == 0 || b == ' ') break;
2917 if (b < '0' || b > ('0' + radix - 1)) {
2918 throw new IOException("Invalid number in header");
2919 }
2920 value = radix * value + (b - '0');
2921 }
2922 return value;
2923 }
2924
2925 String extractString(byte[] data, int offset, int maxChars) throws IOException {
2926 final int end = offset + maxChars;
2927 int eos = offset;
Christopher Tate3f6c77b2011-06-07 13:17:17 -07002928 // tar string fields terminate early with a NUL
2929 while (eos < end && data[eos] != 0) eos++;
Christopher Tate75a99702011-05-18 16:28:19 -07002930 return new String(data, offset, eos-offset, "US-ASCII");
2931 }
2932
2933 void sendStartRestore() {
2934 if (mObserver != null) {
2935 try {
2936 mObserver.onStartRestore();
2937 } catch (RemoteException e) {
2938 Slog.w(TAG, "full restore observer went away: startRestore");
2939 mObserver = null;
2940 }
2941 }
2942 }
2943
2944 void sendOnRestorePackage(String name) {
2945 if (mObserver != null) {
2946 try {
2947 // TODO: use a more user-friendly name string
2948 mObserver.onRestorePackage(name);
2949 } catch (RemoteException e) {
2950 Slog.w(TAG, "full restore observer went away: restorePackage");
2951 mObserver = null;
2952 }
2953 }
2954 }
2955
2956 void sendEndRestore() {
2957 if (mObserver != null) {
2958 try {
2959 mObserver.onEndRestore();
2960 } catch (RemoteException e) {
2961 Slog.w(TAG, "full restore observer went away: endRestore");
2962 mObserver = null;
2963 }
2964 }
2965 }
2966 }
2967
Christopher Tatedf01dea2009-06-09 20:45:02 -07002968 // ----- Restore handling -----
2969
Christopher Tate78dd4a72009-11-04 11:49:08 -08002970 private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
2971 // If the target resides on the system partition, we allow it to restore
2972 // data from the like-named package in a restore set even if the signatures
2973 // do not match. (Unlike general applications, those flashed to the system
2974 // partition will be signed with the device's platform certificate, so on
2975 // different phones the same system app will have different signatures.)
2976 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002977 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
Christopher Tate78dd4a72009-11-04 11:49:08 -08002978 return true;
2979 }
2980
Christopher Tate20efdf6b2009-06-18 19:41:36 -07002981 // Allow unsigned apps, but not signed on one device and unsigned on the other
2982 // !!! TODO: is this the right policy?
Christopher Tate78dd4a72009-11-04 11:49:08 -08002983 Signature[] deviceSigs = target.signatures;
Joe Onorato8a9b2202010-02-26 18:56:32 -08002984 if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
Christopher Tate6aa41f42009-06-19 14:14:22 -07002985 + " device=" + deviceSigs);
Christopher Tate20efdf6b2009-06-18 19:41:36 -07002986 if ((storedSigs == null || storedSigs.length == 0)
2987 && (deviceSigs == null || deviceSigs.length == 0)) {
2988 return true;
2989 }
2990 if (storedSigs == null || deviceSigs == null) {
2991 return false;
2992 }
2993
Christopher Tateabce4e82009-06-18 18:35:32 -07002994 // !!! TODO: this demands that every stored signature match one
2995 // that is present on device, and does not demand the converse.
2996 // Is this this right policy?
2997 int nStored = storedSigs.length;
2998 int nDevice = deviceSigs.length;
2999
3000 for (int i=0; i < nStored; i++) {
3001 boolean match = false;
3002 for (int j=0; j < nDevice; j++) {
3003 if (storedSigs[i].equals(deviceSigs[j])) {
3004 match = true;
3005 break;
3006 }
3007 }
3008 if (!match) {
3009 return false;
3010 }
3011 }
3012 return true;
3013 }
3014
Christopher Tate44a27902010-01-27 17:15:49 -08003015 class PerformRestoreTask implements Runnable {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003016 private IBackupTransport mTransport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07003017 private IRestoreObserver mObserver;
Dan Egnor156411d2009-06-26 13:20:02 -07003018 private long mToken;
Christopher Tate84725812010-02-04 15:52:40 -08003019 private PackageInfo mTargetPackage;
Christopher Tate5cb400b2009-06-25 16:03:14 -07003020 private File mStateDir;
Christopher Tate1bb69062010-02-19 17:02:12 -08003021 private int mPmToken;
Chris Tate249345b2010-10-29 12:57:04 -07003022 private boolean mNeedFullBackup;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003023
Christopher Tate5cbbf562009-06-22 16:44:51 -07003024 class RestoreRequest {
3025 public PackageInfo app;
3026 public int storedAppVersion;
3027
3028 RestoreRequest(PackageInfo _app, int _version) {
3029 app = _app;
3030 storedAppVersion = _version;
3031 }
3032 }
3033
Christopher Tate44a27902010-01-27 17:15:49 -08003034 PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
Chris Tate249345b2010-10-29 12:57:04 -07003035 long restoreSetToken, PackageInfo targetPackage, int pmToken,
3036 boolean needFullBackup) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003037 mTransport = transport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07003038 mObserver = observer;
Christopher Tate9bbc21a2009-06-10 20:23:25 -07003039 mToken = restoreSetToken;
Christopher Tate84725812010-02-04 15:52:40 -08003040 mTargetPackage = targetPackage;
Christopher Tate1bb69062010-02-19 17:02:12 -08003041 mPmToken = pmToken;
Chris Tate249345b2010-10-29 12:57:04 -07003042 mNeedFullBackup = needFullBackup;
Christopher Tate5cb400b2009-06-25 16:03:14 -07003043
3044 try {
3045 mStateDir = new File(mBaseStateDir, transport.transportDirName());
3046 } catch (RemoteException e) {
3047 // can't happen; the transport is local
3048 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003049 }
3050
Christopher Tatedf01dea2009-06-09 20:45:02 -07003051 public void run() {
Dan Egnorbb9001c2009-07-27 12:20:13 -07003052 long startRealtime = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08003053 if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport
Christopher Tate84725812010-02-04 15:52:40 -08003054 + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
Christopher Tate1bb69062010-02-19 17:02:12 -08003055 + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken);
Christopher Tatedf01dea2009-06-09 20:45:02 -07003056
Christopher Tateb49ceb32010-02-08 16:22:24 -08003057 PackageManagerBackupAgent pmAgent = null;
Christopher Tate7d562ec2009-06-25 18:03:43 -07003058 int error = -1; // assume error
3059
Dan Egnorefe52642009-06-24 00:16:33 -07003060 // build the set of apps to restore
Christopher Tatedf01dea2009-06-09 20:45:02 -07003061 try {
Dan Egnorbb9001c2009-07-27 12:20:13 -07003062 // TODO: Log this before getAvailableRestoreSets, somehow
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003063 EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken);
Christopher Tateabce4e82009-06-18 18:35:32 -07003064
Dan Egnorefe52642009-06-24 00:16:33 -07003065 // Get the list of all packages which have backup enabled.
3066 // (Include the Package Manager metadata pseudo-package first.)
3067 ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
3068 PackageInfo omPackage = new PackageInfo();
3069 omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
3070 restorePackages.add(omPackage);
Christopher Tatedf01dea2009-06-09 20:45:02 -07003071
Dan Egnorefe52642009-06-24 00:16:33 -07003072 List<PackageInfo> agentPackages = allAgentPackages();
Christopher Tate84725812010-02-04 15:52:40 -08003073 if (mTargetPackage == null) {
3074 restorePackages.addAll(agentPackages);
3075 } else {
3076 // Just one package to attempt restore of
3077 restorePackages.add(mTargetPackage);
3078 }
Dan Egnorefe52642009-06-24 00:16:33 -07003079
Christopher Tate7d562ec2009-06-25 18:03:43 -07003080 // let the observer know that we're running
3081 if (mObserver != null) {
3082 try {
3083 // !!! TODO: get an actual count from the transport after
3084 // its startRestore() runs?
3085 mObserver.restoreStarting(restorePackages.size());
3086 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003087 Slog.d(TAG, "Restore observer died at restoreStarting");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003088 mObserver = null;
3089 }
3090 }
3091
Dan Egnor01445162009-09-21 17:04:05 -07003092 if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) !=
3093 BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003094 Slog.e(TAG, "Error starting restore operation");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003095 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003096 return;
3097 }
3098
3099 String packageName = mTransport.nextRestorePackage();
3100 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003101 Slog.e(TAG, "Error getting first restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003102 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003103 return;
3104 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003105 Slog.i(TAG, "No restore data available");
Dan Egnorbb9001c2009-07-27 12:20:13 -07003106 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003107 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
Dan Egnorefe52642009-06-24 00:16:33 -07003108 return;
3109 } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003110 Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
Dan Egnorefe52642009-06-24 00:16:33 -07003111 + "\", found only \"" + packageName + "\"");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003112 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003113 "Package manager data missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003114 return;
3115 }
3116
3117 // Pull the Package Manager metadata from the restore set first
Christopher Tateb49ceb32010-02-08 16:22:24 -08003118 pmAgent = new PackageManagerBackupAgent(
Dan Egnorefe52642009-06-24 00:16:33 -07003119 mPackageManager, agentPackages);
Chris Tate249345b2010-10-29 12:57:04 -07003120 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()),
3121 mNeedFullBackup);
Dan Egnorefe52642009-06-24 00:16:33 -07003122
Christopher Tate8c032472009-07-02 14:28:47 -07003123 // Verify that the backup set includes metadata. If not, we can't do
3124 // signature/version verification etc, so we simply do not proceed with
3125 // the restore operation.
Christopher Tate3d7cd132009-07-07 14:23:07 -07003126 if (!pmAgent.hasMetadata()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003127 Slog.e(TAG, "No restore metadata available, so not restoring settings");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003128 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003129 "Package manager restore metadata missing");
Christopher Tate8c032472009-07-02 14:28:47 -07003130 return;
3131 }
3132
Christopher Tate7d562ec2009-06-25 18:03:43 -07003133 int count = 0;
Dan Egnorefe52642009-06-24 00:16:33 -07003134 for (;;) {
3135 packageName = mTransport.nextRestorePackage();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003136
Dan Egnorefe52642009-06-24 00:16:33 -07003137 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003138 Slog.e(TAG, "Error getting next restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003139 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003140 return;
3141 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003142 if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
Dan Egnorefe52642009-06-24 00:16:33 -07003143 break;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003144 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003145
Christopher Tate7d562ec2009-06-25 18:03:43 -07003146 if (mObserver != null) {
Christopher Tate7d562ec2009-06-25 18:03:43 -07003147 try {
Christopher Tate9c3cee92010-03-25 16:06:43 -07003148 mObserver.onUpdate(count, packageName);
Christopher Tate7d562ec2009-06-25 18:03:43 -07003149 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003150 Slog.d(TAG, "Restore observer died in onUpdate");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003151 mObserver = null;
3152 }
3153 }
3154
Dan Egnorefe52642009-06-24 00:16:33 -07003155 Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
3156 if (metaInfo == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003157 Slog.e(TAG, "Missing metadata for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003158 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003159 "Package metadata missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003160 continue;
3161 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003162
Dan Egnorbb9001c2009-07-27 12:20:13 -07003163 PackageInfo packageInfo;
3164 try {
3165 int flags = PackageManager.GET_SIGNATURES;
3166 packageInfo = mPackageManager.getPackageInfo(packageName, flags);
3167 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003168 Slog.e(TAG, "Invalid package restoring data", e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003169 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003170 "Package missing on device");
3171 continue;
3172 }
3173
Dan Egnorefe52642009-06-24 00:16:33 -07003174 if (metaInfo.versionCode > packageInfo.versionCode) {
Christopher Tate3dda5182010-02-24 16:06:18 -08003175 // Data is from a "newer" version of the app than we have currently
3176 // installed. If the app has not declared that it is prepared to
3177 // handle this case, we do not attempt the restore.
3178 if ((packageInfo.applicationInfo.flags
3179 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
3180 String message = "Version " + metaInfo.versionCode
3181 + " > installed version " + packageInfo.versionCode;
Joe Onorato8a9b2202010-02-26 18:56:32 -08003182 Slog.w(TAG, "Package " + packageName + ": " + message);
Christopher Tate3dda5182010-02-24 16:06:18 -08003183 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
3184 packageName, message);
3185 continue;
3186 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003187 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
Christopher Tate3dda5182010-02-24 16:06:18 -08003188 + " > installed " + packageInfo.versionCode
3189 + " but restoreAnyVersion");
3190 }
Dan Egnorefe52642009-06-24 00:16:33 -07003191 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003192
Christopher Tate78dd4a72009-11-04 11:49:08 -08003193 if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003194 Slog.w(TAG, "Signature mismatch restoring " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003195 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003196 "Signature mismatch");
Dan Egnorefe52642009-06-24 00:16:33 -07003197 continue;
3198 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003199
Joe Onorato8a9b2202010-02-26 18:56:32 -08003200 if (DEBUG) Slog.v(TAG, "Package " + packageName
Dan Egnorefe52642009-06-24 00:16:33 -07003201 + " restore version [" + metaInfo.versionCode
3202 + "] is compatible with installed version ["
3203 + packageInfo.versionCode + "]");
Christopher Tatec7b31e32009-06-10 15:49:30 -07003204
Christopher Tate3de55bc2010-03-12 17:28:08 -08003205 // Then set up and bind the agent
Dan Egnorefe52642009-06-24 00:16:33 -07003206 IBackupAgent agent = bindToAgentSynchronous(
3207 packageInfo.applicationInfo,
Christopher Tate3de55bc2010-03-12 17:28:08 -08003208 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Dan Egnorefe52642009-06-24 00:16:33 -07003209 if (agent == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003210 Slog.w(TAG, "Can't find backup agent for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003211 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003212 "Restore agent missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003213 continue;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003214 }
3215
Christopher Tate5e1ab332009-09-01 20:32:49 -07003216 // And then finally run the restore on this agent
Dan Egnorefe52642009-06-24 00:16:33 -07003217 try {
Chris Tate249345b2010-10-29 12:57:04 -07003218 processOneRestore(packageInfo, metaInfo.versionCode, agent,
3219 mNeedFullBackup);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003220 ++count;
Dan Egnorefe52642009-06-24 00:16:33 -07003221 } finally {
Christopher Tate5e1ab332009-09-01 20:32:49 -07003222 // unbind and tidy up even on timeout or failure, just in case
Dan Egnorefe52642009-06-24 00:16:33 -07003223 mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
Christopher Tate5e1ab332009-09-01 20:32:49 -07003224
3225 // The agent was probably running with a stub Application object,
3226 // which isn't a valid run mode for the main app logic. Shut
3227 // down the app so that next time it's launched, it gets the
Christopher Tate3dda5182010-02-24 16:06:18 -08003228 // usual full initialization. Note that this is only done for
3229 // full-system restores: when a single app has requested a restore,
3230 // it is explicitly not killed following that operation.
3231 if (mTargetPackage == null && (packageInfo.applicationInfo.flags
Christopher Tate5e1ab332009-09-01 20:32:49 -07003232 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003233 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
Christopher Tate5e1ab332009-09-01 20:32:49 -07003234 + packageInfo.applicationInfo.processName);
3235 mActivityManager.killApplicationProcess(
3236 packageInfo.applicationInfo.processName,
3237 packageInfo.applicationInfo.uid);
3238 }
Dan Egnorefe52642009-06-24 00:16:33 -07003239 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003240 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003241
3242 // if we get this far, report success to the observer
3243 error = 0;
Dan Egnorbb9001c2009-07-27 12:20:13 -07003244 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003245 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, count, millis);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003246 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003247 Slog.e(TAG, "Error in restore thread", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003248 } finally {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003249 if (DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003250
Dan Egnorefe52642009-06-24 00:16:33 -07003251 try {
3252 mTransport.finishRestore();
3253 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003254 Slog.e(TAG, "Error finishing restore", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003255 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003256
3257 if (mObserver != null) {
3258 try {
3259 mObserver.restoreFinished(error);
3260 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003261 Slog.d(TAG, "Restore observer died at restoreFinished");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003262 }
3263 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003264
Christopher Tate84725812010-02-04 15:52:40 -08003265 // If this was a restoreAll operation, record that this was our
Christopher Tateb49ceb32010-02-08 16:22:24 -08003266 // ancestral dataset, as well as the set of apps that are possibly
3267 // restoreable from the dataset
3268 if (mTargetPackage == null && pmAgent != null) {
3269 mAncestralPackages = pmAgent.getRestoredPackages();
Christopher Tate84725812010-02-04 15:52:40 -08003270 mAncestralToken = mToken;
3271 writeRestoreTokens();
3272 }
3273
Christopher Tate1bb69062010-02-19 17:02:12 -08003274 // We must under all circumstances tell the Package Manager to
3275 // proceed with install notifications if it's waiting for us.
3276 if (mPmToken > 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003277 if (DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
Christopher Tate1bb69062010-02-19 17:02:12 -08003278 try {
3279 mPackageManagerBinder.finishPackageInstall(mPmToken);
3280 } catch (RemoteException e) { /* can't happen */ }
3281 }
3282
Christopher Tate73a3cb32010-12-13 18:27:26 -08003283 // Furthermore we need to reset the session timeout clock
3284 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
3285 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
3286 TIMEOUT_RESTORE_INTERVAL);
3287
Christopher Tateb6787f22009-07-02 17:40:45 -07003288 // done; we can finally release the wakelock
3289 mWakelock.release();
Christopher Tatedf01dea2009-06-09 20:45:02 -07003290 }
3291 }
3292
Dan Egnorefe52642009-06-24 00:16:33 -07003293 // Do the guts of a restore of one application, using mTransport.getRestoreData().
Chris Tate249345b2010-10-29 12:57:04 -07003294 void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
3295 boolean needFullBackup) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003296 // !!! TODO: actually run the restore through mTransport
Christopher Tatec7b31e32009-06-10 15:49:30 -07003297 final String packageName = app.packageName;
3298
Joe Onorato8a9b2202010-02-26 18:56:32 -08003299 if (DEBUG) Slog.d(TAG, "processOneRestore packageName=" + packageName);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04003300
Christopher Tatec7b31e32009-06-10 15:49:30 -07003301 // !!! TODO: get the dirs from the transport
3302 File backupDataName = new File(mDataDir, packageName + ".restore");
Dan Egnorbb9001c2009-07-27 12:20:13 -07003303 File newStateName = new File(mStateDir, packageName + ".new");
3304 File savedStateName = new File(mStateDir, packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003305
Dan Egnorbb9001c2009-07-27 12:20:13 -07003306 ParcelFileDescriptor backupData = null;
3307 ParcelFileDescriptor newState = null;
3308
Christopher Tate4a627c72011-04-01 14:43:32 -07003309 final int token = generateToken();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003310 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07003311 // Run the transport's restore pass
Dan Egnorbb9001c2009-07-27 12:20:13 -07003312 backupData = ParcelFileDescriptor.open(backupDataName,
3313 ParcelFileDescriptor.MODE_READ_WRITE |
3314 ParcelFileDescriptor.MODE_CREATE |
3315 ParcelFileDescriptor.MODE_TRUNCATE);
3316
Dan Egnor01445162009-09-21 17:04:05 -07003317 if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003318 Slog.e(TAG, "Error getting restore data for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003319 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003320 return;
Christopher Tatec7b31e32009-06-10 15:49:30 -07003321 }
3322
3323 // Okay, we have the data. Now have the agent do the restore.
Dan Egnorbb9001c2009-07-27 12:20:13 -07003324 backupData.close();
Christopher Tatec7b31e32009-06-10 15:49:30 -07003325 backupData = ParcelFileDescriptor.open(backupDataName,
3326 ParcelFileDescriptor.MODE_READ_ONLY);
3327
Dan Egnorbb9001c2009-07-27 12:20:13 -07003328 newState = ParcelFileDescriptor.open(newStateName,
3329 ParcelFileDescriptor.MODE_READ_WRITE |
3330 ParcelFileDescriptor.MODE_CREATE |
3331 ParcelFileDescriptor.MODE_TRUNCATE);
3332
Christopher Tate44a27902010-01-27 17:15:49 -08003333 // Kick off the restore, checking for hung agents
3334 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL);
3335 agent.doRestore(backupData, appVersionCode, newState, token, mBackupManagerBinder);
3336 boolean success = waitUntilOperationComplete(token);
3337
3338 if (!success) {
3339 throw new RuntimeException("restore timeout");
3340 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003341
3342 // if everything went okay, remember the recorded state now
Christopher Tate90967f42009-09-20 15:28:33 -07003343 //
3344 // !!! TODO: the restored data should be migrated on the server
3345 // side into the current dataset. In that case the new state file
3346 // we just created would reflect the data already extant in the
3347 // backend, so there'd be nothing more to do. Until that happens,
3348 // however, we need to make sure that we record the data to the
3349 // current backend dataset. (Yes, this means shipping the data over
3350 // the wire in both directions. That's bad, but consistency comes
3351 // first, then efficiency.) Once we introduce server-side data
3352 // migration to the newly-restored device's dataset, we will change
3353 // the following from a discard of the newly-written state to the
3354 // "correct" operation of renaming into the canonical state blob.
3355 newStateName.delete(); // TODO: remove; see above comment
3356 //newStateName.renameTo(savedStateName); // TODO: replace with this
3357
Dan Egnorbb9001c2009-07-27 12:20:13 -07003358 int size = (int) backupDataName.length();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003359 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, packageName, size);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003360 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003361 Slog.e(TAG, "Error restoring data for " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003362 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
Dan Egnorbb9001c2009-07-27 12:20:13 -07003363
Christopher Tate96733042009-07-20 14:49:13 -07003364 // If the agent fails restore, it might have put the app's data
3365 // into an incoherent state. For consistency we wipe its data
3366 // again in this case before propagating the exception
Christopher Tate96733042009-07-20 14:49:13 -07003367 clearApplicationDataSynchronous(packageName);
Christopher Tate1531dc82009-07-24 16:37:43 -07003368 } finally {
3369 backupDataName.delete();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003370 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
3371 try { if (newState != null) newState.close(); } catch (IOException e) {}
3372 backupData = newState = null;
Christopher Tate4a627c72011-04-01 14:43:32 -07003373 synchronized (mCurrentOperations) {
3374 mCurrentOperations.delete(token);
3375 }
Chris Tate249345b2010-10-29 12:57:04 -07003376
3377 // If we know a priori that we'll need to perform a full post-restore backup
3378 // pass, clear the new state file data. This means we're discarding work that
3379 // was just done by the app's agent, but this way the agent doesn't need to
3380 // take any special action based on global device state.
3381 if (needFullBackup) {
3382 newStateName.delete();
3383 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003384 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003385 }
3386 }
3387
Christopher Tate44a27902010-01-27 17:15:49 -08003388 class PerformClearTask implements Runnable {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003389 IBackupTransport mTransport;
3390 PackageInfo mPackage;
3391
Christopher Tate44a27902010-01-27 17:15:49 -08003392 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003393 mTransport = transport;
3394 mPackage = packageInfo;
3395 }
3396
Christopher Tateee0e78a2009-07-02 11:17:03 -07003397 public void run() {
3398 try {
3399 // Clear the on-device backup state to ensure a full backup next time
3400 File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
3401 File stateFile = new File(stateDir, mPackage.packageName);
3402 stateFile.delete();
3403
3404 // Tell the transport to remove all the persistent storage for the app
Christopher Tate13f4a642009-09-30 20:06:45 -07003405 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003406 mTransport.clearBackupData(mPackage);
3407 } catch (RemoteException e) {
3408 // can't happen; the transport is local
3409 } finally {
3410 try {
Christopher Tate13f4a642009-09-30 20:06:45 -07003411 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003412 mTransport.finishBackup();
3413 } catch (RemoteException e) {
3414 // can't happen; the transport is local
3415 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003416
3417 // Last but not least, release the cpu
3418 mWakelock.release();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003419 }
3420 }
3421 }
3422
Christopher Tate44a27902010-01-27 17:15:49 -08003423 class PerformInitializeTask implements Runnable {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003424 HashSet<String> mQueue;
3425
Christopher Tate44a27902010-01-27 17:15:49 -08003426 PerformInitializeTask(HashSet<String> transportNames) {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003427 mQueue = transportNames;
3428 }
3429
Christopher Tate4cc86e12009-09-21 19:36:51 -07003430 public void run() {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003431 try {
3432 for (String transportName : mQueue) {
3433 IBackupTransport transport = getTransport(transportName);
3434 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003435 Slog.e(TAG, "Requested init for " + transportName + " but not found");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003436 continue;
3437 }
3438
Joe Onorato8a9b2202010-02-26 18:56:32 -08003439 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003440 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
Dan Egnor726247c2009-09-29 19:12:31 -07003441 long startRealtime = SystemClock.elapsedRealtime();
3442 int status = transport.initializeDevice();
Christopher Tate4cc86e12009-09-21 19:36:51 -07003443
Christopher Tate4cc86e12009-09-21 19:36:51 -07003444 if (status == BackupConstants.TRANSPORT_OK) {
3445 status = transport.finishBackup();
3446 }
3447
3448 // Okay, the wipe really happened. Clean up our local bookkeeping.
3449 if (status == BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003450 Slog.i(TAG, "Device init successful");
Dan Egnor726247c2009-09-29 19:12:31 -07003451 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003452 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07003453 resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003454 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003455 synchronized (mQueueLock) {
3456 recordInitPendingLocked(false, transportName);
3457 }
Dan Egnor726247c2009-09-29 19:12:31 -07003458 } else {
3459 // If this didn't work, requeue this one and try again
3460 // after a suitable interval
Joe Onorato8a9b2202010-02-26 18:56:32 -08003461 Slog.e(TAG, "Transport error in initializeDevice()");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003462 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003463 synchronized (mQueueLock) {
3464 recordInitPendingLocked(true, transportName);
3465 }
3466 // do this via another alarm to make sure of the wakelock states
3467 long delay = transport.requestBackupTime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08003468 if (DEBUG) Slog.w(TAG, "init failed on "
Christopher Tate4cc86e12009-09-21 19:36:51 -07003469 + transportName + " resched in " + delay);
3470 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
3471 System.currentTimeMillis() + delay, mRunInitIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003472 }
Christopher Tate4cc86e12009-09-21 19:36:51 -07003473 }
3474 } catch (RemoteException e) {
3475 // can't happen; the transports are local
3476 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003477 Slog.e(TAG, "Unexpected error performing init", e);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003478 } finally {
Christopher Tatec2af5d32010-02-02 15:18:58 -08003479 // Done; release the wakelock
Christopher Tate4cc86e12009-09-21 19:36:51 -07003480 mWakelock.release();
3481 }
3482 }
3483 }
3484
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003485 private void dataChangedImpl(String packageName) {
3486 HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3487 dataChangedImpl(packageName, targets);
3488 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003489
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003490 private void dataChangedImpl(String packageName, HashSet<ApplicationInfo> targets) {
Christopher Tate487529a2009-04-29 14:03:25 -07003491 // Record that we need a backup pass for the caller. Since multiple callers
3492 // may share a uid, we need to note all candidates within that uid and schedule
3493 // a backup pass for each of them.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003494 EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003495
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003496 if (targets == null) {
3497 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3498 + " uid=" + Binder.getCallingUid());
3499 return;
3500 }
3501
3502 synchronized (mQueueLock) {
3503 // Note that this client has made data changes that need to be backed up
3504 for (ApplicationInfo app : targets) {
3505 // validate the caller-supplied package name against the known set of
3506 // packages associated with this uid
3507 if (app.packageName.equals(packageName)) {
3508 // Add the caller to the set of pending backups. If there is
3509 // one already there, then overwrite it, but no harm done.
Christopher Tate4a627c72011-04-01 14:43:32 -07003510 BackupRequest req = new BackupRequest(app);
Christopher Tatec28083a2010-12-14 16:16:44 -08003511 if (mPendingBackups.put(app.packageName, req) == null) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003512 // Journal this request in case of crash. The put()
3513 // operation returned null when this package was not already
3514 // in the set; we want to avoid touching the disk redundantly.
3515 writeToJournalLocked(packageName);
3516
3517 if (DEBUG) {
3518 int numKeys = mPendingBackups.size();
3519 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
3520 for (BackupRequest b : mPendingBackups.values()) {
3521 Slog.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
3522 }
3523 }
3524 }
3525 }
3526 }
3527 }
3528 }
3529
3530 // Note: packageName is currently unused, but may be in the future
3531 private HashSet<ApplicationInfo> dataChangedTargets(String packageName) {
Christopher Tate63d27002009-06-16 17:16:42 -07003532 // If the caller does not hold the BACKUP permission, it can only request a
3533 // backup of its own data.
Dianne Hackborncf098292009-07-01 19:55:20 -07003534 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tate63d27002009-06-16 17:16:42 -07003535 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003536 synchronized (mBackupParticipants) {
3537 return mBackupParticipants.get(Binder.getCallingUid());
3538 }
3539 }
3540
3541 // a caller with full permission can ask to back up any participating app
3542 // !!! TODO: allow backup of ANY app?
3543 HashSet<ApplicationInfo> targets = new HashSet<ApplicationInfo>();
3544 synchronized (mBackupParticipants) {
Christopher Tate63d27002009-06-16 17:16:42 -07003545 int N = mBackupParticipants.size();
3546 for (int i = 0; i < N; i++) {
3547 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3548 if (s != null) {
3549 targets.addAll(s);
3550 }
3551 }
3552 }
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003553 return targets;
Christopher Tate487529a2009-04-29 14:03:25 -07003554 }
Christopher Tate46758122009-05-06 11:22:00 -07003555
Christopher Tatecde87f42009-06-12 12:55:53 -07003556 private void writeToJournalLocked(String str) {
Dan Egnor852f8e42009-09-30 11:20:45 -07003557 RandomAccessFile out = null;
3558 try {
3559 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
3560 out = new RandomAccessFile(mJournal, "rws");
3561 out.seek(out.length());
3562 out.writeUTF(str);
3563 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003564 Slog.e(TAG, "Can't write " + str + " to backup journal", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07003565 mJournal = null;
3566 } finally {
3567 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tatecde87f42009-06-12 12:55:53 -07003568 }
3569 }
3570
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003571 // ----- IBackupManager binder interface -----
3572
3573 public void dataChanged(final String packageName) {
3574 final HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3575 if (targets == null) {
3576 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3577 + " uid=" + Binder.getCallingUid());
3578 return;
3579 }
3580
3581 mBackupHandler.post(new Runnable() {
3582 public void run() {
3583 dataChangedImpl(packageName, targets);
3584 }
3585 });
3586 }
3587
Christopher Tateee0e78a2009-07-02 11:17:03 -07003588 // Clear the given package's backup data from the current transport
3589 public void clearBackupData(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003590 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003591 PackageInfo info;
3592 try {
3593 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
3594 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003595 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003596 return;
3597 }
3598
3599 // If the caller does not hold the BACKUP permission, it can only request a
3600 // wipe of its own backed-up data.
3601 HashSet<ApplicationInfo> apps;
Christopher Tate4e3e50c2009-07-02 12:14:05 -07003602 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tateee0e78a2009-07-02 11:17:03 -07003603 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
3604 apps = mBackupParticipants.get(Binder.getCallingUid());
3605 } else {
3606 // a caller with full permission can ask to back up any participating app
3607 // !!! TODO: allow data-clear of ANY app?
Joe Onorato8a9b2202010-02-26 18:56:32 -08003608 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003609 apps = new HashSet<ApplicationInfo>();
3610 int N = mBackupParticipants.size();
3611 for (int i = 0; i < N; i++) {
3612 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3613 if (s != null) {
3614 apps.addAll(s);
3615 }
3616 }
3617 }
3618
3619 // now find the given package in the set of candidate apps
3620 for (ApplicationInfo app : apps) {
3621 if (app.packageName.equals(packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003622 if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003623 // found it; fire off the clear request
3624 synchronized (mQueueLock) {
Christopher Tateaa93b042009-08-05 18:21:40 -07003625 long oldId = Binder.clearCallingIdentity();
Christopher Tateb6787f22009-07-02 17:40:45 -07003626 mWakelock.acquire();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003627 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
3628 new ClearParams(getTransport(mCurrentTransport), info));
3629 mBackupHandler.sendMessage(msg);
Christopher Tateaa93b042009-08-05 18:21:40 -07003630 Binder.restoreCallingIdentity(oldId);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003631 }
3632 break;
3633 }
3634 }
3635 }
3636
Christopher Tateace7f092009-06-15 18:07:25 -07003637 // Run a backup pass immediately for any applications that have declared
3638 // that they have pending updates.
Dan Egnor852f8e42009-09-30 11:20:45 -07003639 public void backupNow() {
Joe Onorato5933a492009-07-23 18:24:08 -04003640 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
Christopher Tate043dadc2009-06-02 16:11:00 -07003641
Joe Onorato8a9b2202010-02-26 18:56:32 -08003642 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
Christopher Tate46758122009-05-06 11:22:00 -07003643 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07003644 // Because the alarms we are using can jitter, and we want an *immediate*
3645 // backup pass to happen, we restart the timer beginning with "next time,"
3646 // then manually fire the backup trigger intent ourselves.
3647 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tateb6787f22009-07-02 17:40:45 -07003648 try {
Christopher Tateb6787f22009-07-02 17:40:45 -07003649 mRunBackupIntent.send();
3650 } catch (PendingIntent.CanceledException e) {
3651 // should never happen
Joe Onorato8a9b2202010-02-26 18:56:32 -08003652 Slog.e(TAG, "run-backup intent cancelled!");
Christopher Tateb6787f22009-07-02 17:40:45 -07003653 }
Christopher Tate46758122009-05-06 11:22:00 -07003654 }
3655 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003656
Christopher Tate4a627c72011-04-01 14:43:32 -07003657 // Run a *full* backup pass for the given package, writing the resulting data stream
3658 // to the supplied file descriptor. This method is synchronous and does not return
3659 // to the caller until the backup has been completed.
3660 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
3661 boolean doAllApps, String[] pkgList) {
3662 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3663
3664 // Validate
3665 if (!doAllApps) {
3666 if (!includeShared) {
3667 // If we're backing up shared data (sdcard or equivalent), then we can run
3668 // without any supplied app names. Otherwise, we'd be doing no work, so
3669 // report the error.
3670 if (pkgList == null || pkgList.length == 0) {
3671 throw new IllegalArgumentException(
3672 "Backup requested but neither shared nor any apps named");
3673 }
3674 }
3675 }
3676
3677 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
3678 + " shared=" + includeShared + " all=" + doAllApps
3679 + " pkgs=" + pkgList);
3680
3681 long oldId = Binder.clearCallingIdentity();
3682 try {
3683 FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
3684 doAllApps, pkgList);
3685 final int token = generateToken();
3686 synchronized (mFullConfirmations) {
3687 mFullConfirmations.put(token, params);
3688 }
3689
Christopher Tate75a99702011-05-18 16:28:19 -07003690 // start up the confirmation UI
3691 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
3692 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
3693 Slog.e(TAG, "Unable to launch full backup confirmation");
Christopher Tate4a627c72011-04-01 14:43:32 -07003694 mFullConfirmations.delete(token);
3695 return;
3696 }
Christopher Tate75a99702011-05-18 16:28:19 -07003697
3698 // make sure the screen is lit for the user interaction
Christopher Tate4a627c72011-04-01 14:43:32 -07003699 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3700
3701 // start the confirmation countdown
Christopher Tate75a99702011-05-18 16:28:19 -07003702 startConfirmationTimeout(token, params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003703
3704 // wait for the backup to be performed
3705 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
3706 waitForCompletion(params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003707 } finally {
Christopher Tate4a627c72011-04-01 14:43:32 -07003708 try {
3709 fd.close();
3710 } catch (IOException e) {
3711 // just eat it
3712 }
Christopher Tate75a99702011-05-18 16:28:19 -07003713 Binder.restoreCallingIdentity(oldId);
Christopher Tate4a627c72011-04-01 14:43:32 -07003714 }
Christopher Tate75a99702011-05-18 16:28:19 -07003715 if (DEBUG) Slog.d(TAG, "Full backup done; returning to caller");
3716 }
3717
3718 public void fullRestore(ParcelFileDescriptor fd) {
3719 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3720 Slog.i(TAG, "Beginning full restore...");
3721
3722 long oldId = Binder.clearCallingIdentity();
3723
3724 try {
3725 FullRestoreParams params = new FullRestoreParams(fd);
3726 final int token = generateToken();
3727 synchronized (mFullConfirmations) {
3728 mFullConfirmations.put(token, params);
3729 }
3730
3731 // start up the confirmation UI
3732 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
3733 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
3734 Slog.e(TAG, "Unable to launch full restore confirmation");
3735 mFullConfirmations.delete(token);
3736 return;
3737 }
3738
3739 // make sure the screen is lit for the user interaction
3740 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3741
3742 // start the confirmation countdown
3743 startConfirmationTimeout(token, params);
3744
3745 // wait for the restore to be performed
3746 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
3747 waitForCompletion(params);
3748 } finally {
3749 try {
3750 fd.close();
3751 } catch (IOException e) {
3752 Slog.w(TAG, "Error trying to close fd after full restore: " + e);
3753 }
3754 Binder.restoreCallingIdentity(oldId);
3755 Slog.i(TAG, "Full restore completed");
3756 }
3757 }
3758
3759 boolean startConfirmationUi(int token, String action) {
3760 try {
3761 Intent confIntent = new Intent(action);
3762 confIntent.setClassName("com.android.backupconfirm",
3763 "com.android.backupconfirm.BackupRestoreConfirmation");
3764 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
3765 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3766 mContext.startActivity(confIntent);
3767 } catch (ActivityNotFoundException e) {
3768 return false;
3769 }
3770 return true;
3771 }
3772
3773 void startConfirmationTimeout(int token, FullParams params) {
3774 if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
3775 + TIMEOUT_FULL_CONFIRMATION + " millis");
3776 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
3777 token, 0, params);
3778 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
Christopher Tate4a627c72011-04-01 14:43:32 -07003779 }
3780
3781 void waitForCompletion(FullParams params) {
3782 synchronized (params.latch) {
3783 while (params.latch.get() == false) {
3784 try {
3785 params.latch.wait();
3786 } catch (InterruptedException e) { /* never interrupted */ }
3787 }
3788 }
3789 }
3790
3791 void signalFullBackupRestoreCompletion(FullParams params) {
3792 synchronized (params.latch) {
3793 params.latch.set(true);
3794 params.latch.notifyAll();
3795 }
3796 }
3797
3798 // Confirm that the previously-requested full backup/restore operation can proceed. This
3799 // is used to require a user-facing disclosure about the operation.
3800 public void acknowledgeFullBackupOrRestore(int token, boolean allow,
3801 IFullBackupRestoreObserver observer) {
3802 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
3803 + " allow=" + allow);
3804
3805 // TODO: possibly require not just this signature-only permission, but even
3806 // require that the specific designated confirmation-UI app uid is the caller?
3807 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3808
3809 long oldId = Binder.clearCallingIdentity();
3810 try {
3811
3812 FullParams params;
3813 synchronized (mFullConfirmations) {
3814 params = mFullConfirmations.get(token);
3815 if (params != null) {
3816 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
3817 mFullConfirmations.delete(token);
3818
3819 if (allow) {
3820 params.observer = observer;
3821 final int verb = params instanceof FullBackupParams
Christopher Tate75a99702011-05-18 16:28:19 -07003822 ? MSG_RUN_FULL_BACKUP
Christopher Tate4a627c72011-04-01 14:43:32 -07003823 : MSG_RUN_FULL_RESTORE;
3824
Christopher Tate75a99702011-05-18 16:28:19 -07003825 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
Christopher Tate4a627c72011-04-01 14:43:32 -07003826 mWakelock.acquire();
3827 Message msg = mBackupHandler.obtainMessage(verb, params);
3828 mBackupHandler.sendMessage(msg);
3829 } else {
3830 Slog.w(TAG, "User rejected full backup/restore operation");
3831 // indicate completion without having actually transferred any data
3832 signalFullBackupRestoreCompletion(params);
3833 }
3834 } else {
3835 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
3836 }
3837 }
3838 } finally {
3839 Binder.restoreCallingIdentity(oldId);
3840 }
3841 }
3842
Christopher Tate8031a3d2009-07-06 16:36:05 -07003843 // Enable/disable the backup service
Christopher Tate6ef58a12009-06-29 14:56:28 -07003844 public void setBackupEnabled(boolean enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003845 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4a627c72011-04-01 14:43:32 -07003846 "setBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07003847
Joe Onorato8a9b2202010-02-26 18:56:32 -08003848 Slog.i(TAG, "Backup enabled => " + enable);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003849
Christopher Tate6ef58a12009-06-29 14:56:28 -07003850 boolean wasEnabled = mEnabled;
3851 synchronized (this) {
Dianne Hackborncf098292009-07-01 19:55:20 -07003852 Settings.Secure.putInt(mContext.getContentResolver(),
3853 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
Christopher Tate6ef58a12009-06-29 14:56:28 -07003854 mEnabled = enable;
3855 }
3856
Christopher Tate49401dd2009-07-01 12:34:29 -07003857 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07003858 if (enable && !wasEnabled && mProvisioned) {
Christopher Tate49401dd2009-07-01 12:34:29 -07003859 // if we've just been enabled, start scheduling backup passes
Christopher Tate8031a3d2009-07-06 16:36:05 -07003860 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tate49401dd2009-07-01 12:34:29 -07003861 } else if (!enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003862 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08003863 if (DEBUG) Slog.i(TAG, "Opting out of backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003864
Christopher Tateb6787f22009-07-02 17:40:45 -07003865 mAlarmManager.cancel(mRunBackupIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003866
3867 // This also constitutes an opt-out, so we wipe any data for
3868 // this device from the backend. We start that process with
3869 // an alarm in order to guarantee wakelock states.
3870 if (wasEnabled && mProvisioned) {
3871 // NOTE: we currently flush every registered transport, not just
3872 // the currently-active one.
3873 HashSet<String> allTransports;
3874 synchronized (mTransports) {
3875 allTransports = new HashSet<String>(mTransports.keySet());
3876 }
3877 // build the set of transports for which we are posting an init
3878 for (String transport : allTransports) {
3879 recordInitPendingLocked(true, transport);
3880 }
3881 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
3882 mRunInitIntent);
3883 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07003884 }
3885 }
Christopher Tate49401dd2009-07-01 12:34:29 -07003886 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07003887
Christopher Tatecce9da52010-02-03 15:11:15 -08003888 // Enable/disable automatic restore of app data at install time
3889 public void setAutoRestore(boolean doAutoRestore) {
3890 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3891 "setBackupEnabled");
3892
Joe Onorato8a9b2202010-02-26 18:56:32 -08003893 Slog.i(TAG, "Auto restore => " + doAutoRestore);
Christopher Tatecce9da52010-02-03 15:11:15 -08003894
3895 synchronized (this) {
3896 Settings.Secure.putInt(mContext.getContentResolver(),
3897 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
3898 mAutoRestore = doAutoRestore;
3899 }
3900 }
3901
Christopher Tate8031a3d2009-07-06 16:36:05 -07003902 // Mark the backup service as having been provisioned
3903 public void setBackupProvisioned(boolean available) {
3904 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3905 "setBackupProvisioned");
3906
3907 boolean wasProvisioned = mProvisioned;
3908 synchronized (this) {
3909 Settings.Secure.putInt(mContext.getContentResolver(),
3910 Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
3911 mProvisioned = available;
3912 }
3913
3914 synchronized (mQueueLock) {
3915 if (available && !wasProvisioned && mEnabled) {
3916 // we're now good to go, so start the backup alarms
3917 startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
3918 } else if (!available) {
3919 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08003920 Slog.w(TAG, "Backup service no longer provisioned");
Christopher Tate8031a3d2009-07-06 16:36:05 -07003921 mAlarmManager.cancel(mRunBackupIntent);
3922 }
3923 }
3924 }
3925
3926 private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
Dan Egnorc1c49c02009-10-30 17:35:39 -07003927 // We used to use setInexactRepeating(), but that may be linked to
3928 // backups running at :00 more often than not, creating load spikes.
3929 // Schedule at an exact time for now, and also add a bit of "fuzz".
3930
3931 Random random = new Random();
3932 long when = System.currentTimeMillis() + delayBeforeFirstBackup +
3933 random.nextInt(FUZZ_MILLIS);
3934 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
3935 BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
Christopher Tate55f931a2009-09-29 17:17:34 -07003936 mNextBackupPass = when;
Christopher Tate8031a3d2009-07-06 16:36:05 -07003937 }
3938
Christopher Tate6ef58a12009-06-29 14:56:28 -07003939 // Report whether the backup mechanism is currently enabled
3940 public boolean isBackupEnabled() {
Joe Onorato5933a492009-07-23 18:24:08 -04003941 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07003942 return mEnabled; // no need to synchronize just to read it
3943 }
3944
Christopher Tate91717492009-06-26 21:07:13 -07003945 // Report the name of the currently active transport
3946 public String getCurrentTransport() {
Joe Onorato5933a492009-07-23 18:24:08 -04003947 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4e3e50c2009-07-02 12:14:05 -07003948 "getCurrentTransport");
Joe Onorato8a9b2202010-02-26 18:56:32 -08003949 if (DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -07003950 return mCurrentTransport;
Christopher Tateace7f092009-06-15 18:07:25 -07003951 }
3952
Christopher Tate91717492009-06-26 21:07:13 -07003953 // Report all known, available backup transports
3954 public String[] listAllTransports() {
Christopher Tate34ebd0e2009-07-06 15:44:54 -07003955 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
Christopher Tate043dadc2009-06-02 16:11:00 -07003956
Christopher Tate91717492009-06-26 21:07:13 -07003957 String[] list = null;
3958 ArrayList<String> known = new ArrayList<String>();
3959 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
3960 if (entry.getValue() != null) {
3961 known.add(entry.getKey());
3962 }
3963 }
3964
3965 if (known.size() > 0) {
3966 list = new String[known.size()];
3967 known.toArray(list);
3968 }
3969 return list;
3970 }
3971
3972 // Select which transport to use for the next backup operation. If the given
3973 // name is not one of the available transports, no action is taken and the method
3974 // returns null.
3975 public String selectBackupTransport(String transport) {
Joe Onorato5933a492009-07-23 18:24:08 -04003976 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
Christopher Tate91717492009-06-26 21:07:13 -07003977
3978 synchronized (mTransports) {
3979 String prevTransport = null;
3980 if (mTransports.get(transport) != null) {
3981 prevTransport = mCurrentTransport;
3982 mCurrentTransport = transport;
Dianne Hackborncf098292009-07-01 19:55:20 -07003983 Settings.Secure.putString(mContext.getContentResolver(),
3984 Settings.Secure.BACKUP_TRANSPORT, transport);
Joe Onorato8a9b2202010-02-26 18:56:32 -08003985 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
Christopher Tate91717492009-06-26 21:07:13 -07003986 + " returning " + prevTransport);
3987 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003988 Slog.w(TAG, "Attempt to select unavailable transport " + transport);
Christopher Tate91717492009-06-26 21:07:13 -07003989 }
3990 return prevTransport;
3991 }
Christopher Tate043dadc2009-06-02 16:11:00 -07003992 }
3993
Christopher Tatef5e1c292010-12-08 18:40:26 -08003994 // Supply the configuration Intent for the given transport. If the name is not one
3995 // of the available transports, or if the transport does not supply any configuration
3996 // UI, the method returns null.
3997 public Intent getConfigurationIntent(String transportName) {
3998 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3999 "getConfigurationIntent");
4000
4001 synchronized (mTransports) {
4002 final IBackupTransport transport = mTransports.get(transportName);
4003 if (transport != null) {
4004 try {
4005 final Intent intent = transport.configurationIntent();
4006 if (DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
4007 + intent);
4008 return intent;
4009 } catch (RemoteException e) {
4010 /* fall through to return null */
4011 }
4012 }
4013 }
4014
4015 return null;
4016 }
4017
4018 // Supply the configuration summary string for the given transport. If the name is
4019 // not one of the available transports, or if the transport does not supply any
4020 // summary / destination string, the method can return null.
4021 //
4022 // This string is used VERBATIM as the summary text of the relevant Settings item!
4023 public String getDestinationString(String transportName) {
4024 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4025 "getConfigurationIntent");
4026
4027 synchronized (mTransports) {
4028 final IBackupTransport transport = mTransports.get(transportName);
4029 if (transport != null) {
4030 try {
4031 final String text = transport.currentDestinationString();
4032 if (DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
4033 return text;
4034 } catch (RemoteException e) {
4035 /* fall through to return null */
4036 }
4037 }
4038 }
4039
4040 return null;
4041 }
4042
Christopher Tate043dadc2009-06-02 16:11:00 -07004043 // Callback: a requested backup agent has been instantiated. This should only
4044 // be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07004045 public void agentConnected(String packageName, IBinder agentBinder) {
Christopher Tate043dadc2009-06-02 16:11:00 -07004046 synchronized(mAgentConnectLock) {
4047 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004048 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
Christopher Tate043dadc2009-06-02 16:11:00 -07004049 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
4050 mConnectedAgent = agent;
4051 mConnecting = false;
4052 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004053 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07004054 + " claiming agent connected");
4055 }
4056 mAgentConnectLock.notifyAll();
4057 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004058 }
4059
4060 // Callback: a backup agent has failed to come up, or has unexpectedly quit.
4061 // If the agent failed to come up in the first place, the agentBinder argument
Christopher Tate043dadc2009-06-02 16:11:00 -07004062 // will be null. This should only be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07004063 public void agentDisconnected(String packageName) {
4064 // TODO: handle backup being interrupted
Christopher Tate043dadc2009-06-02 16:11:00 -07004065 synchronized(mAgentConnectLock) {
4066 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
4067 mConnectedAgent = null;
4068 mConnecting = false;
4069 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004070 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07004071 + " claiming agent disconnected");
4072 }
4073 mAgentConnectLock.notifyAll();
4074 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004075 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004076
Christopher Tate1bb69062010-02-19 17:02:12 -08004077 // An application being installed will need a restore pass, then the Package Manager
4078 // will need to be told when the restore is finished.
4079 public void restoreAtInstall(String packageName, int token) {
4080 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004081 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate1bb69062010-02-19 17:02:12 -08004082 + " attemping install-time restore");
4083 return;
4084 }
4085
4086 long restoreSet = getAvailableRestoreToken(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08004087 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
Christopher Tate1bb69062010-02-19 17:02:12 -08004088 + " token=" + Integer.toHexString(token));
4089
Christopher Tatef0872722010-02-25 15:22:48 -08004090 if (mAutoRestore && mProvisioned && restoreSet != 0) {
Christopher Tate1bb69062010-02-19 17:02:12 -08004091 // okay, we're going to attempt a restore of this package from this restore set.
4092 // The eventual message back into the Package Manager to run the post-install
4093 // steps for 'token' will be issued from the restore handling code.
4094
4095 // We can use a synthetic PackageInfo here because:
4096 // 1. We know it's valid, since the Package Manager supplied the name
4097 // 2. Only the packageName field will be used by the restore code
4098 PackageInfo pkg = new PackageInfo();
4099 pkg.packageName = packageName;
4100
4101 mWakelock.acquire();
4102 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
4103 msg.obj = new RestoreParams(getTransport(mCurrentTransport), null,
Chris Tate249345b2010-10-29 12:57:04 -07004104 restoreSet, pkg, token, true);
Christopher Tate1bb69062010-02-19 17:02:12 -08004105 mBackupHandler.sendMessage(msg);
4106 } else {
Christopher Tatef0872722010-02-25 15:22:48 -08004107 // Auto-restore disabled or no way to attempt a restore; just tell the Package
4108 // Manager to proceed with the post-install handling for this package.
Joe Onorato8a9b2202010-02-26 18:56:32 -08004109 if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore");
Christopher Tate1bb69062010-02-19 17:02:12 -08004110 try {
4111 mPackageManagerBinder.finishPackageInstall(token);
4112 } catch (RemoteException e) { /* can't happen */ }
4113 }
4114 }
4115
Christopher Tate8c850b72009-06-07 19:33:20 -07004116 // Hand off a restore session
Chris Tate44ab8452010-11-16 15:10:49 -08004117 public IRestoreSession beginRestoreSession(String packageName, String transport) {
4118 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
4119 + " transport=" + transport);
4120
4121 boolean needPermission = true;
4122 if (transport == null) {
4123 transport = mCurrentTransport;
4124
4125 if (packageName != null) {
4126 PackageInfo app = null;
4127 try {
4128 app = mPackageManager.getPackageInfo(packageName, 0);
4129 } catch (NameNotFoundException nnf) {
4130 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
4131 throw new IllegalArgumentException("Package " + packageName + " not found");
4132 }
4133
4134 if (app.applicationInfo.uid == Binder.getCallingUid()) {
4135 // So: using the current active transport, and the caller has asked
4136 // that its own package will be restored. In this narrow use case
4137 // we do not require the caller to hold the permission.
4138 needPermission = false;
4139 }
4140 }
4141 }
4142
4143 if (needPermission) {
4144 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4145 "beginRestoreSession");
4146 } else {
4147 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
4148 }
Christopher Tatef68eb502009-06-16 11:02:01 -07004149
4150 synchronized(this) {
4151 if (mActiveRestoreSession != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004152 Slog.d(TAG, "Restore session requested but one already active");
Christopher Tatef68eb502009-06-16 11:02:01 -07004153 return null;
4154 }
Chris Tate44ab8452010-11-16 15:10:49 -08004155 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
Christopher Tate73a3cb32010-12-13 18:27:26 -08004156 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
Christopher Tatef68eb502009-06-16 11:02:01 -07004157 }
4158 return mActiveRestoreSession;
Christopher Tate8c850b72009-06-07 19:33:20 -07004159 }
Christopher Tate043dadc2009-06-02 16:11:00 -07004160
Christopher Tate73a3cb32010-12-13 18:27:26 -08004161 void clearRestoreSession(ActiveRestoreSession currentSession) {
4162 synchronized(this) {
4163 if (currentSession != mActiveRestoreSession) {
4164 Slog.e(TAG, "ending non-current restore session");
4165 } else {
4166 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
4167 mActiveRestoreSession = null;
4168 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
4169 }
4170 }
4171 }
4172
Christopher Tate44a27902010-01-27 17:15:49 -08004173 // Note that a currently-active backup agent has notified us that it has
4174 // completed the given outstanding asynchronous backup/restore operation.
4175 public void opComplete(int token) {
4176 synchronized (mCurrentOpLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004177 if (DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
Christopher Tate44a27902010-01-27 17:15:49 -08004178 mCurrentOperations.put(token, OP_ACKNOWLEDGED);
4179 mCurrentOpLock.notifyAll();
4180 }
4181 }
4182
Christopher Tate9b3905c2009-06-08 15:24:01 -07004183 // ----- Restore session -----
4184
Christopher Tate80202c82010-01-25 19:37:47 -08004185 class ActiveRestoreSession extends IRestoreSession.Stub {
Christopher Tatef68eb502009-06-16 11:02:01 -07004186 private static final String TAG = "RestoreSession";
4187
Chris Tate44ab8452010-11-16 15:10:49 -08004188 private String mPackageName;
Christopher Tate9b3905c2009-06-08 15:24:01 -07004189 private IBackupTransport mRestoreTransport = null;
4190 RestoreSet[] mRestoreSets = null;
Christopher Tate73a3cb32010-12-13 18:27:26 -08004191 boolean mEnded = false;
Christopher Tate9b3905c2009-06-08 15:24:01 -07004192
Chris Tate44ab8452010-11-16 15:10:49 -08004193 ActiveRestoreSession(String packageName, String transport) {
4194 mPackageName = packageName;
Christopher Tate91717492009-06-26 21:07:13 -07004195 mRestoreTransport = getTransport(transport);
Christopher Tate9b3905c2009-06-08 15:24:01 -07004196 }
4197
4198 // --- Binder interface ---
Christopher Tate2d449afe2010-03-29 19:14:24 -07004199 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
Joe Onorato5933a492009-07-23 18:24:08 -04004200 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004201 "getAvailableRestoreSets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004202 if (observer == null) {
4203 throw new IllegalArgumentException("Observer must not be null");
4204 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004205
Christopher Tate73a3cb32010-12-13 18:27:26 -08004206 if (mEnded) {
4207 throw new IllegalStateException("Restore session already ended");
4208 }
4209
Christopher Tate1bb69062010-02-19 17:02:12 -08004210 long oldId = Binder.clearCallingIdentity();
Christopher Tatef68eb502009-06-16 11:02:01 -07004211 try {
Christopher Tate43383042009-07-13 15:17:13 -07004212 if (mRestoreTransport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004213 Slog.w(TAG, "Null transport getting restore sets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004214 return -1;
Dan Egnor0084da52009-07-29 12:57:16 -07004215 }
Christopher Tate2d449afe2010-03-29 19:14:24 -07004216 // spin off the transport request to our service thread
4217 mWakelock.acquire();
4218 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
4219 new RestoreGetSetsParams(mRestoreTransport, this, observer));
4220 mBackupHandler.sendMessage(msg);
4221 return 0;
Dan Egnor0084da52009-07-29 12:57:16 -07004222 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004223 Slog.e(TAG, "Error in getAvailableRestoreSets", e);
Christopher Tate2d449afe2010-03-29 19:14:24 -07004224 return -1;
Christopher Tate1bb69062010-02-19 17:02:12 -08004225 } finally {
4226 Binder.restoreCallingIdentity(oldId);
Christopher Tatef68eb502009-06-16 11:02:01 -07004227 }
Christopher Tate9b3905c2009-06-08 15:24:01 -07004228 }
4229
Christopher Tate84725812010-02-04 15:52:40 -08004230 public synchronized int restoreAll(long token, IRestoreObserver observer) {
Dan Egnor0084da52009-07-29 12:57:16 -07004231 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4232 "performRestore");
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004233
Chris Tate44ab8452010-11-16 15:10:49 -08004234 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
Christopher Tatef2c321a2009-08-10 15:43:36 -07004235 + " observer=" + observer);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004236
Christopher Tate73a3cb32010-12-13 18:27:26 -08004237 if (mEnded) {
4238 throw new IllegalStateException("Restore session already ended");
4239 }
4240
Dan Egnor0084da52009-07-29 12:57:16 -07004241 if (mRestoreTransport == null || mRestoreSets == null) {
Chris Tate44ab8452010-11-16 15:10:49 -08004242 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
4243 return -1;
4244 }
4245
4246 if (mPackageName != null) {
4247 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
Dan Egnor0084da52009-07-29 12:57:16 -07004248 return -1;
4249 }
4250
Christopher Tate21ab6a52009-09-24 18:01:46 -07004251 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07004252 for (int i = 0; i < mRestoreSets.length; i++) {
4253 if (token == mRestoreSets[i].token) {
4254 long oldId = Binder.clearCallingIdentity();
Christopher Tate21ab6a52009-09-24 18:01:46 -07004255 mWakelock.acquire();
4256 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004257 msg.obj = new RestoreParams(mRestoreTransport, observer, token, true);
Christopher Tate21ab6a52009-09-24 18:01:46 -07004258 mBackupHandler.sendMessage(msg);
4259 Binder.restoreCallingIdentity(oldId);
4260 return 0;
4261 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004262 }
4263 }
Christopher Tate0e0b4ae2009-08-10 16:13:47 -07004264
Joe Onorato8a9b2202010-02-26 18:56:32 -08004265 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
Christopher Tate9b3905c2009-06-08 15:24:01 -07004266 return -1;
4267 }
4268
Christopher Tate84725812010-02-04 15:52:40 -08004269 public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004270 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
Christopher Tate84725812010-02-04 15:52:40 -08004271
Christopher Tate73a3cb32010-12-13 18:27:26 -08004272 if (mEnded) {
4273 throw new IllegalStateException("Restore session already ended");
4274 }
4275
Chris Tate44ab8452010-11-16 15:10:49 -08004276 if (mPackageName != null) {
4277 if (! mPackageName.equals(packageName)) {
4278 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
4279 + " on session for package " + mPackageName);
4280 return -1;
4281 }
4282 }
4283
Christopher Tate84725812010-02-04 15:52:40 -08004284 PackageInfo app = null;
4285 try {
4286 app = mPackageManager.getPackageInfo(packageName, 0);
4287 } catch (NameNotFoundException nnf) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004288 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004289 return -1;
4290 }
4291
4292 // If the caller is not privileged and is not coming from the target
4293 // app's uid, throw a permission exception back to the caller.
4294 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
4295 Binder.getCallingPid(), Binder.getCallingUid());
4296 if ((perm == PackageManager.PERMISSION_DENIED) &&
4297 (app.applicationInfo.uid != Binder.getCallingUid())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004298 Slog.w(TAG, "restorePackage: bad packageName=" + packageName
Christopher Tate84725812010-02-04 15:52:40 -08004299 + " or calling uid=" + Binder.getCallingUid());
4300 throw new SecurityException("No permission to restore other packages");
4301 }
4302
Christopher Tate7d411a32010-02-26 11:27:08 -08004303 // If the package has no backup agent, we obviously cannot proceed
4304 if (app.applicationInfo.backupAgentName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004305 Slog.w(TAG, "Asked to restore package " + packageName + " with no agent");
Christopher Tate7d411a32010-02-26 11:27:08 -08004306 return -1;
4307 }
4308
Christopher Tate84725812010-02-04 15:52:40 -08004309 // So far so good; we're allowed to try to restore this package. Now
4310 // check whether there is data for it in the current dataset, falling back
4311 // to the ancestral dataset if not.
Christopher Tate1bb69062010-02-19 17:02:12 -08004312 long token = getAvailableRestoreToken(packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004313
4314 // If we didn't come up with a place to look -- no ancestral dataset and
4315 // the app has never been backed up from this device -- there's nothing
4316 // to do but return failure.
4317 if (token == 0) {
Chris Tate44ab8452010-11-16 15:10:49 -08004318 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
Christopher Tate84725812010-02-04 15:52:40 -08004319 return -1;
4320 }
4321
4322 // Ready to go: enqueue the restore request and claim success
4323 long oldId = Binder.clearCallingIdentity();
4324 mWakelock.acquire();
4325 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004326 msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0, false);
Christopher Tate84725812010-02-04 15:52:40 -08004327 mBackupHandler.sendMessage(msg);
4328 Binder.restoreCallingIdentity(oldId);
4329 return 0;
4330 }
4331
Christopher Tate73a3cb32010-12-13 18:27:26 -08004332 // Posted to the handler to tear down a restore session in a cleanly synchronized way
4333 class EndRestoreRunnable implements Runnable {
4334 BackupManagerService mBackupManager;
4335 ActiveRestoreSession mSession;
4336
4337 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
4338 mBackupManager = manager;
4339 mSession = session;
4340 }
4341
4342 public void run() {
4343 // clean up the session's bookkeeping
4344 synchronized (mSession) {
4345 try {
4346 if (mSession.mRestoreTransport != null) {
4347 mSession.mRestoreTransport.finishRestore();
4348 }
4349 } catch (Exception e) {
4350 Slog.e(TAG, "Error in finishRestore", e);
4351 } finally {
4352 mSession.mRestoreTransport = null;
4353 mSession.mEnded = true;
4354 }
4355 }
4356
4357 // clean up the BackupManagerService side of the bookkeeping
4358 // and cancel any pending timeout message
4359 mBackupManager.clearRestoreSession(mSession);
4360 }
4361 }
4362
Dan Egnor0084da52009-07-29 12:57:16 -07004363 public synchronized void endRestoreSession() {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004364 if (DEBUG) Slog.d(TAG, "endRestoreSession");
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004365
Christopher Tate73a3cb32010-12-13 18:27:26 -08004366 if (mEnded) {
4367 throw new IllegalStateException("Restore session already ended");
Dan Egnor0084da52009-07-29 12:57:16 -07004368 }
4369
Christopher Tate73a3cb32010-12-13 18:27:26 -08004370 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
Christopher Tate9b3905c2009-06-08 15:24:01 -07004371 }
4372 }
4373
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004374 @Override
4375 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004376 long identityToken = Binder.clearCallingIdentity();
4377 try {
4378 dumpInternal(pw);
4379 } finally {
4380 Binder.restoreCallingIdentity(identityToken);
4381 }
4382 }
4383
4384 private void dumpInternal(PrintWriter pw) {
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004385 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07004386 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
Christopher Tate55f931a2009-09-29 17:17:34 -07004387 + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
Christopher Tatec2af5d32010-02-02 15:18:58 -08004388 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
Christopher Tateae06ed92010-02-25 17:13:28 -08004389 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
Christopher Tate55f931a2009-09-29 17:17:34 -07004390 pw.println("Last backup pass: " + mLastBackupPass
4391 + " (now = " + System.currentTimeMillis() + ')');
4392 pw.println(" next scheduled: " + mNextBackupPass);
4393
Christopher Tate91717492009-06-26 21:07:13 -07004394 pw.println("Available transports:");
4395 for (String t : listAllTransports()) {
Dan Egnor852f8e42009-09-30 11:20:45 -07004396 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
4397 try {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004398 IBackupTransport transport = getTransport(t);
4399 File dir = new File(mBaseStateDir, transport.transportDirName());
4400 pw.println(" destination: " + transport.currentDestinationString());
4401 pw.println(" intent: " + transport.configurationIntent());
Dan Egnor852f8e42009-09-30 11:20:45 -07004402 for (File f : dir.listFiles()) {
4403 pw.println(" " + f.getName() + " - " + f.length() + " state bytes");
4404 }
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004405 } catch (Exception e) {
4406 Slog.e(TAG, "Error in transport", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07004407 pw.println(" Error: " + e);
4408 }
Christopher Tate91717492009-06-26 21:07:13 -07004409 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004410
4411 pw.println("Pending init: " + mPendingInits.size());
4412 for (String s : mPendingInits) {
4413 pw.println(" " + s);
4414 }
4415
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004416 int N = mBackupParticipants.size();
Christopher Tate55f931a2009-09-29 17:17:34 -07004417 pw.println("Participants:");
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004418 for (int i=0; i<N; i++) {
4419 int uid = mBackupParticipants.keyAt(i);
4420 pw.print(" uid: ");
4421 pw.println(uid);
Christopher Tate181fafa2009-05-14 11:12:14 -07004422 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
4423 for (ApplicationInfo app: participants) {
Christopher Tate55f931a2009-09-29 17:17:34 -07004424 pw.println(" " + app.packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004425 }
4426 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004427
Christopher Tateb49ceb32010-02-08 16:22:24 -08004428 pw.println("Ancestral packages: "
4429 + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
Christopher Tate5923c972010-04-04 17:45:35 -07004430 if (mAncestralPackages != null) {
4431 for (String pkg : mAncestralPackages) {
4432 pw.println(" " + pkg);
4433 }
Christopher Tateb49ceb32010-02-08 16:22:24 -08004434 }
4435
Christopher Tate73e02522009-07-15 14:18:26 -07004436 pw.println("Ever backed up: " + mEverStoredApps.size());
4437 for (String pkg : mEverStoredApps) {
4438 pw.println(" " + pkg);
4439 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004440
4441 pw.println("Pending backup: " + mPendingBackups.size());
Christopher Tate6aa41f42009-06-19 14:14:22 -07004442 for (BackupRequest req : mPendingBackups.values()) {
Christopher Tate6ef58a12009-06-29 14:56:28 -07004443 pw.println(" " + req);
Christopher Tate181fafa2009-05-14 11:12:14 -07004444 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004445 }
4446 }
Christopher Tate487529a2009-04-29 14:03:25 -07004447}