blob: c28732e8d2b4b045cbe5050971137e94aacd3623 [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 Tate4a627c72011-04-01 14:43:32 -070026import android.app.backup.BackupDataOutput;
27import android.app.backup.FullBackup;
Jason parksa3cdaa52011-01-13 14:15:43 -060028import android.app.backup.RestoreSet;
Christopher Tate45281862010-03-05 15:46:30 -080029import android.app.backup.IBackupManager;
Christopher Tate4a627c72011-04-01 14:43:32 -070030import android.app.backup.IFullBackupRestoreObserver;
Christopher Tate45281862010-03-05 15:46:30 -080031import android.app.backup.IRestoreObserver;
32import android.app.backup.IRestoreSession;
Christopher Tate4a627c72011-04-01 14:43:32 -070033import android.content.ActivityNotFoundException;
Christopher Tate3799bc22009-05-06 16:13:56 -070034import android.content.BroadcastReceiver;
Dan Egnor87a02bc2009-06-17 02:30:10 -070035import android.content.ComponentName;
Christopher Tate487529a2009-04-29 14:03:25 -070036import android.content.Context;
37import android.content.Intent;
Christopher Tate3799bc22009-05-06 16:13:56 -070038import android.content.IntentFilter;
Dan Egnor87a02bc2009-06-17 02:30:10 -070039import android.content.ServiceConnection;
Christopher Tate181fafa2009-05-14 11:12:14 -070040import android.content.pm.ApplicationInfo;
Christopher Tatec7b31e32009-06-10 15:49:30 -070041import android.content.pm.IPackageDataObserver;
Christopher Tatea858cb02011-06-03 12:27:51 -070042import android.content.pm.IPackageDeleteObserver;
Christopher Tate75a99702011-05-18 16:28:19 -070043import android.content.pm.IPackageInstallObserver;
Christopher Tate1bb69062010-02-19 17:02:12 -080044import android.content.pm.IPackageManager;
Christopher Tate7b881282009-06-07 13:52:37 -070045import android.content.pm.PackageInfo;
Dan Egnor87a02bc2009-06-17 02:30:10 -070046import android.content.pm.PackageManager;
Jason parks1125d782011-01-12 09:47:26 -060047import android.content.pm.Signature;
Jason parksa3cdaa52011-01-13 14:15:43 -060048import android.content.pm.PackageManager.NameNotFoundException;
Christopher Tate3799bc22009-05-06 16:13:56 -070049import android.net.Uri;
Christopher Tate487529a2009-04-29 14:03:25 -070050import android.os.Binder;
Christopher Tate75a99702011-05-18 16:28:19 -070051import android.os.Build;
Christopher Tate3799bc22009-05-06 16:13:56 -070052import android.os.Bundle;
Christopher Tate22b87872009-05-04 16:41:53 -070053import android.os.Environment;
Christopher Tate487529a2009-04-29 14:03:25 -070054import android.os.Handler;
Christopher Tate44a27902010-01-27 17:15:49 -080055import android.os.HandlerThread;
Christopher Tate487529a2009-04-29 14:03:25 -070056import android.os.IBinder;
Christopher Tate44a27902010-01-27 17:15:49 -080057import android.os.Looper;
Christopher Tate487529a2009-04-29 14:03:25 -070058import android.os.Message;
Christopher Tate22b87872009-05-04 16:41:53 -070059import android.os.ParcelFileDescriptor;
Christopher Tateb6787f22009-07-02 17:40:45 -070060import android.os.PowerManager;
Christopher Tate043dadc2009-06-02 16:11:00 -070061import android.os.Process;
Christopher Tate487529a2009-04-29 14:03:25 -070062import android.os.RemoteException;
Dan Egnorbb9001c2009-07-27 12:20:13 -070063import android.os.SystemClock;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070064import android.os.WorkSource;
Oscar Montemayora8529f62009-11-18 10:14:20 -080065import android.provider.Settings;
Dan Egnorbb9001c2009-07-27 12:20:13 -070066import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080067import android.util.Slog;
Christopher Tate487529a2009-04-29 14:03:25 -070068import android.util.SparseArray;
Christopher Tate44a27902010-01-27 17:15:49 -080069import android.util.SparseIntArray;
Christopher Tate4a627c72011-04-01 14:43:32 -070070import android.util.StringBuilderPrinter;
71
Jason parksa3cdaa52011-01-13 14:15:43 -060072import com.android.internal.backup.BackupConstants;
73import com.android.internal.backup.IBackupTransport;
74import com.android.internal.backup.LocalTransport;
75import com.android.server.PackageManagerBackupAgent.Metadata;
76
Christopher Tatecde87f42009-06-12 12:55:53 -070077import java.io.EOFException;
Christopher Tate22b87872009-05-04 16:41:53 -070078import java.io.File;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070079import java.io.FileDescriptor;
Christopher Tate75a99702011-05-18 16:28:19 -070080import java.io.FileInputStream;
Christopher Tate1168baa2010-02-17 13:03:40 -080081import java.io.FileNotFoundException;
Christopher Tate4cc86e12009-09-21 19:36:51 -070082import java.io.FileOutputStream;
Christopher Tatec7b31e32009-06-10 15:49:30 -070083import java.io.IOException;
Christopher Tate75a99702011-05-18 16:28:19 -070084import java.io.InputStream;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070085import java.io.PrintWriter;
Christopher Tatecde87f42009-06-12 12:55:53 -070086import java.io.RandomAccessFile;
Christopher Tate75a99702011-05-18 16:28:19 -070087import java.text.SimpleDateFormat;
Joe Onorato8ad02812009-05-13 01:41:44 -040088import java.util.ArrayList;
Christopher Tate75a99702011-05-18 16:28:19 -070089import java.util.Date;
Joe Onorato8ad02812009-05-13 01:41:44 -040090import java.util.HashMap;
Christopher Tate487529a2009-04-29 14:03:25 -070091import java.util.HashSet;
92import java.util.List;
Christopher Tate91717492009-06-26 21:07:13 -070093import java.util.Map;
Dan Egnorc1c49c02009-10-30 17:35:39 -070094import java.util.Random;
Christopher Tateb49ceb32010-02-08 16:22:24 -080095import java.util.Set;
Christopher Tate4a627c72011-04-01 14:43:32 -070096import java.util.concurrent.atomic.AtomicBoolean;
Christopher Tate487529a2009-04-29 14:03:25 -070097
98class BackupManagerService extends IBackupManager.Stub {
99 private static final String TAG = "BackupManagerService";
Christopher Tate4a627c72011-04-01 14:43:32 -0700100 private static final boolean DEBUG = true;
101
102 // Name and current contents version of the full-backup manifest file
103 static final String BACKUP_MANIFEST_FILENAME = "_manifest";
104 static final int BACKUP_MANIFEST_VERSION = 1;
Christopher Tateaa088442009-06-16 18:25:46 -0700105
Christopher Tate49401dd2009-07-01 12:34:29 -0700106 // How often we perform a backup pass. Privileged external callers can
107 // trigger an immediate pass.
Christopher Tateb6787f22009-07-02 17:40:45 -0700108 private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
Christopher Tate487529a2009-04-29 14:03:25 -0700109
Dan Egnorc1c49c02009-10-30 17:35:39 -0700110 // Random variation in backup scheduling time to avoid server load spikes
111 private static final int FUZZ_MILLIS = 5 * 60 * 1000;
112
Christopher Tate8031a3d2009-07-06 16:36:05 -0700113 // The amount of time between the initial provisioning of the device and
114 // the first backup pass.
115 private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
116
Christopher Tate45281862010-03-05 15:46:30 -0800117 private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
118 private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
119 private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
Christopher Tate487529a2009-04-29 14:03:25 -0700120 private static final int MSG_RUN_BACKUP = 1;
Christopher Tate043dadc2009-06-02 16:11:00 -0700121 private static final int MSG_RUN_FULL_BACKUP = 2;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700122 private static final int MSG_RUN_RESTORE = 3;
Christopher Tateee0e78a2009-07-02 11:17:03 -0700123 private static final int MSG_RUN_CLEAR = 4;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700124 private static final int MSG_RUN_INITIALIZE = 5;
Christopher Tate2d449afe2010-03-29 19:14:24 -0700125 private static final int MSG_RUN_GET_RESTORE_SETS = 6;
126 private static final int MSG_TIMEOUT = 7;
Christopher Tate73a3cb32010-12-13 18:27:26 -0800127 private static final int MSG_RESTORE_TIMEOUT = 8;
Christopher Tate4a627c72011-04-01 14:43:32 -0700128 private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
129 private static final int MSG_RUN_FULL_RESTORE = 10;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700130
131 // Timeout interval for deciding that a bind or clear-data has taken too long
132 static final long TIMEOUT_INTERVAL = 10 * 1000;
133
Christopher Tate44a27902010-01-27 17:15:49 -0800134 // Timeout intervals for agent backup & restore operations
135 static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
Christopher Tate4a627c72011-04-01 14:43:32 -0700136 static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
Christopher Tateb0628bf2011-06-02 15:08:13 -0700137 static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
Christopher Tate44a27902010-01-27 17:15:49 -0800138 static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
139
Christopher Tate4a627c72011-04-01 14:43:32 -0700140 // User confirmation timeout for a full backup/restore operation
141 static final long TIMEOUT_FULL_CONFIRMATION = 30 * 1000;
142
Christopher Tate487529a2009-04-29 14:03:25 -0700143 private Context mContext;
144 private PackageManager mPackageManager;
Christopher Tate1bb69062010-02-19 17:02:12 -0800145 IPackageManager mPackageManagerBinder;
Christopher Tate6ef58a12009-06-29 14:56:28 -0700146 private IActivityManager mActivityManager;
Christopher Tateb6787f22009-07-02 17:40:45 -0700147 private PowerManager mPowerManager;
148 private AlarmManager mAlarmManager;
Christopher Tate44a27902010-01-27 17:15:49 -0800149 IBackupManager mBackupManagerBinder;
Christopher Tateb6787f22009-07-02 17:40:45 -0700150
Christopher Tate73e02522009-07-15 14:18:26 -0700151 boolean mEnabled; // access to this is synchronized on 'this'
152 boolean mProvisioned;
Christopher Tatecce9da52010-02-03 15:11:15 -0800153 boolean mAutoRestore;
Christopher Tate73e02522009-07-15 14:18:26 -0700154 PowerManager.WakeLock mWakelock;
Christopher Tate44a27902010-01-27 17:15:49 -0800155 HandlerThread mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
156 BackupHandler mBackupHandler;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700157 PendingIntent mRunBackupIntent, mRunInitIntent;
158 BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
Christopher Tate487529a2009-04-29 14:03:25 -0700159 // map UIDs to the set of backup client services within that UID's app set
Christopher Tate73e02522009-07-15 14:18:26 -0700160 final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
Christopher Tate181fafa2009-05-14 11:12:14 -0700161 = new SparseArray<HashSet<ApplicationInfo>>();
Christopher Tate487529a2009-04-29 14:03:25 -0700162 // set of backup services that have pending changes
Christopher Tate73e02522009-07-15 14:18:26 -0700163 class BackupRequest {
Christopher Tate181fafa2009-05-14 11:12:14 -0700164 public ApplicationInfo appInfo;
Christopher Tateaa088442009-06-16 18:25:46 -0700165
Christopher Tate4a627c72011-04-01 14:43:32 -0700166 BackupRequest(ApplicationInfo app) {
Christopher Tate181fafa2009-05-14 11:12:14 -0700167 appInfo = app;
Christopher Tate46758122009-05-06 11:22:00 -0700168 }
Christopher Tate181fafa2009-05-14 11:12:14 -0700169
170 public String toString() {
Christopher Tate4a627c72011-04-01 14:43:32 -0700171 return "BackupRequest{app=" + appInfo + "}";
Christopher Tate181fafa2009-05-14 11:12:14 -0700172 }
Christopher Tate46758122009-05-06 11:22:00 -0700173 }
Christopher Tatec28083a2010-12-14 16:16:44 -0800174 // Backups that we haven't started yet. Keys are package names.
175 HashMap<String,BackupRequest> mPendingBackups
176 = new HashMap<String,BackupRequest>();
Christopher Tate5cb400b2009-06-25 16:03:14 -0700177
178 // Pseudoname that we use for the Package Manager metadata "package"
Christopher Tate73e02522009-07-15 14:18:26 -0700179 static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
Christopher Tate6aa41f42009-06-19 14:14:22 -0700180
181 // locking around the pending-backup management
Christopher Tate73e02522009-07-15 14:18:26 -0700182 final Object mQueueLock = new Object();
Christopher Tate487529a2009-04-29 14:03:25 -0700183
Christopher Tate043dadc2009-06-02 16:11:00 -0700184 // The thread performing the sequence of queued backups binds to each app's agent
185 // in succession. Bind notifications are asynchronously delivered through the
186 // Activity Manager; use this lock object to signal when a requested binding has
187 // completed.
Christopher Tate73e02522009-07-15 14:18:26 -0700188 final Object mAgentConnectLock = new Object();
189 IBackupAgent mConnectedAgent;
190 volatile boolean mConnecting;
Christopher Tate55f931a2009-09-29 17:17:34 -0700191 volatile long mLastBackupPass;
192 volatile long mNextBackupPass;
Christopher Tate043dadc2009-06-02 16:11:00 -0700193
Christopher Tate55f931a2009-09-29 17:17:34 -0700194 // A similar synchronization mechanism around clearing apps' data for restore
Christopher Tate73e02522009-07-15 14:18:26 -0700195 final Object mClearDataLock = new Object();
196 volatile boolean mClearingData;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700197
Christopher Tate91717492009-06-26 21:07:13 -0700198 // Transport bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700199 final HashMap<String,IBackupTransport> mTransports
Christopher Tate91717492009-06-26 21:07:13 -0700200 = new HashMap<String,IBackupTransport>();
Christopher Tate73e02522009-07-15 14:18:26 -0700201 String mCurrentTransport;
202 IBackupTransport mLocalTransport, mGoogleTransport;
Christopher Tate80202c82010-01-25 19:37:47 -0800203 ActiveRestoreSession mActiveRestoreSession;
Christopher Tate043dadc2009-06-02 16:11:00 -0700204
Christopher Tate2d449afe2010-03-29 19:14:24 -0700205 class RestoreGetSetsParams {
206 public IBackupTransport transport;
207 public ActiveRestoreSession session;
208 public IRestoreObserver observer;
209
210 RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
211 IRestoreObserver _observer) {
212 transport = _transport;
213 session = _session;
214 observer = _observer;
215 }
216 }
217
Christopher Tate73e02522009-07-15 14:18:26 -0700218 class RestoreParams {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700219 public IBackupTransport transport;
220 public IRestoreObserver observer;
Dan Egnor156411d2009-06-26 13:20:02 -0700221 public long token;
Christopher Tate84725812010-02-04 15:52:40 -0800222 public PackageInfo pkgInfo;
Christopher Tate1bb69062010-02-19 17:02:12 -0800223 public int pmToken; // in post-install restore, the PM's token for this transaction
Chris Tate249345b2010-10-29 12:57:04 -0700224 public boolean needFullBackup;
Christopher Tate84725812010-02-04 15:52:40 -0800225
226 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
Chris Tate249345b2010-10-29 12:57:04 -0700227 long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
Christopher Tate84725812010-02-04 15:52:40 -0800228 transport = _transport;
229 observer = _obs;
230 token = _token;
231 pkgInfo = _pkg;
Christopher Tate1bb69062010-02-19 17:02:12 -0800232 pmToken = _pmToken;
Chris Tate249345b2010-10-29 12:57:04 -0700233 needFullBackup = _needFullBackup;
Christopher Tate84725812010-02-04 15:52:40 -0800234 }
Christopher Tate7d562ec2009-06-25 18:03:43 -0700235
Chris Tate249345b2010-10-29 12:57:04 -0700236 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
237 boolean _needFullBackup) {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700238 transport = _transport;
239 observer = _obs;
Dan Egnor156411d2009-06-26 13:20:02 -0700240 token = _token;
Christopher Tate84725812010-02-04 15:52:40 -0800241 pkgInfo = null;
Christopher Tate1bb69062010-02-19 17:02:12 -0800242 pmToken = 0;
Chris Tate249345b2010-10-29 12:57:04 -0700243 needFullBackup = _needFullBackup;
Christopher Tate7d562ec2009-06-25 18:03:43 -0700244 }
245 }
246
Christopher Tate73e02522009-07-15 14:18:26 -0700247 class ClearParams {
Christopher Tateee0e78a2009-07-02 11:17:03 -0700248 public IBackupTransport transport;
249 public PackageInfo packageInfo;
250
251 ClearParams(IBackupTransport _transport, PackageInfo _info) {
252 transport = _transport;
253 packageInfo = _info;
254 }
255 }
256
Christopher Tate4a627c72011-04-01 14:43:32 -0700257 class FullParams {
258 public ParcelFileDescriptor fd;
259 public final AtomicBoolean latch;
260 public IFullBackupRestoreObserver observer;
261
262 FullParams() {
263 latch = new AtomicBoolean(false);
264 }
265 }
266
267 class FullBackupParams extends FullParams {
268 public boolean includeApks;
269 public boolean includeShared;
270 public boolean allApps;
271 public String[] packages;
272
273 FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
274 boolean doAllApps, String[] pkgList) {
275 fd = output;
276 includeApks = saveApks;
277 includeShared = saveShared;
278 allApps = doAllApps;
279 packages = pkgList;
280 }
281 }
282
283 class FullRestoreParams extends FullParams {
284 FullRestoreParams(ParcelFileDescriptor input) {
285 fd = input;
286 }
287 }
288
Christopher Tate44a27902010-01-27 17:15:49 -0800289 // Bookkeeping of in-flight operations for timeout etc. purposes. The operation
290 // token is the index of the entry in the pending-operations list.
291 static final int OP_PENDING = 0;
292 static final int OP_ACKNOWLEDGED = 1;
293 static final int OP_TIMEOUT = -1;
294
295 final SparseIntArray mCurrentOperations = new SparseIntArray();
296 final Object mCurrentOpLock = new Object();
297 final Random mTokenGenerator = new Random();
298
Christopher Tate4a627c72011-04-01 14:43:32 -0700299 final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
300
Christopher Tate5cb400b2009-06-25 16:03:14 -0700301 // Where we keep our journal files and other bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700302 File mBaseStateDir;
303 File mDataDir;
304 File mJournalDir;
305 File mJournal;
Christopher Tate73e02522009-07-15 14:18:26 -0700306
Christopher Tate84725812010-02-04 15:52:40 -0800307 // Keep a log of all the apps we've ever backed up, and what the
308 // dataset tokens are for both the current backup dataset and
309 // the ancestral dataset.
Christopher Tate73e02522009-07-15 14:18:26 -0700310 private File mEverStored;
Christopher Tate73e02522009-07-15 14:18:26 -0700311 HashSet<String> mEverStoredApps = new HashSet<String>();
312
Christopher Tateb49ceb32010-02-08 16:22:24 -0800313 static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes
Christopher Tate84725812010-02-04 15:52:40 -0800314 File mTokenFile;
Christopher Tateb49ceb32010-02-08 16:22:24 -0800315 Set<String> mAncestralPackages = null;
Christopher Tate84725812010-02-04 15:52:40 -0800316 long mAncestralToken = 0;
317 long mCurrentToken = 0;
318
Christopher Tate4cc86e12009-09-21 19:36:51 -0700319 // Persistently track the need to do a full init
320 static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
321 HashSet<String> mPendingInits = new HashSet<String>(); // transport names
Christopher Tateaa088442009-06-16 18:25:46 -0700322
Christopher Tate4a627c72011-04-01 14:43:32 -0700323 // Utility: build a new random integer token
324 int generateToken() {
325 int token;
326 do {
327 synchronized (mTokenGenerator) {
328 token = mTokenGenerator.nextInt();
329 }
330 } while (token < 0);
331 return token;
332 }
333
Christopher Tate44a27902010-01-27 17:15:49 -0800334 // ----- Asynchronous backup/restore handler thread -----
335
336 private class BackupHandler extends Handler {
337 public BackupHandler(Looper looper) {
338 super(looper);
339 }
340
341 public void handleMessage(Message msg) {
342
343 switch (msg.what) {
344 case MSG_RUN_BACKUP:
345 {
346 mLastBackupPass = System.currentTimeMillis();
347 mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
348
349 IBackupTransport transport = getTransport(mCurrentTransport);
350 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800351 Slog.v(TAG, "Backup requested but no transport available");
Christopher Tate44a27902010-01-27 17:15:49 -0800352 mWakelock.release();
353 break;
354 }
355
356 // snapshot the pending-backup set and work on that
357 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
Christopher Tatec61da312010-02-05 10:41:27 -0800358 File oldJournal = mJournal;
Christopher Tate44a27902010-01-27 17:15:49 -0800359 synchronized (mQueueLock) {
Christopher Tatec61da312010-02-05 10:41:27 -0800360 // Do we have any work to do? Construct the work queue
361 // then release the synchronization lock to actually run
362 // the backup.
Christopher Tate44a27902010-01-27 17:15:49 -0800363 if (mPendingBackups.size() > 0) {
364 for (BackupRequest b: mPendingBackups.values()) {
365 queue.add(b);
366 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800367 if (DEBUG) Slog.v(TAG, "clearing pending backups");
Christopher Tate44a27902010-01-27 17:15:49 -0800368 mPendingBackups.clear();
369
370 // Start a new backup-queue journal file too
Christopher Tate44a27902010-01-27 17:15:49 -0800371 mJournal = null;
372
Christopher Tate44a27902010-01-27 17:15:49 -0800373 }
374 }
Christopher Tatec61da312010-02-05 10:41:27 -0800375
376 if (queue.size() > 0) {
377 // At this point, we have started a new journal file, and the old
378 // file identity is being passed to the backup processing thread.
379 // When it completes successfully, that old journal file will be
380 // deleted. If we crash prior to that, the old journal is parsed
381 // at next boot and the journaled requests fulfilled.
382 (new PerformBackupTask(transport, queue, oldJournal)).run();
383 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800384 Slog.v(TAG, "Backup requested but nothing pending");
Christopher Tatec61da312010-02-05 10:41:27 -0800385 mWakelock.release();
386 }
Christopher Tate44a27902010-01-27 17:15:49 -0800387 break;
388 }
389
390 case MSG_RUN_FULL_BACKUP:
Christopher Tate4a627c72011-04-01 14:43:32 -0700391 {
392 FullBackupParams params = (FullBackupParams)msg.obj;
393 (new PerformFullBackupTask(params.fd, params.observer, params.includeApks,
394 params.includeShared, params.allApps, params.packages,
395 params.latch)).run();
Christopher Tate44a27902010-01-27 17:15:49 -0800396 break;
Christopher Tate4a627c72011-04-01 14:43:32 -0700397 }
Christopher Tate44a27902010-01-27 17:15:49 -0800398
399 case MSG_RUN_RESTORE:
400 {
401 RestoreParams params = (RestoreParams)msg.obj;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800402 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
Christopher Tate44a27902010-01-27 17:15:49 -0800403 (new PerformRestoreTask(params.transport, params.observer,
Chris Tate249345b2010-10-29 12:57:04 -0700404 params.token, params.pkgInfo, params.pmToken,
405 params.needFullBackup)).run();
Christopher Tate44a27902010-01-27 17:15:49 -0800406 break;
407 }
408
Christopher Tate75a99702011-05-18 16:28:19 -0700409 case MSG_RUN_FULL_RESTORE:
410 {
411 FullRestoreParams params = (FullRestoreParams)msg.obj;
412 (new PerformFullRestoreTask(params.fd, params.observer, params.latch)).run();
413 break;
414 }
415
Christopher Tate44a27902010-01-27 17:15:49 -0800416 case MSG_RUN_CLEAR:
417 {
418 ClearParams params = (ClearParams)msg.obj;
419 (new PerformClearTask(params.transport, params.packageInfo)).run();
420 break;
421 }
422
423 case MSG_RUN_INITIALIZE:
424 {
425 HashSet<String> queue;
426
427 // Snapshot the pending-init queue and work on that
428 synchronized (mQueueLock) {
429 queue = new HashSet<String>(mPendingInits);
430 mPendingInits.clear();
431 }
432
433 (new PerformInitializeTask(queue)).run();
434 break;
435 }
436
Christopher Tate2d449afe2010-03-29 19:14:24 -0700437 case MSG_RUN_GET_RESTORE_SETS:
438 {
439 // Like other async operations, this is entered with the wakelock held
440 RestoreSet[] sets = null;
441 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
442 try {
443 sets = params.transport.getAvailableRestoreSets();
444 // cache the result in the active session
445 synchronized (params.session) {
446 params.session.mRestoreSets = sets;
447 }
448 if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
449 } catch (Exception e) {
450 Slog.e(TAG, "Error from transport getting set list");
451 } finally {
452 if (params.observer != null) {
453 try {
454 params.observer.restoreSetsAvailable(sets);
455 } catch (RemoteException re) {
456 Slog.e(TAG, "Unable to report listing to observer");
457 } catch (Exception e) {
458 Slog.e(TAG, "Restore observer threw", e);
459 }
460 }
461
Christopher Tate2a935092011-03-03 17:30:32 -0800462 // Done: reset the session timeout clock
463 removeMessages(MSG_RESTORE_TIMEOUT);
464 sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
465
Christopher Tate2d449afe2010-03-29 19:14:24 -0700466 mWakelock.release();
467 }
468 break;
469 }
470
Christopher Tate44a27902010-01-27 17:15:49 -0800471 case MSG_TIMEOUT:
472 {
473 synchronized (mCurrentOpLock) {
474 final int token = msg.arg1;
475 int state = mCurrentOperations.get(token, OP_TIMEOUT);
476 if (state == OP_PENDING) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800477 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + token);
Christopher Tate44a27902010-01-27 17:15:49 -0800478 mCurrentOperations.put(token, OP_TIMEOUT);
479 }
480 mCurrentOpLock.notifyAll();
481 }
482 break;
483 }
Christopher Tate73a3cb32010-12-13 18:27:26 -0800484
485 case MSG_RESTORE_TIMEOUT:
486 {
487 synchronized (BackupManagerService.this) {
488 if (mActiveRestoreSession != null) {
489 // Client app left the restore session dangling. We know that it
490 // can't be in the middle of an actual restore operation because
491 // those are executed serially on this same handler thread. Clean
492 // up now.
493 Slog.w(TAG, "Restore session timed out; aborting");
494 post(mActiveRestoreSession.new EndRestoreRunnable(
495 BackupManagerService.this, mActiveRestoreSession));
496 }
497 }
498 }
Christopher Tate4a627c72011-04-01 14:43:32 -0700499
500 case MSG_FULL_CONFIRMATION_TIMEOUT:
501 {
502 synchronized (mFullConfirmations) {
503 FullParams params = mFullConfirmations.get(msg.arg1);
504 if (params != null) {
505 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
506
507 // Release the waiter; timeout == completion
508 signalFullBackupRestoreCompletion(params);
509
510 // Remove the token from the set
511 mFullConfirmations.delete(msg.arg1);
512
513 // Report a timeout to the observer, if any
514 if (params.observer != null) {
515 try {
516 params.observer.onTimeout();
517 } catch (RemoteException e) {
518 /* don't care if the app has gone away */
519 }
520 }
521 } else {
522 Slog.d(TAG, "couldn't find params for token " + msg.arg1);
523 }
524 }
525 break;
526 }
Christopher Tate44a27902010-01-27 17:15:49 -0800527 }
528 }
529 }
530
531 // ----- Main service implementation -----
532
Christopher Tate487529a2009-04-29 14:03:25 -0700533 public BackupManagerService(Context context) {
534 mContext = context;
535 mPackageManager = context.getPackageManager();
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -0700536 mPackageManagerBinder = AppGlobals.getPackageManager();
Christopher Tate181fafa2009-05-14 11:12:14 -0700537 mActivityManager = ActivityManagerNative.getDefault();
Christopher Tate487529a2009-04-29 14:03:25 -0700538
Christopher Tateb6787f22009-07-02 17:40:45 -0700539 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
540 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
541
Christopher Tate44a27902010-01-27 17:15:49 -0800542 mBackupManagerBinder = asInterface(asBinder());
543
544 // spin up the backup/restore handler thread
545 mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
546 mHandlerThread.start();
547 mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
548
Christopher Tate22b87872009-05-04 16:41:53 -0700549 // Set up our bookkeeping
Christopher Tateb6787f22009-07-02 17:40:45 -0700550 boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
Dianne Hackborncf098292009-07-01 19:55:20 -0700551 Settings.Secure.BACKUP_ENABLED, 0) != 0;
Christopher Tate8031a3d2009-07-06 16:36:05 -0700552 mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
Joe Onoratoab9a2a52009-07-27 08:56:39 -0700553 Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
Christopher Tatecce9da52010-02-03 15:11:15 -0800554 mAutoRestore = Settings.Secure.getInt(context.getContentResolver(),
Christopher Tate5035fda2010-02-25 18:01:14 -0800555 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
Oscar Montemayora8529f62009-11-18 10:14:20 -0800556 // If Encrypted file systems is enabled or disabled, this call will return the
557 // correct directory.
Jason parksa3cdaa52011-01-13 14:15:43 -0600558 mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
Oscar Montemayora8529f62009-11-18 10:14:20 -0800559 mBaseStateDir.mkdirs();
Christopher Tatef4172472009-05-05 15:50:03 -0700560 mDataDir = Environment.getDownloadCacheDirectory();
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700561
Christopher Tate4cc86e12009-09-21 19:36:51 -0700562 // Alarm receivers for scheduled backups & initialization operations
Christopher Tateb6787f22009-07-02 17:40:45 -0700563 mRunBackupReceiver = new RunBackupReceiver();
Christopher Tate4cc86e12009-09-21 19:36:51 -0700564 IntentFilter filter = new IntentFilter();
565 filter.addAction(RUN_BACKUP_ACTION);
566 context.registerReceiver(mRunBackupReceiver, filter,
567 android.Manifest.permission.BACKUP, null);
568
569 mRunInitReceiver = new RunInitializeReceiver();
570 filter = new IntentFilter();
571 filter.addAction(RUN_INITIALIZE_ACTION);
572 context.registerReceiver(mRunInitReceiver, filter,
573 android.Manifest.permission.BACKUP, null);
Christopher Tateb6787f22009-07-02 17:40:45 -0700574
575 Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
Christopher Tateb6787f22009-07-02 17:40:45 -0700576 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
577 mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
578
Christopher Tate4cc86e12009-09-21 19:36:51 -0700579 Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
580 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
581 mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
582
Christopher Tatecde87f42009-06-12 12:55:53 -0700583 // Set up the backup-request journaling
Christopher Tate5cb400b2009-06-25 16:03:14 -0700584 mJournalDir = new File(mBaseStateDir, "pending");
585 mJournalDir.mkdirs(); // creates mBaseStateDir along the way
Dan Egnor852f8e42009-09-30 11:20:45 -0700586 mJournal = null; // will be created on first use
Christopher Tatecde87f42009-06-12 12:55:53 -0700587
Christopher Tate73e02522009-07-15 14:18:26 -0700588 // Set up the various sorts of package tracking we do
589 initPackageTracking();
590
Christopher Tateabce4e82009-06-18 18:35:32 -0700591 // Build our mapping of uid to backup client services. This implicitly
592 // schedules a backup pass on the Package Manager metadata the first
593 // time anything needs to be backed up.
Christopher Tate3799bc22009-05-06 16:13:56 -0700594 synchronized (mBackupParticipants) {
595 addPackageParticipantsLocked(null);
Christopher Tate487529a2009-04-29 14:03:25 -0700596 }
597
Dan Egnor87a02bc2009-06-17 02:30:10 -0700598 // Set up our transport options and initialize the default transport
599 // TODO: Have transports register themselves somehow?
600 // TODO: Don't create transports that we don't need to?
Dan Egnor87a02bc2009-06-17 02:30:10 -0700601 mLocalTransport = new LocalTransport(context); // This is actually pretty cheap
Christopher Tate91717492009-06-26 21:07:13 -0700602 ComponentName localName = new ComponentName(context, LocalTransport.class);
603 registerTransport(localName.flattenToShortString(), mLocalTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700604
Christopher Tate91717492009-06-26 21:07:13 -0700605 mGoogleTransport = null;
Dianne Hackborncf098292009-07-01 19:55:20 -0700606 mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
607 Settings.Secure.BACKUP_TRANSPORT);
608 if ("".equals(mCurrentTransport)) {
609 mCurrentTransport = null;
Christopher Tatece0bf062009-07-01 11:43:53 -0700610 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800611 if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -0700612
613 // Attach to the Google backup transport. When this comes up, it will set
614 // itself as the current transport because we explicitly reset mCurrentTransport
615 // to null.
Christopher Tatea32504f2010-04-21 17:58:07 -0700616 ComponentName transportComponent = new ComponentName("com.google.android.backup",
617 "com.google.android.backup.BackupTransportService");
618 try {
619 // If there's something out there that is supposed to be the Google
620 // backup transport, make sure it's legitimately part of the OS build
621 // and not an app lying about its package name.
622 ApplicationInfo info = mPackageManager.getApplicationInfo(
623 transportComponent.getPackageName(), 0);
624 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
625 if (DEBUG) Slog.v(TAG, "Binding to Google transport");
626 Intent intent = new Intent().setComponent(transportComponent);
627 context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
628 } else {
629 Slog.w(TAG, "Possible Google transport spoof: ignoring " + info);
630 }
631 } catch (PackageManager.NameNotFoundException nnf) {
632 // No such package? No binding.
633 if (DEBUG) Slog.v(TAG, "Google transport not present");
634 }
Christopher Tateaa088442009-06-16 18:25:46 -0700635
Christopher Tatecde87f42009-06-12 12:55:53 -0700636 // Now that we know about valid backup participants, parse any
Christopher Tate49401dd2009-07-01 12:34:29 -0700637 // leftover journal files into the pending backup set
Christopher Tatecde87f42009-06-12 12:55:53 -0700638 parseLeftoverJournals();
639
Christopher Tateb6787f22009-07-02 17:40:45 -0700640 // Power management
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700641 mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
Christopher Tateb6787f22009-07-02 17:40:45 -0700642
643 // Start the backup passes going
644 setBackupEnabled(areEnabled);
645 }
646
647 private class RunBackupReceiver extends BroadcastReceiver {
648 public void onReceive(Context context, Intent intent) {
649 if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
Christopher Tateb6787f22009-07-02 17:40:45 -0700650 synchronized (mQueueLock) {
Christopher Tate4cc86e12009-09-21 19:36:51 -0700651 if (mPendingInits.size() > 0) {
652 // If there are pending init operations, we process those
653 // and then settle into the usual periodic backup schedule.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800654 if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700655 try {
656 mAlarmManager.cancel(mRunInitIntent);
657 mRunInitIntent.send();
658 } catch (PendingIntent.CanceledException ce) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800659 Slog.e(TAG, "Run init intent cancelled");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700660 // can't really do more than bail here
661 }
662 } else {
Christopher Tatec2af5d32010-02-02 15:18:58 -0800663 // Don't run backups now if we're disabled or not yet
664 // fully set up.
665 if (mEnabled && mProvisioned) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800666 if (DEBUG) Slog.v(TAG, "Running a backup pass");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700667
668 // Acquire the wakelock and pass it to the backup thread. it will
669 // be released once backup concludes.
670 mWakelock.acquire();
671
672 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
673 mBackupHandler.sendMessage(msg);
674 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800675 Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
Christopher Tate4cc86e12009-09-21 19:36:51 -0700676 }
677 }
678 }
679 }
680 }
681 }
682
683 private class RunInitializeReceiver extends BroadcastReceiver {
684 public void onReceive(Context context, Intent intent) {
685 if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
686 synchronized (mQueueLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800687 if (DEBUG) Slog.v(TAG, "Running a device init");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700688
689 // Acquire the wakelock and pass it to the init thread. it will
690 // be released once init concludes.
Christopher Tateb6787f22009-07-02 17:40:45 -0700691 mWakelock.acquire();
692
Christopher Tate4cc86e12009-09-21 19:36:51 -0700693 Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
Christopher Tateb6787f22009-07-02 17:40:45 -0700694 mBackupHandler.sendMessage(msg);
695 }
696 }
Christopher Tate49401dd2009-07-01 12:34:29 -0700697 }
Christopher Tateb6787f22009-07-02 17:40:45 -0700698 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700699
Christopher Tate73e02522009-07-15 14:18:26 -0700700 private void initPackageTracking() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800701 if (DEBUG) Slog.v(TAG, "Initializing package tracking");
Christopher Tate73e02522009-07-15 14:18:26 -0700702
Christopher Tate84725812010-02-04 15:52:40 -0800703 // Remember our ancestral dataset
704 mTokenFile = new File(mBaseStateDir, "ancestral");
705 try {
706 RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
Christopher Tateb49ceb32010-02-08 16:22:24 -0800707 int version = tf.readInt();
708 if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
709 mAncestralToken = tf.readLong();
710 mCurrentToken = tf.readLong();
711
712 int numPackages = tf.readInt();
713 if (numPackages >= 0) {
714 mAncestralPackages = new HashSet<String>();
715 for (int i = 0; i < numPackages; i++) {
716 String pkgName = tf.readUTF();
717 mAncestralPackages.add(pkgName);
718 }
719 }
720 }
Brad Fitzpatrick725d8f02010-11-15 11:12:42 -0800721 tf.close();
Christopher Tate1168baa2010-02-17 13:03:40 -0800722 } catch (FileNotFoundException fnf) {
723 // Probably innocuous
Joe Onorato8a9b2202010-02-26 18:56:32 -0800724 Slog.v(TAG, "No ancestral data");
Christopher Tate84725812010-02-04 15:52:40 -0800725 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800726 Slog.w(TAG, "Unable to read token file", e);
Christopher Tate84725812010-02-04 15:52:40 -0800727 }
728
Christopher Tatee97e8072009-07-15 16:45:50 -0700729 // Keep a log of what apps we've ever backed up. Because we might have
730 // rebooted in the middle of an operation that was removing something from
731 // this log, we sanity-check its contents here and reconstruct it.
Christopher Tate73e02522009-07-15 14:18:26 -0700732 mEverStored = new File(mBaseStateDir, "processed");
Christopher Tatee97e8072009-07-15 16:45:50 -0700733 File tempProcessedFile = new File(mBaseStateDir, "processed.new");
Christopher Tate73e02522009-07-15 14:18:26 -0700734
Christopher Tatee97e8072009-07-15 16:45:50 -0700735 // If we were in the middle of removing something from the ever-backed-up
736 // file, there might be a transient "processed.new" file still present.
Dan Egnor852f8e42009-09-30 11:20:45 -0700737 // Ignore it -- we'll validate "processed" against the current package set.
Christopher Tatee97e8072009-07-15 16:45:50 -0700738 if (tempProcessedFile.exists()) {
739 tempProcessedFile.delete();
740 }
741
Dan Egnor852f8e42009-09-30 11:20:45 -0700742 // If there are previous contents, parse them out then start a new
743 // file to continue the recordkeeping.
744 if (mEverStored.exists()) {
745 RandomAccessFile temp = null;
746 RandomAccessFile in = null;
747
748 try {
749 temp = new RandomAccessFile(tempProcessedFile, "rws");
750 in = new RandomAccessFile(mEverStored, "r");
751
752 while (true) {
753 PackageInfo info;
754 String pkg = in.readUTF();
755 try {
756 info = mPackageManager.getPackageInfo(pkg, 0);
757 mEverStoredApps.add(pkg);
758 temp.writeUTF(pkg);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800759 if (DEBUG) Slog.v(TAG, " + " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700760 } catch (NameNotFoundException e) {
761 // nope, this package was uninstalled; don't include it
Joe Onorato8a9b2202010-02-26 18:56:32 -0800762 if (DEBUG) Slog.v(TAG, " - " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700763 }
764 }
765 } catch (EOFException e) {
766 // Once we've rewritten the backup history log, atomically replace the
767 // old one with the new one then reopen the file for continuing use.
768 if (!tempProcessedFile.renameTo(mEverStored)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800769 Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -0700770 }
771 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800772 Slog.e(TAG, "Error in processed file", e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700773 } finally {
774 try { if (temp != null) temp.close(); } catch (IOException e) {}
775 try { if (in != null) in.close(); } catch (IOException e) {}
776 }
777 }
778
Christopher Tate73e02522009-07-15 14:18:26 -0700779 // Register for broadcasts about package install, etc., so we can
780 // update the provider list.
781 IntentFilter filter = new IntentFilter();
782 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
783 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
784 filter.addDataScheme("package");
785 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800786 // Register for events related to sdcard installation.
787 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800788 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
789 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800790 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
Christopher Tate73e02522009-07-15 14:18:26 -0700791 }
792
Christopher Tatecde87f42009-06-12 12:55:53 -0700793 private void parseLeftoverJournals() {
Dan Egnor852f8e42009-09-30 11:20:45 -0700794 for (File f : mJournalDir.listFiles()) {
795 if (mJournal == null || f.compareTo(mJournal) != 0) {
796 // This isn't the current journal, so it must be a leftover. Read
797 // out the package names mentioned there and schedule them for
798 // backup.
799 RandomAccessFile in = null;
800 try {
Joe Onorato431bb222010-10-18 19:13:23 -0400801 Slog.i(TAG, "Found stale backup journal, scheduling");
Dan Egnor852f8e42009-09-30 11:20:45 -0700802 in = new RandomAccessFile(f, "r");
803 while (true) {
804 String packageName = in.readUTF();
Joe Onorato431bb222010-10-18 19:13:23 -0400805 Slog.i(TAG, " " + packageName);
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -0700806 dataChangedImpl(packageName);
Christopher Tatecde87f42009-06-12 12:55:53 -0700807 }
Dan Egnor852f8e42009-09-30 11:20:45 -0700808 } catch (EOFException e) {
809 // no more data; we're done
810 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800811 Slog.e(TAG, "Can't read " + f, e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700812 } finally {
813 // close/delete the file
814 try { if (in != null) in.close(); } catch (IOException e) {}
815 f.delete();
Christopher Tatecde87f42009-06-12 12:55:53 -0700816 }
817 }
818 }
819 }
820
Christopher Tate4cc86e12009-09-21 19:36:51 -0700821 // Maintain persistent state around whether need to do an initialize operation.
822 // Must be called with the queue lock held.
823 void recordInitPendingLocked(boolean isPending, String transportName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800824 if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
Christopher Tate4cc86e12009-09-21 19:36:51 -0700825 + " on transport " + transportName);
826 try {
827 IBackupTransport transport = getTransport(transportName);
828 String transportDirName = transport.transportDirName();
829 File stateDir = new File(mBaseStateDir, transportDirName);
830 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
831
832 if (isPending) {
833 // We need an init before we can proceed with sending backup data.
834 // Record that with an entry in our set of pending inits, as well as
835 // journaling it via creation of a sentinel file.
836 mPendingInits.add(transportName);
837 try {
838 (new FileOutputStream(initPendingFile)).close();
839 } catch (IOException ioe) {
840 // Something is badly wrong with our permissions; just try to move on
841 }
842 } else {
843 // No more initialization needed; wipe the journal and reset our state.
844 initPendingFile.delete();
845 mPendingInits.remove(transportName);
846 }
847 } catch (RemoteException e) {
848 // can't happen; the transport is local
849 }
850 }
851
Christopher Tated55e18a2009-09-21 10:12:59 -0700852 // Reset all of our bookkeeping, in response to having been told that
853 // the backend data has been wiped [due to idle expiry, for example],
854 // so we must re-upload all saved settings.
855 void resetBackupState(File stateFileDir) {
856 synchronized (mQueueLock) {
857 // Wipe the "what we've ever backed up" tracking
Christopher Tated55e18a2009-09-21 10:12:59 -0700858 mEverStoredApps.clear();
Dan Egnor852f8e42009-09-30 11:20:45 -0700859 mEverStored.delete();
Christopher Tated55e18a2009-09-21 10:12:59 -0700860
Christopher Tate84725812010-02-04 15:52:40 -0800861 mCurrentToken = 0;
862 writeRestoreTokens();
863
Christopher Tated55e18a2009-09-21 10:12:59 -0700864 // Remove all the state files
865 for (File sf : stateFileDir.listFiles()) {
Christopher Tate4cc86e12009-09-21 19:36:51 -0700866 // ... but don't touch the needs-init sentinel
867 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
868 sf.delete();
869 }
Christopher Tated55e18a2009-09-21 10:12:59 -0700870 }
Christopher Tate45597642011-04-04 16:59:21 -0700871 }
Christopher Tated55e18a2009-09-21 10:12:59 -0700872
Christopher Tate45597642011-04-04 16:59:21 -0700873 // Enqueue a new backup of every participant
874 int N = mBackupParticipants.size();
875 for (int i=0; i<N; i++) {
876 int uid = mBackupParticipants.keyAt(i);
877 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
878 for (ApplicationInfo app: participants) {
879 dataChangedImpl(app.packageName);
Christopher Tated55e18a2009-09-21 10:12:59 -0700880 }
881 }
882 }
883
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800884 // Add a transport to our set of available backends. If 'transport' is null, this
885 // is an unregistration, and the transport's entry is removed from our bookkeeping.
Christopher Tate91717492009-06-26 21:07:13 -0700886 private void registerTransport(String name, IBackupTransport transport) {
887 synchronized (mTransports) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800888 if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport);
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800889 if (transport != null) {
890 mTransports.put(name, transport);
891 } else {
892 mTransports.remove(name);
Christopher Tateb0dcaaf2010-01-29 16:27:04 -0800893 if ((mCurrentTransport != null) && mCurrentTransport.equals(name)) {
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800894 mCurrentTransport = null;
895 }
896 // Nothing further to do in the unregistration case
897 return;
898 }
Christopher Tate91717492009-06-26 21:07:13 -0700899 }
Christopher Tate4cc86e12009-09-21 19:36:51 -0700900
901 // If the init sentinel file exists, we need to be sure to perform the init
902 // as soon as practical. We also create the state directory at registration
903 // time to ensure it's present from the outset.
904 try {
905 String transportName = transport.transportDirName();
906 File stateDir = new File(mBaseStateDir, transportName);
907 stateDir.mkdirs();
908
909 File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
910 if (initSentinel.exists()) {
911 synchronized (mQueueLock) {
912 mPendingInits.add(transportName);
913
914 // TODO: pick a better starting time than now + 1 minute
915 long delay = 1000 * 60; // one minute, in milliseconds
916 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
917 System.currentTimeMillis() + delay, mRunInitIntent);
918 }
919 }
920 } catch (RemoteException e) {
921 // can't happen, the transport is local
922 }
Christopher Tate91717492009-06-26 21:07:13 -0700923 }
924
Christopher Tate3799bc22009-05-06 16:13:56 -0700925 // ----- Track installation/removal of packages -----
926 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
927 public void onReceive(Context context, Intent intent) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800928 if (DEBUG) Slog.d(TAG, "Received broadcast " + intent);
Christopher Tate3799bc22009-05-06 16:13:56 -0700929
Christopher Tate3799bc22009-05-06 16:13:56 -0700930 String action = intent.getAction();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800931 boolean replacing = false;
932 boolean added = false;
933 Bundle extras = intent.getExtras();
934 String pkgList[] = null;
935 if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
936 Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
937 Uri uri = intent.getData();
938 if (uri == null) {
939 return;
940 }
941 String pkgName = uri.getSchemeSpecificPart();
942 if (pkgName != null) {
943 pkgList = new String[] { pkgName };
944 }
945 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
946 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800947 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800948 added = true;
949 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800950 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800951 added = false;
952 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
953 }
954 if (pkgList == null || pkgList.length == 0) {
955 return;
956 }
957 if (added) {
Christopher Tate3799bc22009-05-06 16:13:56 -0700958 synchronized (mBackupParticipants) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800959 for (String pkgName : pkgList) {
960 if (replacing) {
961 // The package was just upgraded
962 updatePackageParticipantsLocked(pkgName);
963 } else {
964 // The package was just added
965 addPackageParticipantsLocked(pkgName);
966 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700967 }
968 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800969 } else {
970 if (replacing) {
Christopher Tate3799bc22009-05-06 16:13:56 -0700971 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
972 } else {
973 synchronized (mBackupParticipants) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800974 for (String pkgName : pkgList) {
975 removePackageParticipantsLocked(pkgName);
976 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700977 }
978 }
979 }
980 }
981 };
982
Dan Egnor87a02bc2009-06-17 02:30:10 -0700983 // ----- Track connection to GoogleBackupTransport service -----
984 ServiceConnection mGoogleConnection = new ServiceConnection() {
985 public void onServiceConnected(ComponentName name, IBinder service) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800986 if (DEBUG) Slog.v(TAG, "Connected to Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -0700987 mGoogleTransport = IBackupTransport.Stub.asInterface(service);
Christopher Tate91717492009-06-26 21:07:13 -0700988 registerTransport(name.flattenToShortString(), mGoogleTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700989 }
990
991 public void onServiceDisconnected(ComponentName name) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800992 if (DEBUG) Slog.v(TAG, "Disconnected from Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -0700993 mGoogleTransport = null;
Christopher Tate91717492009-06-26 21:07:13 -0700994 registerTransport(name.flattenToShortString(), null);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700995 }
996 };
997
Christopher Tate181fafa2009-05-14 11:12:14 -0700998 // Add the backup agents in the given package to our set of known backup participants.
999 // If 'packageName' is null, adds all backup agents in the whole system.
Christopher Tate3799bc22009-05-06 16:13:56 -07001000 void addPackageParticipantsLocked(String packageName) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001001 // Look for apps that define the android:backupAgent attribute
Joe Onorato8a9b2202010-02-26 18:56:32 -08001002 if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: " + packageName);
Dan Egnorefe52642009-06-24 00:16:33 -07001003 List<PackageInfo> targetApps = allAgentPackages();
Christopher Tate181fafa2009-05-14 11:12:14 -07001004 addPackageParticipantsLockedInner(packageName, targetApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001005 }
1006
Christopher Tate181fafa2009-05-14 11:12:14 -07001007 private void addPackageParticipantsLockedInner(String packageName,
Dan Egnorefe52642009-06-24 00:16:33 -07001008 List<PackageInfo> targetPkgs) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001009 if (DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001010 Slog.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
Dan Egnorefe52642009-06-24 00:16:33 -07001011 for (PackageInfo p : targetPkgs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001012 Slog.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName
Christopher Tate5e1ab332009-09-01 20:32:49 -07001013 + " uid=" + p.applicationInfo.uid
1014 + " killAfterRestore="
1015 + (((p.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) ? "true" : "false")
Christopher Tate5e1ab332009-09-01 20:32:49 -07001016 );
Christopher Tate181fafa2009-05-14 11:12:14 -07001017 }
1018 }
1019
Dan Egnorefe52642009-06-24 00:16:33 -07001020 for (PackageInfo pkg : targetPkgs) {
1021 if (packageName == null || pkg.packageName.equals(packageName)) {
1022 int uid = pkg.applicationInfo.uid;
Christopher Tate181fafa2009-05-14 11:12:14 -07001023 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001024 if (set == null) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001025 set = new HashSet<ApplicationInfo>();
Christopher Tate3799bc22009-05-06 16:13:56 -07001026 mBackupParticipants.put(uid, set);
1027 }
Dan Egnorefe52642009-06-24 00:16:33 -07001028 set.add(pkg.applicationInfo);
Christopher Tate73e02522009-07-15 14:18:26 -07001029
1030 // If we've never seen this app before, schedule a backup for it
1031 if (!mEverStoredApps.contains(pkg.packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001032 if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName
Christopher Tate73e02522009-07-15 14:18:26 -07001033 + " never backed up; scheduling");
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07001034 dataChangedImpl(pkg.packageName);
Christopher Tate73e02522009-07-15 14:18:26 -07001035 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001036 }
Christopher Tate487529a2009-04-29 14:03:25 -07001037 }
1038 }
1039
Christopher Tate6785dd82009-06-18 15:58:25 -07001040 // Remove the given package's entry from our known active set. If
1041 // 'packageName' is null, *all* participating apps will be removed.
Christopher Tate3799bc22009-05-06 16:13:56 -07001042 void removePackageParticipantsLocked(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001043 if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: " + packageName);
Christopher Tatec28083a2010-12-14 16:16:44 -08001044 List<String> allApps = new ArrayList<String>();
Christopher Tate181fafa2009-05-14 11:12:14 -07001045 if (packageName != null) {
Christopher Tatec28083a2010-12-14 16:16:44 -08001046 allApps.add(packageName);
Christopher Tate181fafa2009-05-14 11:12:14 -07001047 } else {
1048 // all apps with agents
Christopher Tatec28083a2010-12-14 16:16:44 -08001049 List<PackageInfo> knownPackages = allAgentPackages();
1050 for (PackageInfo pkg : knownPackages) {
1051 allApps.add(pkg.packageName);
1052 }
Christopher Tate181fafa2009-05-14 11:12:14 -07001053 }
1054 removePackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001055 }
1056
Joe Onorato8ad02812009-05-13 01:41:44 -04001057 private void removePackageParticipantsLockedInner(String packageName,
Christopher Tatec28083a2010-12-14 16:16:44 -08001058 List<String> allPackageNames) {
Christopher Tate043dadc2009-06-02 16:11:00 -07001059 if (DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001060 Slog.v(TAG, "removePackageParticipantsLockedInner (" + packageName
Christopher Tatec28083a2010-12-14 16:16:44 -08001061 + ") removing " + allPackageNames.size() + " entries");
1062 for (String p : allPackageNames) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001063 Slog.v(TAG, " - " + p);
Christopher Tate043dadc2009-06-02 16:11:00 -07001064 }
1065 }
Christopher Tatec28083a2010-12-14 16:16:44 -08001066 for (String pkg : allPackageNames) {
1067 if (packageName == null || pkg.equals(packageName)) {
1068 int uid = -1;
1069 try {
1070 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1071 uid = info.applicationInfo.uid;
1072 } catch (NameNotFoundException e) {
1073 // we don't know this package name, so just skip it for now
1074 continue;
1075 }
1076
Christopher Tate181fafa2009-05-14 11:12:14 -07001077 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001078 if (set != null) {
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001079 // Find the existing entry with the same package name, and remove it.
1080 // We can't just remove(app) because the instances are different.
1081 for (ApplicationInfo entry: set) {
Christopher Tatec28083a2010-12-14 16:16:44 -08001082 if (entry.packageName.equals(pkg)) {
1083 if (DEBUG) Slog.v(TAG, " removing participant " + pkg);
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001084 set.remove(entry);
Christopher Tatec28083a2010-12-14 16:16:44 -08001085 removeEverBackedUp(pkg);
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001086 break;
1087 }
1088 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001089 if (set.size() == 0) {
Dan Egnorefe52642009-06-24 00:16:33 -07001090 mBackupParticipants.delete(uid);
1091 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001092 }
1093 }
1094 }
1095 }
1096
Christopher Tate181fafa2009-05-14 11:12:14 -07001097 // Returns the set of all applications that define an android:backupAgent attribute
Christopher Tate73e02522009-07-15 14:18:26 -07001098 List<PackageInfo> allAgentPackages() {
Christopher Tate6785dd82009-06-18 15:58:25 -07001099 // !!! TODO: cache this and regenerate only when necessary
Dan Egnorefe52642009-06-24 00:16:33 -07001100 int flags = PackageManager.GET_SIGNATURES;
1101 List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
1102 int N = packages.size();
1103 for (int a = N-1; a >= 0; a--) {
Christopher Tate0749dcd2009-08-13 15:13:03 -07001104 PackageInfo pkg = packages.get(a);
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001105 try {
1106 ApplicationInfo app = pkg.applicationInfo;
1107 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
Christopher Tatea87240c2010-02-12 14:12:34 -08001108 || app.backupAgentName == null) {
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001109 packages.remove(a);
1110 }
1111 else {
1112 // we will need the shared library path, so look that up and store it here
1113 app = mPackageManager.getApplicationInfo(pkg.packageName,
1114 PackageManager.GET_SHARED_LIBRARY_FILES);
1115 pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
1116 }
1117 } catch (NameNotFoundException e) {
Dan Egnorefe52642009-06-24 00:16:33 -07001118 packages.remove(a);
Christopher Tate181fafa2009-05-14 11:12:14 -07001119 }
1120 }
Dan Egnorefe52642009-06-24 00:16:33 -07001121 return packages;
Christopher Tate181fafa2009-05-14 11:12:14 -07001122 }
Christopher Tateaa088442009-06-16 18:25:46 -07001123
Christopher Tate3799bc22009-05-06 16:13:56 -07001124 // Reset the given package's known backup participants. Unlike add/remove, the update
1125 // action cannot be passed a null package name.
1126 void updatePackageParticipantsLocked(String packageName) {
1127 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001128 Slog.e(TAG, "updatePackageParticipants called with null package name");
Christopher Tate3799bc22009-05-06 16:13:56 -07001129 return;
1130 }
Joe Onorato8a9b2202010-02-26 18:56:32 -08001131 if (DEBUG) Slog.v(TAG, "updatePackageParticipantsLocked: " + packageName);
Christopher Tate3799bc22009-05-06 16:13:56 -07001132
1133 // brute force but small code size
Dan Egnorefe52642009-06-24 00:16:33 -07001134 List<PackageInfo> allApps = allAgentPackages();
Christopher Tatec28083a2010-12-14 16:16:44 -08001135 List<String> allAppNames = new ArrayList<String>();
1136 for (PackageInfo pkg : allApps) {
1137 allAppNames.add(pkg.packageName);
1138 }
1139 removePackageParticipantsLockedInner(packageName, allAppNames);
Christopher Tate181fafa2009-05-14 11:12:14 -07001140 addPackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001141 }
1142
Christopher Tate84725812010-02-04 15:52:40 -08001143 // Called from the backup task: record that the given app has been successfully
Christopher Tate73e02522009-07-15 14:18:26 -07001144 // backed up at least once
1145 void logBackupComplete(String packageName) {
Dan Egnor852f8e42009-09-30 11:20:45 -07001146 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
1147
1148 synchronized (mEverStoredApps) {
1149 if (!mEverStoredApps.add(packageName)) return;
1150
1151 RandomAccessFile out = null;
1152 try {
1153 out = new RandomAccessFile(mEverStored, "rws");
1154 out.seek(out.length());
1155 out.writeUTF(packageName);
1156 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001157 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -07001158 } finally {
1159 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tate73e02522009-07-15 14:18:26 -07001160 }
1161 }
1162 }
1163
Christopher Tatee97e8072009-07-15 16:45:50 -07001164 // Remove our awareness of having ever backed up the given package
1165 void removeEverBackedUp(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001166 if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName + ", new set:");
Christopher Tatee97e8072009-07-15 16:45:50 -07001167
Dan Egnor852f8e42009-09-30 11:20:45 -07001168 synchronized (mEverStoredApps) {
1169 // Rewrite the file and rename to overwrite. If we reboot in the middle,
1170 // we'll recognize on initialization time that the package no longer
1171 // exists and fix it up then.
1172 File tempKnownFile = new File(mBaseStateDir, "processed.new");
1173 RandomAccessFile known = null;
1174 try {
1175 known = new RandomAccessFile(tempKnownFile, "rws");
1176 mEverStoredApps.remove(packageName);
1177 for (String s : mEverStoredApps) {
1178 known.writeUTF(s);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001179 if (DEBUG) Slog.v(TAG, " " + s);
Christopher Tatee97e8072009-07-15 16:45:50 -07001180 }
Dan Egnor852f8e42009-09-30 11:20:45 -07001181 known.close();
1182 known = null;
1183 if (!tempKnownFile.renameTo(mEverStored)) {
1184 throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
1185 }
1186 } catch (IOException e) {
1187 // Bad: we couldn't create the new copy. For safety's sake we
1188 // abandon the whole process and remove all what's-backed-up
1189 // state entirely, meaning we'll force a backup pass for every
1190 // participant on the next boot or [re]install.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001191 Slog.w(TAG, "Error rewriting " + mEverStored, e);
Dan Egnor852f8e42009-09-30 11:20:45 -07001192 mEverStoredApps.clear();
1193 tempKnownFile.delete();
1194 mEverStored.delete();
1195 } finally {
1196 try { if (known != null) known.close(); } catch (IOException e) {}
Christopher Tatee97e8072009-07-15 16:45:50 -07001197 }
1198 }
1199 }
1200
Christopher Tateb49ceb32010-02-08 16:22:24 -08001201 // Persistently record the current and ancestral backup tokens as well
1202 // as the set of packages with data [supposedly] available in the
1203 // ancestral dataset.
Christopher Tate84725812010-02-04 15:52:40 -08001204 void writeRestoreTokens() {
1205 try {
1206 RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
Christopher Tateb49ceb32010-02-08 16:22:24 -08001207
1208 // First, the version number of this record, for futureproofing
1209 af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
1210
1211 // Write the ancestral and current tokens
Christopher Tate84725812010-02-04 15:52:40 -08001212 af.writeLong(mAncestralToken);
1213 af.writeLong(mCurrentToken);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001214
1215 // Now write the set of ancestral packages
1216 if (mAncestralPackages == null) {
1217 af.writeInt(-1);
1218 } else {
1219 af.writeInt(mAncestralPackages.size());
Joe Onorato8a9b2202010-02-26 18:56:32 -08001220 if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
Christopher Tateb49ceb32010-02-08 16:22:24 -08001221 for (String pkgName : mAncestralPackages) {
1222 af.writeUTF(pkgName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001223 if (DEBUG) Slog.v(TAG, " " + pkgName);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001224 }
1225 }
Christopher Tate84725812010-02-04 15:52:40 -08001226 af.close();
1227 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001228 Slog.w(TAG, "Unable to write token file:", e);
Christopher Tate84725812010-02-04 15:52:40 -08001229 }
1230 }
1231
Dan Egnor87a02bc2009-06-17 02:30:10 -07001232 // Return the given transport
Christopher Tate91717492009-06-26 21:07:13 -07001233 private IBackupTransport getTransport(String transportName) {
1234 synchronized (mTransports) {
1235 IBackupTransport transport = mTransports.get(transportName);
1236 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001237 Slog.w(TAG, "Requested unavailable transport: " + transportName);
Christopher Tate91717492009-06-26 21:07:13 -07001238 }
1239 return transport;
Christopher Tate8c850b72009-06-07 19:33:20 -07001240 }
Christopher Tate8c850b72009-06-07 19:33:20 -07001241 }
1242
Christopher Tatedf01dea2009-06-09 20:45:02 -07001243 // fire off a backup agent, blocking until it attaches or times out
1244 IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
1245 IBackupAgent agent = null;
1246 synchronized(mAgentConnectLock) {
1247 mConnecting = true;
1248 mConnectedAgent = null;
1249 try {
1250 if (mActivityManager.bindBackupAgent(app, mode)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001251 Slog.d(TAG, "awaiting agent for " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001252
1253 // success; wait for the agent to arrive
Christopher Tate75a99702011-05-18 16:28:19 -07001254 // only wait 10 seconds for the bind to happen
Christopher Tatec7b31e32009-06-10 15:49:30 -07001255 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1256 while (mConnecting && mConnectedAgent == null
1257 && (System.currentTimeMillis() < timeoutMark)) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07001258 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001259 mAgentConnectLock.wait(5000);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001260 } catch (InterruptedException e) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001261 // just bail
Christopher Tatedf01dea2009-06-09 20:45:02 -07001262 return null;
1263 }
1264 }
1265
1266 // if we timed out with no connect, abort and move on
1267 if (mConnecting == true) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001268 Slog.w(TAG, "Timeout waiting for agent " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001269 return null;
1270 }
1271 agent = mConnectedAgent;
1272 }
1273 } catch (RemoteException e) {
1274 // can't happen
1275 }
1276 }
1277 return agent;
1278 }
1279
Christopher Tatec7b31e32009-06-10 15:49:30 -07001280 // clear an application's data, blocking until the operation completes or times out
1281 void clearApplicationDataSynchronous(String packageName) {
Christopher Tatef7c886b2009-06-26 15:34:09 -07001282 // Don't wipe packages marked allowClearUserData=false
1283 try {
1284 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1285 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001286 if (DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
Christopher Tatef7c886b2009-06-26 15:34:09 -07001287 + packageName);
1288 return;
1289 }
1290 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001291 Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
Christopher Tatef7c886b2009-06-26 15:34:09 -07001292 return;
1293 }
1294
Christopher Tatec7b31e32009-06-10 15:49:30 -07001295 ClearDataObserver observer = new ClearDataObserver();
1296
1297 synchronized(mClearDataLock) {
1298 mClearingData = true;
Christopher Tate9dfdac52009-08-06 14:57:53 -07001299 try {
1300 mActivityManager.clearApplicationUserData(packageName, observer);
1301 } catch (RemoteException e) {
1302 // can't happen because the activity manager is in this process
1303 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001304
1305 // only wait 10 seconds for the clear data to happen
1306 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1307 while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
1308 try {
1309 mClearDataLock.wait(5000);
1310 } catch (InterruptedException e) {
1311 // won't happen, but still.
1312 mClearingData = false;
1313 }
1314 }
1315 }
1316 }
1317
1318 class ClearDataObserver extends IPackageDataObserver.Stub {
Dan Egnor852f8e42009-09-30 11:20:45 -07001319 public void onRemoveCompleted(String packageName, boolean succeeded) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001320 synchronized(mClearDataLock) {
1321 mClearingData = false;
Christopher Tatef68eb502009-06-16 11:02:01 -07001322 mClearDataLock.notifyAll();
Christopher Tatec7b31e32009-06-10 15:49:30 -07001323 }
1324 }
1325 }
1326
Christopher Tate1bb69062010-02-19 17:02:12 -08001327 // Get the restore-set token for the best-available restore set for this package:
1328 // the active set if possible, else the ancestral one. Returns zero if none available.
1329 long getAvailableRestoreToken(String packageName) {
1330 long token = mAncestralToken;
1331 synchronized (mQueueLock) {
1332 if (mEverStoredApps.contains(packageName)) {
1333 token = mCurrentToken;
1334 }
1335 }
1336 return token;
1337 }
1338
Christopher Tate44a27902010-01-27 17:15:49 -08001339 // -----
1340 // Utility methods used by the asynchronous-with-timeout backup/restore operations
1341 boolean waitUntilOperationComplete(int token) {
1342 int finalState = OP_PENDING;
1343 synchronized (mCurrentOpLock) {
1344 try {
1345 while ((finalState = mCurrentOperations.get(token, OP_TIMEOUT)) == OP_PENDING) {
1346 try {
1347 mCurrentOpLock.wait();
1348 } catch (InterruptedException e) {}
1349 }
1350 } catch (IndexOutOfBoundsException e) {
1351 // the operation has been mysteriously cleared from our
1352 // bookkeeping -- consider this a success and ignore it.
1353 }
1354 }
1355 mBackupHandler.removeMessages(MSG_TIMEOUT);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001356 if (DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
Christopher Tate1bb69062010-02-19 17:02:12 -08001357 + " complete: finalState=" + finalState);
Christopher Tate44a27902010-01-27 17:15:49 -08001358 return finalState == OP_ACKNOWLEDGED;
1359 }
1360
1361 void prepareOperationTimeout(int token, long interval) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001362 if (DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
Christopher Tate1bb69062010-02-19 17:02:12 -08001363 + " interval=" + interval);
Christopher Tate4a627c72011-04-01 14:43:32 -07001364 synchronized (mCurrentOpLock) {
1365 mCurrentOperations.put(token, OP_PENDING);
1366 Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
1367 mBackupHandler.sendMessageDelayed(msg, interval);
1368 }
Christopher Tate44a27902010-01-27 17:15:49 -08001369 }
1370
Christopher Tate043dadc2009-06-02 16:11:00 -07001371 // ----- Back up a set of applications via a worker thread -----
1372
Christopher Tate44a27902010-01-27 17:15:49 -08001373 class PerformBackupTask implements Runnable {
Christopher Tate043dadc2009-06-02 16:11:00 -07001374 private static final String TAG = "PerformBackupThread";
Christopher Tateaa088442009-06-16 18:25:46 -07001375 IBackupTransport mTransport;
Christopher Tate043dadc2009-06-02 16:11:00 -07001376 ArrayList<BackupRequest> mQueue;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001377 File mStateDir;
Christopher Tatecde87f42009-06-12 12:55:53 -07001378 File mJournal;
Christopher Tate043dadc2009-06-02 16:11:00 -07001379
Christopher Tate44a27902010-01-27 17:15:49 -08001380 public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue,
Christopher Tatecde87f42009-06-12 12:55:53 -07001381 File journal) {
Christopher Tateaa088442009-06-16 18:25:46 -07001382 mTransport = transport;
Christopher Tate043dadc2009-06-02 16:11:00 -07001383 mQueue = queue;
Christopher Tatecde87f42009-06-12 12:55:53 -07001384 mJournal = journal;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001385
1386 try {
1387 mStateDir = new File(mBaseStateDir, transport.transportDirName());
1388 } catch (RemoteException e) {
1389 // can't happen; the transport is local
1390 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001391 }
1392
Christopher Tate043dadc2009-06-02 16:11:00 -07001393 public void run() {
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001394 int status = BackupConstants.TRANSPORT_OK;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001395 long startRealtime = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001396 if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
Christopher Tate043dadc2009-06-02 16:11:00 -07001397
Christopher Tate79588342009-06-30 16:11:49 -07001398 // Backups run at background priority
1399 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1400
Christopher Tate043dadc2009-06-02 16:11:00 -07001401 try {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001402 EventLog.writeEvent(EventLogTags.BACKUP_START, mTransport.transportDirName());
Dan Egnor01445162009-09-21 17:04:05 -07001403
Dan Egnor852f8e42009-09-30 11:20:45 -07001404 // If we haven't stored package manager metadata yet, we must init the transport.
1405 File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
1406 if (status == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001407 Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
Dan Egnor852f8e42009-09-30 11:20:45 -07001408 resetBackupState(mStateDir); // Just to make sure.
Dan Egnor01445162009-09-21 17:04:05 -07001409 status = mTransport.initializeDevice();
Dan Egnor726247c2009-09-29 19:12:31 -07001410 if (status == BackupConstants.TRANSPORT_OK) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001411 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07001412 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001413 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Joe Onorato8a9b2202010-02-26 18:56:32 -08001414 Slog.e(TAG, "Transport error in initializeDevice()");
Dan Egnor726247c2009-09-29 19:12:31 -07001415 }
Dan Egnor01445162009-09-21 17:04:05 -07001416 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001417
1418 // The package manager doesn't have a proper <application> etc, but since
1419 // it's running here in the system process we can just set up its agent
1420 // directly and use a synthetic BackupRequest. We always run this pass
1421 // because it's cheap and this way we guarantee that we don't get out of
1422 // step even if we're selecting among various transports at run time.
Dan Egnor01445162009-09-21 17:04:05 -07001423 if (status == BackupConstants.TRANSPORT_OK) {
1424 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
1425 mPackageManager, allAgentPackages());
Christopher Tate4a627c72011-04-01 14:43:32 -07001426 BackupRequest pmRequest = new BackupRequest(new ApplicationInfo());
Dan Egnor01445162009-09-21 17:04:05 -07001427 pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
1428 status = processOneBackup(pmRequest,
1429 IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
1430 }
Christopher Tate90967f42009-09-20 15:28:33 -07001431
Dan Egnor01445162009-09-21 17:04:05 -07001432 if (status == BackupConstants.TRANSPORT_OK) {
1433 // Now run all the backups in our queue
1434 status = doQueuedBackups(mTransport);
1435 }
1436
1437 if (status == BackupConstants.TRANSPORT_OK) {
1438 // Tell the transport to finish everything it has buffered
1439 status = mTransport.finishBackup();
1440 if (status == BackupConstants.TRANSPORT_OK) {
1441 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001442 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, mQueue.size(), millis);
Dan Egnor01445162009-09-21 17:04:05 -07001443 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001444 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(finish)");
Joe Onorato8a9b2202010-02-26 18:56:32 -08001445 Slog.e(TAG, "Transport error in finishBackup()");
Dan Egnor01445162009-09-21 17:04:05 -07001446 }
1447 }
1448
Dan Egnor01445162009-09-21 17:04:05 -07001449 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
Christopher Tated55e18a2009-09-21 10:12:59 -07001450 // The backend reports that our dataset has been wiped. We need to
1451 // reset all of our bookkeeping and instead run a new backup pass for
Christopher Tatec2af5d32010-02-02 15:18:58 -08001452 // everything.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001453 EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
Christopher Tated55e18a2009-09-21 10:12:59 -07001454 resetBackupState(mStateDir);
Dan Egnorbb9001c2009-07-27 12:20:13 -07001455 }
1456 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001457 Slog.e(TAG, "Error in backup thread", e);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001458 status = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001459 } finally {
Christopher Tate84725812010-02-04 15:52:40 -08001460 // If everything actually went through and this is the first time we've
1461 // done a backup, we can now record what the current backup dataset token
1462 // is.
Christopher Tate29505552010-06-24 15:58:01 -07001463 if ((mCurrentToken == 0) && (status == BackupConstants.TRANSPORT_OK)) {
Christopher Tate84725812010-02-04 15:52:40 -08001464 try {
1465 mCurrentToken = mTransport.getCurrentRestoreSet();
1466 } catch (RemoteException e) { /* cannot happen */ }
1467 writeRestoreTokens();
1468 }
1469
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001470 // If things went wrong, we need to re-stage the apps we had expected
1471 // to be backing up in this pass. This journals the package names in
1472 // the current active pending-backup file, not in the we are holding
1473 // here in mJournal.
1474 if (status != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001475 Slog.w(TAG, "Backup pass unsuccessful, restaging");
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001476 for (BackupRequest req : mQueue) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07001477 dataChangedImpl(req.appInfo.packageName);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001478 }
Christopher Tate21ab6a52009-09-24 18:01:46 -07001479
1480 // We also want to reset the backup schedule based on whatever
1481 // the transport suggests by way of retry/backoff time.
1482 try {
1483 startBackupAlarmsLocked(mTransport.requestBackupTime());
1484 } catch (RemoteException e) { /* cannot happen */ }
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001485 }
1486
1487 // Either backup was successful, in which case we of course do not need
1488 // this pass's journal any more; or it failed, in which case we just
1489 // re-enqueued all of these packages in the current active journal.
1490 // Either way, we no longer need this pass's journal.
Dan Egnor852f8e42009-09-30 11:20:45 -07001491 if (mJournal != null && !mJournal.delete()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001492 Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001493 }
1494
Christopher Tatec2af5d32010-02-02 15:18:58 -08001495 // Only once we're entirely finished do we release the wakelock
Dan Egnor852f8e42009-09-30 11:20:45 -07001496 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
Dan Egnor852f8e42009-09-30 11:20:45 -07001497 backupNow();
1498 }
1499
Dan Egnorbb9001c2009-07-27 12:20:13 -07001500 mWakelock.release();
Christopher Tatecde87f42009-06-12 12:55:53 -07001501 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001502 }
1503
Dan Egnor01445162009-09-21 17:04:05 -07001504 private int doQueuedBackups(IBackupTransport transport) {
Christopher Tate043dadc2009-06-02 16:11:00 -07001505 for (BackupRequest request : mQueue) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001506 Slog.d(TAG, "starting agent for backup of " + request);
Christopher Tate043dadc2009-06-02 16:11:00 -07001507
Christopher Tatec28083a2010-12-14 16:16:44 -08001508 // Verify that the requested app exists; it might be something that
1509 // requested a backup but was then uninstalled. The request was
1510 // journalled and rather than tamper with the journal it's safer
1511 // to sanity-check here.
1512 try {
1513 mPackageManager.getPackageInfo(request.appInfo.packageName, 0);
1514 } catch (NameNotFoundException e) {
1515 Slog.d(TAG, "Package does not exist; skipping");
1516 continue;
1517 }
1518
Christopher Tate043dadc2009-06-02 16:11:00 -07001519 IBackupAgent agent = null;
Christopher Tate043dadc2009-06-02 16:11:00 -07001520 try {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001521 mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
Christopher Tate4a627c72011-04-01 14:43:32 -07001522 agent = bindToAgentSynchronous(request.appInfo,
1523 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001524 if (agent != null) {
Dan Egnor01445162009-09-21 17:04:05 -07001525 int result = processOneBackup(request, agent, transport);
1526 if (result != BackupConstants.TRANSPORT_OK) return result;
Christopher Tate043dadc2009-06-02 16:11:00 -07001527 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001528 } catch (SecurityException ex) {
1529 // Try for the next one.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001530 Slog.d(TAG, "error in bind/backup", ex);
Dan Egnor01445162009-09-21 17:04:05 -07001531 } finally {
1532 try { // unbind even on timeout, just in case
1533 mActivityManager.unbindBackupAgent(request.appInfo);
1534 } catch (RemoteException e) {}
Christopher Tate043dadc2009-06-02 16:11:00 -07001535 }
1536 }
Dan Egnor01445162009-09-21 17:04:05 -07001537
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001538 mWakelock.setWorkSource(null);
1539
Dan Egnor01445162009-09-21 17:04:05 -07001540 return BackupConstants.TRANSPORT_OK;
Christopher Tate043dadc2009-06-02 16:11:00 -07001541 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001542
Dan Egnor01445162009-09-21 17:04:05 -07001543 private int processOneBackup(BackupRequest request, IBackupAgent agent,
1544 IBackupTransport transport) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001545 final String packageName = request.appInfo.packageName;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001546 if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001547
Dan Egnorbb9001c2009-07-27 12:20:13 -07001548 File savedStateName = new File(mStateDir, packageName);
1549 File backupDataName = new File(mDataDir, packageName + ".data");
1550 File newStateName = new File(mStateDir, packageName + ".new");
1551
1552 ParcelFileDescriptor savedState = null;
1553 ParcelFileDescriptor backupData = null;
1554 ParcelFileDescriptor newState = null;
1555
1556 PackageInfo packInfo;
Christopher Tate4a627c72011-04-01 14:43:32 -07001557 final int token = generateToken();
Christopher Tatec7b31e32009-06-10 15:49:30 -07001558 try {
1559 // Look up the package info & signatures. This is first so that if it
1560 // throws an exception, there's no file setup yet that would need to
1561 // be unraveled.
Christopher Tateabce4e82009-06-18 18:35:32 -07001562 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
1563 // The metadata 'package' is synthetic
1564 packInfo = new PackageInfo();
1565 packInfo.packageName = packageName;
1566 } else {
1567 packInfo = mPackageManager.getPackageInfo(packageName,
Christopher Tatec7b31e32009-06-10 15:49:30 -07001568 PackageManager.GET_SIGNATURES);
Christopher Tateabce4e82009-06-18 18:35:32 -07001569 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001570
Christopher Tatec7b31e32009-06-10 15:49:30 -07001571 // In a full backup, we pass a null ParcelFileDescriptor as
Christopher Tate4a627c72011-04-01 14:43:32 -07001572 // the saved-state "file". This is by definition an incremental,
1573 // so we build a saved state file to pass.
1574 savedState = ParcelFileDescriptor.open(savedStateName,
1575 ParcelFileDescriptor.MODE_READ_ONLY |
1576 ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
Christopher Tatec7b31e32009-06-10 15:49:30 -07001577
Dan Egnorbb9001c2009-07-27 12:20:13 -07001578 backupData = ParcelFileDescriptor.open(backupDataName,
1579 ParcelFileDescriptor.MODE_READ_WRITE |
1580 ParcelFileDescriptor.MODE_CREATE |
1581 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001582
Dan Egnorbb9001c2009-07-27 12:20:13 -07001583 newState = ParcelFileDescriptor.open(newStateName,
1584 ParcelFileDescriptor.MODE_READ_WRITE |
1585 ParcelFileDescriptor.MODE_CREATE |
1586 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001587
Christopher Tate44a27902010-01-27 17:15:49 -08001588 // Initiate the target's backup pass
1589 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
Christopher Tate4a627c72011-04-01 14:43:32 -07001590 agent.doBackup(savedState, backupData, newState, false,
1591 token, mBackupManagerBinder);
Christopher Tate44a27902010-01-27 17:15:49 -08001592 boolean success = waitUntilOperationComplete(token);
1593
1594 if (!success) {
1595 // timeout -- bail out into the failed-transaction logic
1596 throw new RuntimeException("Backup timeout");
1597 }
1598
Dan Egnorbb9001c2009-07-27 12:20:13 -07001599 logBackupComplete(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001600 if (DEBUG) Slog.v(TAG, "doBackup() success");
Christopher Tatec7b31e32009-06-10 15:49:30 -07001601 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001602 Slog.e(TAG, "Error backing up " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001603 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString());
Dan Egnorbb9001c2009-07-27 12:20:13 -07001604 backupDataName.delete();
1605 newStateName.delete();
Christopher Tated55e18a2009-09-21 10:12:59 -07001606 return BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001607 } finally {
1608 try { if (savedState != null) savedState.close(); } catch (IOException e) {}
1609 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1610 try { if (newState != null) newState.close(); } catch (IOException e) {}
1611 savedState = backupData = newState = null;
Christopher Tate44a27902010-01-27 17:15:49 -08001612 synchronized (mCurrentOpLock) {
1613 mCurrentOperations.clear();
1614 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001615 }
1616
1617 // Now propagate the newly-backed-up data to the transport
Dan Egnor01445162009-09-21 17:04:05 -07001618 int result = BackupConstants.TRANSPORT_OK;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001619 try {
1620 int size = (int) backupDataName.length();
1621 if (size > 0) {
Dan Egnor01445162009-09-21 17:04:05 -07001622 if (result == BackupConstants.TRANSPORT_OK) {
1623 backupData = ParcelFileDescriptor.open(backupDataName,
1624 ParcelFileDescriptor.MODE_READ_ONLY);
1625 result = transport.performBackup(packInfo, backupData);
1626 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001627
Dan Egnor83861e72009-09-17 16:17:55 -07001628 // TODO - We call finishBackup() for each application backed up, because
1629 // we need to know now whether it succeeded or failed. Instead, we should
1630 // hold off on finishBackup() until the end, which implies holding off on
1631 // renaming *all* the output state files (see below) until that happens.
1632
Dan Egnor01445162009-09-21 17:04:05 -07001633 if (result == BackupConstants.TRANSPORT_OK) {
1634 result = transport.finishBackup();
Dan Egnor83861e72009-09-17 16:17:55 -07001635 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001636 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001637 if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
Dan Egnorbb9001c2009-07-27 12:20:13 -07001638 }
1639
1640 // After successful transport, delete the now-stale data
1641 // and juggle the files so that next time we supply the agent
1642 // with the new state file it just created.
Dan Egnor01445162009-09-21 17:04:05 -07001643 if (result == BackupConstants.TRANSPORT_OK) {
1644 backupDataName.delete();
1645 newStateName.renameTo(savedStateName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001646 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, packageName, size);
Dan Egnor01445162009-09-21 17:04:05 -07001647 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001648 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
Dan Egnor01445162009-09-21 17:04:05 -07001649 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001650 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001651 Slog.e(TAG, "Transport error backing up " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001652 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
Dan Egnor01445162009-09-21 17:04:05 -07001653 result = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001654 } finally {
1655 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
Christopher Tatec7b31e32009-06-10 15:49:30 -07001656 }
Christopher Tated55e18a2009-09-21 10:12:59 -07001657
Dan Egnor01445162009-09-21 17:04:05 -07001658 return result;
Christopher Tatec7b31e32009-06-10 15:49:30 -07001659 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001660 }
1661
Christopher Tatedf01dea2009-06-09 20:45:02 -07001662
Christopher Tate4a627c72011-04-01 14:43:32 -07001663 // ----- Full backup to a file/socket -----
1664
1665 class PerformFullBackupTask implements Runnable {
1666 ParcelFileDescriptor mOutputFile;
1667 IFullBackupRestoreObserver mObserver;
1668 boolean mIncludeApks;
1669 boolean mIncludeShared;
1670 boolean mAllApps;
1671 String[] mPackages;
1672 AtomicBoolean mLatchObject;
1673 File mFilesDir;
1674 File mManifestFile;
1675
1676 PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
1677 boolean includeApks, boolean includeShared,
1678 boolean doAllApps, String[] packages, AtomicBoolean latch) {
1679 mOutputFile = fd;
1680 mObserver = observer;
1681 mIncludeApks = includeApks;
1682 mIncludeShared = includeShared;
1683 mAllApps = doAllApps;
1684 mPackages = packages;
1685 mLatchObject = latch;
1686
1687 mFilesDir = new File("/data/system");
1688 mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
1689 }
1690
1691 @Override
1692 public void run() {
1693 final List<PackageInfo> packagesToBackup;
1694
Christopher Tateb0628bf2011-06-02 15:08:13 -07001695 Slog.i(TAG, "--- Performing full-dataset backup ---");
Christopher Tate4a627c72011-04-01 14:43:32 -07001696 sendStartBackup();
1697
1698 // doAllApps supersedes the package set if any
1699 if (mAllApps) {
1700 packagesToBackup = mPackageManager.getInstalledPackages(
1701 PackageManager.GET_SIGNATURES);
1702 } else {
1703 packagesToBackup = new ArrayList<PackageInfo>();
1704 for (String pkgName : mPackages) {
1705 try {
1706 packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
1707 PackageManager.GET_SIGNATURES));
1708 } catch (NameNotFoundException e) {
1709 Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
1710 }
1711 }
1712 }
1713
Christopher Tatea858cb02011-06-03 12:27:51 -07001714 // Cull any packages that have indicated that backups are not permitted.
1715 for (int i = 0; i < packagesToBackup.size(); ) {
1716 PackageInfo info = packagesToBackup.get(i);
1717 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
1718 packagesToBackup.remove(i);
1719 } else {
1720 i++;
1721 }
1722 }
1723
Christopher Tate4a627c72011-04-01 14:43:32 -07001724 PackageInfo pkg = null;
1725 try {
Christopher Tateb0628bf2011-06-02 15:08:13 -07001726 // Now back up the app data via the agent mechanism
Christopher Tate4a627c72011-04-01 14:43:32 -07001727 int N = packagesToBackup.size();
1728 for (int i = 0; i < N; i++) {
1729 pkg = packagesToBackup.get(i);
Christopher Tateb0628bf2011-06-02 15:08:13 -07001730 backupOnePackage(pkg);
1731 }
Christopher Tate4a627c72011-04-01 14:43:32 -07001732
Christopher Tateb0628bf2011-06-02 15:08:13 -07001733 // Finally, shared storage if requested
1734 if (mIncludeShared) {
1735 backupSharedStorage();
Christopher Tate4a627c72011-04-01 14:43:32 -07001736 }
1737 } catch (RemoteException e) {
1738 Slog.e(TAG, "App died during full backup");
1739 } finally {
Christopher Tateb0628bf2011-06-02 15:08:13 -07001740 tearDown(pkg);
Christopher Tate4a627c72011-04-01 14:43:32 -07001741 try {
1742 mOutputFile.close();
1743 } catch (IOException e) {
1744 /* nothing we can do about this */
1745 }
1746 synchronized (mCurrentOpLock) {
1747 mCurrentOperations.clear();
1748 }
1749 synchronized (mLatchObject) {
1750 mLatchObject.set(true);
1751 mLatchObject.notifyAll();
1752 }
1753 sendEndBackup();
1754 mWakelock.release();
1755 if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
1756 }
1757 }
1758
Christopher Tateb0628bf2011-06-02 15:08:13 -07001759 private void backupOnePackage(PackageInfo pkg) throws RemoteException {
1760 Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
1761
1762 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
1763 IApplicationThread.BACKUP_MODE_FULL);
1764 if (agent != null) {
1765 try {
1766 ApplicationInfo app = pkg.applicationInfo;
1767 boolean sendApk = mIncludeApks
1768 && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
1769 && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
1770 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
1771
1772 sendOnBackupPackage(pkg.packageName);
1773
1774 {
1775 BackupDataOutput output = new BackupDataOutput(
1776 mOutputFile.getFileDescriptor());
1777
1778 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
1786 if (DEBUG) Slog.d(TAG, "Calling doBackup()");
1787 final int token = generateToken();
1788 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
1789 agent.doBackup(null, mOutputFile, null, sendApk,
1790 token, mBackupManagerBinder);
1791 if (!waitUntilOperationComplete(token)) {
1792 Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
1793 } else {
1794 if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
1795 }
1796 } catch (IOException e) {
1797 Slog.e(TAG, "Error backing up " + pkg.packageName, e);
1798 }
1799 } else {
1800 Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
1801 }
1802 tearDown(pkg);
1803 }
1804
1805 private void backupSharedStorage() throws RemoteException {
1806 PackageInfo pkg = null;
1807 try {
1808 pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0);
1809 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
1810 IApplicationThread.BACKUP_MODE_FULL);
1811 if (agent != null) {
1812 sendOnBackupPackage("Shared storage");
1813
1814 final int token = generateToken();
1815 prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
1816 agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder);
1817 if (!waitUntilOperationComplete(token)) {
1818 Slog.e(TAG, "Full backup failed on shared storage");
1819 } else {
1820 if (DEBUG) Slog.d(TAG, "Full shared storage backup success");
1821 }
1822 } else {
1823 Slog.e(TAG, "Could not bind to shared storage backup agent");
1824 }
1825 } catch (NameNotFoundException e) {
1826 Slog.e(TAG, "Shared storage backup package not found");
1827 } finally {
1828 tearDown(pkg);
1829 }
1830 }
1831
Christopher Tate4a627c72011-04-01 14:43:32 -07001832 private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
1833 throws IOException {
1834 // Manifest format. All data are strings ending in LF:
1835 // BACKUP_MANIFEST_VERSION, currently 1
1836 //
1837 // Version 1:
1838 // package name
1839 // package's versionCode
Christopher Tate75a99702011-05-18 16:28:19 -07001840 // platform versionCode
1841 // getInstallerPackageName() for this package (maybe empty)
1842 // boolean: "1" if archive includes .apk; any other string means not
Christopher Tate4a627c72011-04-01 14:43:32 -07001843 // number of signatures == N
1844 // N*: signature byte array in ascii format per Signature.toCharsString()
1845 StringBuilder builder = new StringBuilder(4096);
1846 StringBuilderPrinter printer = new StringBuilderPrinter(builder);
1847
1848 printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
1849 printer.println(pkg.packageName);
1850 printer.println(Integer.toString(pkg.versionCode));
Christopher Tate75a99702011-05-18 16:28:19 -07001851 printer.println(Integer.toString(Build.VERSION.SDK_INT));
1852
1853 String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
1854 printer.println((installerName != null) ? installerName : "");
1855
Christopher Tate4a627c72011-04-01 14:43:32 -07001856 printer.println(withApk ? "1" : "0");
1857 if (pkg.signatures == null) {
1858 printer.println("0");
1859 } else {
1860 printer.println(Integer.toString(pkg.signatures.length));
1861 for (Signature sig : pkg.signatures) {
1862 printer.println(sig.toCharsString());
1863 }
1864 }
1865
1866 FileOutputStream outstream = new FileOutputStream(manifestFile);
Christopher Tate4a627c72011-04-01 14:43:32 -07001867 outstream.write(builder.toString().getBytes());
1868 outstream.close();
1869 }
1870
1871 private void tearDown(PackageInfo pkg) {
Christopher Tateb0628bf2011-06-02 15:08:13 -07001872 if (pkg != null) {
1873 final ApplicationInfo app = pkg.applicationInfo;
1874 if (app != null) {
1875 try {
1876 // unbind and tidy up even on timeout or failure, just in case
1877 mActivityManager.unbindBackupAgent(app);
Christopher Tate4a627c72011-04-01 14:43:32 -07001878
Christopher Tateb0628bf2011-06-02 15:08:13 -07001879 // The agent was running with a stub Application object, so shut it down.
1880 if (app.uid != Process.SYSTEM_UID) {
1881 if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
1882 mActivityManager.killApplicationProcess(app.processName, app.uid);
1883 } else {
1884 if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
1885 }
1886 } catch (RemoteException e) {
1887 Slog.d(TAG, "Lost app trying to shut down");
1888 }
Christopher Tate4a627c72011-04-01 14:43:32 -07001889 }
Christopher Tate4a627c72011-04-01 14:43:32 -07001890 }
1891 }
1892
1893 // wrappers for observer use
1894 void sendStartBackup() {
1895 if (mObserver != null) {
1896 try {
1897 mObserver.onStartBackup();
1898 } catch (RemoteException e) {
1899 Slog.w(TAG, "full backup observer went away: startBackup");
1900 mObserver = null;
1901 }
1902 }
1903 }
1904
1905 void sendOnBackupPackage(String name) {
1906 if (mObserver != null) {
1907 try {
1908 // TODO: use a more user-friendly name string
1909 mObserver.onBackupPackage(name);
1910 } catch (RemoteException e) {
1911 Slog.w(TAG, "full backup observer went away: backupPackage");
1912 mObserver = null;
1913 }
1914 }
1915 }
1916
1917 void sendEndBackup() {
1918 if (mObserver != null) {
1919 try {
1920 mObserver.onEndBackup();
1921 } catch (RemoteException e) {
1922 Slog.w(TAG, "full backup observer went away: endBackup");
1923 mObserver = null;
1924 }
1925 }
1926 }
1927 }
1928
1929
Christopher Tate75a99702011-05-18 16:28:19 -07001930 // ----- Full restore from a file/socket -----
1931
1932 // Description of a file in the restore datastream
1933 static class FileMetadata {
1934 String packageName; // name of the owning app
1935 String installerPackageName; // name of the market-type app that installed the owner
1936 int type; // e.g. FullBackup.TYPE_DIRECTORY
1937 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN
1938 String path; // subpath within the semantic domain
1939 long mode; // e.g. 0666 (actually int)
1940 long mtime; // last mod time, UTC time_t (actually int)
1941 long size; // bytes of content
1942 }
1943
1944 enum RestorePolicy {
1945 IGNORE,
1946 ACCEPT,
1947 ACCEPT_IF_APK
1948 }
1949
1950 class PerformFullRestoreTask implements Runnable {
1951 ParcelFileDescriptor mInputFile;
1952 IFullBackupRestoreObserver mObserver;
1953 AtomicBoolean mLatchObject;
1954 IBackupAgent mAgent;
1955 String mAgentPackage;
1956 ApplicationInfo mTargetApp;
1957 ParcelFileDescriptor[] mPipes = null;
1958
1959 // possible handling states for a given package in the restore dataset
1960 final HashMap<String, RestorePolicy> mPackagePolicies
1961 = new HashMap<String, RestorePolicy>();
1962
1963 // installer package names for each encountered app, derived from the manifests
1964 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
1965
1966 // Signatures for a given package found in its manifest file
1967 final HashMap<String, Signature[]> mManifestSignatures
1968 = new HashMap<String, Signature[]>();
1969
1970 // Packages we've already wiped data on when restoring their first file
1971 final HashSet<String> mClearedPackages = new HashSet<String>();
1972
1973 PerformFullRestoreTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
1974 AtomicBoolean latch) {
1975 mInputFile = fd;
1976 mObserver = observer;
1977 mLatchObject = latch;
1978 mAgent = null;
1979 mAgentPackage = null;
1980 mTargetApp = null;
1981
1982 // Which packages we've already wiped data on. We prepopulate this
1983 // with a whitelist of packages known to be unclearable.
1984 mClearedPackages.add("android");
Christopher Tate75a99702011-05-18 16:28:19 -07001985 mClearedPackages.add("com.android.providers.settings");
Christopher Tateb0628bf2011-06-02 15:08:13 -07001986
Christopher Tate75a99702011-05-18 16:28:19 -07001987 }
1988
1989 class RestoreFileRunnable implements Runnable {
1990 IBackupAgent mAgent;
1991 FileMetadata mInfo;
1992 ParcelFileDescriptor mSocket;
1993 int mToken;
1994
1995 RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
1996 ParcelFileDescriptor socket, int token) throws IOException {
1997 mAgent = agent;
1998 mInfo = info;
1999 mToken = token;
2000
2001 // This class is used strictly for process-local binder invocations. The
2002 // semantics of ParcelFileDescriptor differ in this case; in particular, we
2003 // do not automatically get a 'dup'ed descriptor that we can can continue
2004 // to use asynchronously from the caller. So, we make sure to dup it ourselves
2005 // before proceeding to do the restore.
2006 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
2007 }
2008
2009 @Override
2010 public void run() {
2011 try {
2012 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
2013 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
2014 mToken, mBackupManagerBinder);
2015 } catch (RemoteException e) {
2016 // never happens; this is used strictly for local binder calls
2017 }
2018 }
2019 }
2020
2021 @Override
2022 public void run() {
2023 Slog.i(TAG, "--- Performing full-dataset restore ---");
2024 sendStartRestore();
2025
Christopher Tateb0628bf2011-06-02 15:08:13 -07002026 // Are we able to restore shared-storage data?
2027 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
2028 mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
2029 }
2030
Christopher Tate75a99702011-05-18 16:28:19 -07002031 try {
2032 byte[] buffer = new byte[32 * 1024];
2033 FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
2034
2035 boolean didRestore;
2036 do {
2037 didRestore = restoreOneFile(instream, buffer);
2038 } while (didRestore);
2039
2040 if (DEBUG) Slog.v(TAG, "Done consuming input tarfile");
2041 } finally {
2042 tearDownPipes();
2043 tearDownAgent(mTargetApp);
2044
2045 try {
2046 mInputFile.close();
2047 } catch (IOException e) {
2048 /* nothing we can do about this */
2049 }
2050 synchronized (mCurrentOpLock) {
2051 mCurrentOperations.clear();
2052 }
2053 synchronized (mLatchObject) {
2054 mLatchObject.set(true);
2055 mLatchObject.notifyAll();
2056 }
2057 sendEndRestore();
2058 mWakelock.release();
2059 if (DEBUG) Slog.d(TAG, "Full restore pass complete.");
2060 }
2061 }
2062
2063 boolean restoreOneFile(InputStream instream, byte[] buffer) {
2064 FileMetadata info;
2065 try {
2066 info = readTarHeaders(instream);
2067 if (info != null) {
2068 if (DEBUG) {
2069 dumpFileMetadata(info);
2070 }
2071
2072 final String pkg = info.packageName;
2073 if (!pkg.equals(mAgentPackage)) {
2074 // okay, change in package; set up our various
2075 // bookkeeping if we haven't seen it yet
2076 if (!mPackagePolicies.containsKey(pkg)) {
2077 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2078 }
2079
2080 // Clean up the previous agent relationship if necessary,
2081 // and let the observer know we're considering a new app.
2082 if (mAgent != null) {
2083 if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
2084 tearDownPipes();
2085 tearDownAgent(mTargetApp);
2086 mTargetApp = null;
2087 mAgentPackage = null;
2088 }
2089 }
2090
2091 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2092 mPackagePolicies.put(pkg, readAppManifest(info, instream));
2093 mPackageInstallers.put(pkg, info.installerPackageName);
2094 // We've read only the manifest content itself at this point,
2095 // so consume the footer before looping around to the next
2096 // input file
2097 skipTarPadding(info.size, instream);
2098 sendOnRestorePackage(pkg);
2099 } else {
2100 // Non-manifest, so it's actual file data. Is this a package
2101 // we're ignoring?
2102 boolean okay = true;
2103 RestorePolicy policy = mPackagePolicies.get(pkg);
2104 switch (policy) {
2105 case IGNORE:
2106 okay = false;
2107 break;
2108
2109 case ACCEPT_IF_APK:
2110 // If we're in accept-if-apk state, then the first file we
2111 // see MUST be the apk.
2112 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2113 if (DEBUG) Slog.d(TAG, "APK file; installing");
2114 // Try to install the app.
2115 String installerName = mPackageInstallers.get(pkg);
2116 okay = installApk(info, installerName, instream);
2117 // good to go; promote to ACCEPT
2118 mPackagePolicies.put(pkg, (okay)
2119 ? RestorePolicy.ACCEPT
2120 : RestorePolicy.IGNORE);
2121 // At this point we've consumed this file entry
2122 // ourselves, so just strip the tar footer and
2123 // go on to the next file in the input stream
2124 skipTarPadding(info.size, instream);
2125 return true;
2126 } else {
2127 // File data before (or without) the apk. We can't
2128 // handle it coherently in this case so ignore it.
2129 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2130 okay = false;
2131 }
2132 break;
2133
2134 case ACCEPT:
2135 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2136 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
2137 // we can take the data without the apk, so we
2138 // *want* to do so. skip the apk by declaring this
2139 // one file not-okay without changing the restore
2140 // policy for the package.
2141 okay = false;
2142 }
2143 break;
2144
2145 default:
2146 // Something has gone dreadfully wrong when determining
2147 // the restore policy from the manifest. Ignore the
2148 // rest of this package's data.
2149 Slog.e(TAG, "Invalid policy from manifest");
2150 okay = false;
2151 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2152 break;
2153 }
2154
2155 // If the policy is satisfied, go ahead and set up to pipe the
2156 // data to the agent.
2157 if (DEBUG && okay && mAgent != null) {
2158 Slog.i(TAG, "Reusing existing agent instance");
2159 }
2160 if (okay && mAgent == null) {
2161 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
2162
2163 try {
2164 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
2165
2166 // If we haven't sent any data to this app yet, we probably
2167 // need to clear it first. Check that.
2168 if (!mClearedPackages.contains(pkg)) {
2169 // apps with their own full backup agents are
2170 // responsible for coherently managing a full
2171 // restore.
2172 if (mTargetApp.fullBackupAgentName == null) {
2173 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
2174 clearApplicationDataSynchronous(pkg);
2175 } else {
2176 if (DEBUG) Slog.d(TAG, "full backup agent ("
2177 + mTargetApp.fullBackupAgentName + ") => no clear");
2178 }
2179 mClearedPackages.add(pkg);
2180 } else {
2181 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
2182 }
2183
2184 // All set; now set up the IPC and launch the agent
2185 setUpPipes();
2186 mAgent = bindToAgentSynchronous(mTargetApp,
2187 IApplicationThread.BACKUP_MODE_RESTORE_FULL);
2188 mAgentPackage = pkg;
2189 } catch (IOException e) {
2190 // fall through to error handling
2191 } catch (NameNotFoundException e) {
2192 // fall through to error handling
2193 }
2194
2195 if (mAgent == null) {
2196 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
2197 okay = false;
2198 tearDownPipes();
2199 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2200 }
2201 }
2202
2203 // Sanity check: make sure we never give data to the wrong app. This
2204 // should never happen but a little paranoia here won't go amiss.
2205 if (okay && !pkg.equals(mAgentPackage)) {
2206 Slog.e(TAG, "Restoring data for " + pkg
2207 + " but agent is for " + mAgentPackage);
2208 okay = false;
2209 }
2210
2211 // At this point we have an agent ready to handle the full
2212 // restore data as well as a pipe for sending data to
2213 // that agent. Tell the agent to start reading from the
2214 // pipe.
2215 if (okay) {
2216 boolean agentSuccess = true;
2217 long toCopy = info.size;
2218 final int token = generateToken();
2219 try {
2220 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
2221 + info.path);
2222 prepareOperationTimeout(token,
2223 TIMEOUT_FULL_BACKUP_INTERVAL);
2224 // fire up the app's agent listening on the socket. If
2225 // the agent is running in the system process we can't
2226 // just invoke it asynchronously, so we provide a thread
2227 // for it here.
2228 if (mTargetApp.processName.equals("system")) {
2229 Slog.d(TAG, "system process agent - spinning a thread");
2230 RestoreFileRunnable runner = new RestoreFileRunnable(
2231 mAgent, info, mPipes[0], token);
2232 new Thread(runner).start();
2233 } else {
2234 mAgent.doRestoreFile(mPipes[0], info.size, info.type,
2235 info.domain, info.path, info.mode, info.mtime,
2236 token, mBackupManagerBinder);
2237 }
2238 } catch (IOException e) {
2239 // couldn't dup the socket for a process-local restore
2240 Slog.d(TAG, "Couldn't establish restore");
2241 agentSuccess = false;
2242 okay = false;
2243 } catch (RemoteException e) {
2244 // whoops, remote agent went away. We'll eat the content
2245 // ourselves, then, and not copy it over.
2246 Slog.e(TAG, "Agent crashed during full restore");
2247 agentSuccess = false;
2248 okay = false;
2249 }
2250
2251 // Copy over the data if the agent is still good
2252 if (okay) {
2253 boolean pipeOkay = true;
2254 FileOutputStream pipe = new FileOutputStream(
2255 mPipes[1].getFileDescriptor());
2256 if (DEBUG) Slog.d(TAG, "Piping data to agent");
2257 while (toCopy > 0) {
2258 int toRead = (toCopy > buffer.length)
2259 ? buffer.length : (int)toCopy;
2260 int nRead = instream.read(buffer, 0, toRead);
2261 if (nRead <= 0) break;
2262 toCopy -= nRead;
2263
2264 // send it to the output pipe as long as things
2265 // are still good
2266 if (pipeOkay) {
2267 try {
2268 pipe.write(buffer, 0, nRead);
2269 } catch (IOException e) {
2270 Slog.e(TAG,
2271 "Failed to write to restore pipe", e);
2272 pipeOkay = false;
2273 }
2274 }
2275 }
2276
2277 // done sending that file! Now we just need to consume
2278 // the delta from info.size to the end of block.
2279 skipTarPadding(info.size, instream);
2280
2281 // and now that we've sent it all, wait for the remote
2282 // side to acknowledge receipt
2283 agentSuccess = waitUntilOperationComplete(token);
2284 }
2285
2286 // okay, if the remote end failed at any point, deal with
2287 // it by ignoring the rest of the restore on it
2288 if (!agentSuccess) {
2289 mBackupHandler.removeMessages(MSG_TIMEOUT);
2290 tearDownPipes();
2291 tearDownAgent(mTargetApp);
2292 mAgent = null;
2293 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2294 }
2295 }
2296
2297 // Problems setting up the agent communication, or an already-
2298 // ignored package: skip to the next tar stream entry by
2299 // reading and discarding this file.
2300 if (!okay) {
2301 if (DEBUG) Slog.d(TAG, "[discarding file content]");
2302 long bytesToConsume = (info.size + 511) & ~511;
2303 while (bytesToConsume > 0) {
2304 int toRead = (bytesToConsume > buffer.length)
2305 ? buffer.length : (int)bytesToConsume;
2306 long nRead = instream.read(buffer, 0, toRead);
2307 if (nRead <= 0) break;
2308 bytesToConsume -= nRead;
2309 }
2310 }
2311 }
2312 }
2313 } catch (IOException e) {
2314 Slog.w(TAG, "io exception on restore socket read", e);
2315 // treat as EOF
2316 info = null;
2317 }
2318
2319 return (info != null);
2320 }
2321
2322 void setUpPipes() throws IOException {
2323 mPipes = ParcelFileDescriptor.createPipe();
2324 }
2325
2326 void tearDownPipes() {
2327 if (mPipes != null) {
2328 if (mPipes[0] != null) {
2329 try {
2330 mPipes[0].close();
2331 mPipes[0] = null;
2332 mPipes[1].close();
2333 mPipes[1] = null;
2334 } catch (IOException e) {
2335 Slog.w(TAG, "Couldn't close agent pipes", e);
2336 }
2337 }
2338 mPipes = null;
2339 }
2340 }
2341
2342 void tearDownAgent(ApplicationInfo app) {
2343 if (mAgent != null) {
2344 try {
2345 // unbind and tidy up even on timeout or failure, just in case
2346 mActivityManager.unbindBackupAgent(app);
2347
2348 // The agent was running with a stub Application object, so shut it down.
2349 // !!! We hardcode the confirmation UI's package name here rather than use a
2350 // manifest flag! TODO something less direct.
2351 if (app.uid != Process.SYSTEM_UID
2352 && !app.packageName.equals("com.android.backupconfirm")) {
2353 if (DEBUG) Slog.d(TAG, "Killing host process");
2354 mActivityManager.killApplicationProcess(app.processName, app.uid);
2355 } else {
2356 if (DEBUG) Slog.d(TAG, "Not killing after full restore");
2357 }
2358 } catch (RemoteException e) {
2359 Slog.d(TAG, "Lost app trying to shut down");
2360 }
2361 mAgent = null;
2362 }
2363 }
2364
2365 class RestoreInstallObserver extends IPackageInstallObserver.Stub {
2366 final AtomicBoolean mDone = new AtomicBoolean();
Christopher Tatea858cb02011-06-03 12:27:51 -07002367 String mPackageName;
Christopher Tate75a99702011-05-18 16:28:19 -07002368 int mResult;
2369
2370 public void reset() {
2371 synchronized (mDone) {
2372 mDone.set(false);
2373 }
2374 }
2375
2376 public void waitForCompletion() {
2377 synchronized (mDone) {
2378 while (mDone.get() == false) {
2379 try {
2380 mDone.wait();
2381 } catch (InterruptedException e) { }
2382 }
2383 }
2384 }
2385
2386 int getResult() {
2387 return mResult;
2388 }
2389
2390 @Override
2391 public void packageInstalled(String packageName, int returnCode)
2392 throws RemoteException {
2393 synchronized (mDone) {
2394 mResult = returnCode;
Christopher Tatea858cb02011-06-03 12:27:51 -07002395 mPackageName = packageName;
Christopher Tate75a99702011-05-18 16:28:19 -07002396 mDone.set(true);
2397 mDone.notifyAll();
2398 }
2399 }
2400 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002401
2402 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
2403 final AtomicBoolean mDone = new AtomicBoolean();
2404 int mResult;
2405
2406 public void reset() {
2407 synchronized (mDone) {
2408 mDone.set(false);
2409 }
2410 }
2411
2412 public void waitForCompletion() {
2413 synchronized (mDone) {
2414 while (mDone.get() == false) {
2415 try {
2416 mDone.wait();
2417 } catch (InterruptedException e) { }
2418 }
2419 }
2420 }
2421
2422 @Override
2423 public void packageDeleted(String packageName, int returnCode) throws RemoteException {
2424 synchronized (mDone) {
2425 mResult = returnCode;
2426 mDone.set(true);
2427 mDone.notifyAll();
2428 }
2429 }
2430 }
2431
Christopher Tate75a99702011-05-18 16:28:19 -07002432 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
Christopher Tatea858cb02011-06-03 12:27:51 -07002433 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
Christopher Tate75a99702011-05-18 16:28:19 -07002434
2435 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
2436 boolean okay = true;
2437
2438 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
2439
2440 // The file content is an .apk file. Copy it out to a staging location and
2441 // attempt to install it.
2442 File apkFile = new File(mDataDir, info.packageName);
2443 try {
2444 FileOutputStream apkStream = new FileOutputStream(apkFile);
2445 byte[] buffer = new byte[32 * 1024];
2446 long size = info.size;
2447 while (size > 0) {
2448 long toRead = (buffer.length < size) ? buffer.length : size;
2449 int didRead = instream.read(buffer, 0, (int)toRead);
2450 apkStream.write(buffer, 0, didRead);
2451 size -= didRead;
2452 }
2453 apkStream.close();
2454
2455 // make sure the installer can read it
2456 apkFile.setReadable(true, false);
2457
2458 // Now install it
2459 Uri packageUri = Uri.fromFile(apkFile);
2460 mInstallObserver.reset();
2461 mPackageManager.installPackage(packageUri, mInstallObserver,
2462 PackageManager.INSTALL_REPLACE_EXISTING, installerPackage);
2463 mInstallObserver.waitForCompletion();
2464
2465 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
2466 // The only time we continue to accept install of data even if the
2467 // apk install failed is if we had already determined that we could
2468 // accept the data regardless.
2469 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
2470 okay = false;
2471 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002472 } else {
2473 // Okay, the install succeeded. Make sure it was the right app.
2474 boolean uninstall = false;
2475 if (!mInstallObserver.mPackageName.equals(info.packageName)) {
2476 Slog.w(TAG, "Restore stream claimed to include apk for "
2477 + info.packageName + " but apk was really "
2478 + mInstallObserver.mPackageName);
2479 // delete the package we just put in place; it might be fraudulent
2480 okay = false;
2481 uninstall = true;
2482 } else {
2483 try {
2484 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
2485 PackageManager.GET_SIGNATURES);
2486 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
2487 Slog.w(TAG, "Restore stream contains apk of package "
2488 + info.packageName + " but it disallows backup/restore");
2489 okay = false;
2490 } else {
2491 // So far so good -- do the signatures match the manifest?
2492 Signature[] sigs = mManifestSignatures.get(info.packageName);
2493 if (!signaturesMatch(sigs, pkg)) {
2494 Slog.w(TAG, "Installed app " + info.packageName
2495 + " signatures do not match restore manifest");
2496 okay = false;
2497 uninstall = true;
2498 }
2499 }
2500 } catch (NameNotFoundException e) {
2501 Slog.w(TAG, "Install of package " + info.packageName
2502 + " succeeded but now not found");
2503 okay = false;
2504 }
2505 }
2506
2507 // If we're not okay at this point, we need to delete the package
2508 // that we just installed.
2509 if (uninstall) {
2510 mDeleteObserver.reset();
2511 mPackageManager.deletePackage(mInstallObserver.mPackageName,
2512 mDeleteObserver, 0);
2513 mDeleteObserver.waitForCompletion();
2514 }
Christopher Tate75a99702011-05-18 16:28:19 -07002515 }
2516 } catch (IOException e) {
2517 Slog.e(TAG, "Unable to transcribe restored apk for install");
2518 okay = false;
2519 } finally {
2520 apkFile.delete();
2521 }
2522
2523 return okay;
2524 }
2525
2526 // Given an actual file content size, consume the post-content padding mandated
2527 // by the tar format.
2528 void skipTarPadding(long size, InputStream instream) throws IOException {
2529 long partial = (size + 512) % 512;
2530 if (partial > 0) {
2531 byte[] buffer = new byte[512];
2532 instream.read(buffer, 0, 512 - (int)partial);
2533 }
2534 }
2535
2536 // Returns a policy constant; takes a buffer arg to reduce memory churn
2537 RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
2538 throws IOException {
2539 // Fail on suspiciously large manifest files
2540 if (info.size > 64 * 1024) {
2541 throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
2542 }
2543 byte[] buffer = new byte[(int) info.size];
2544 int nRead = 0;
2545 while (nRead < info.size) {
2546 nRead += instream.read(buffer, nRead, (int)info.size - nRead);
2547 }
2548
2549 RestorePolicy policy = RestorePolicy.IGNORE;
2550 String[] str = new String[1];
2551 int offset = 0;
2552
2553 try {
2554 offset = extractLine(buffer, offset, str);
2555 int version = Integer.parseInt(str[0]);
2556 if (version == BACKUP_MANIFEST_VERSION) {
2557 offset = extractLine(buffer, offset, str);
2558 String manifestPackage = str[0];
2559 // TODO: handle <original-package>
2560 if (manifestPackage.equals(info.packageName)) {
2561 offset = extractLine(buffer, offset, str);
2562 version = Integer.parseInt(str[0]); // app version
2563 offset = extractLine(buffer, offset, str);
2564 int platformVersion = Integer.parseInt(str[0]);
2565 offset = extractLine(buffer, offset, str);
2566 info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
2567 offset = extractLine(buffer, offset, str);
2568 boolean hasApk = str[0].equals("1");
2569 offset = extractLine(buffer, offset, str);
2570 int numSigs = Integer.parseInt(str[0]);
Christopher Tate75a99702011-05-18 16:28:19 -07002571 if (numSigs > 0) {
Christopher Tatea858cb02011-06-03 12:27:51 -07002572 Signature[] sigs = new Signature[numSigs];
Christopher Tate75a99702011-05-18 16:28:19 -07002573 for (int i = 0; i < numSigs; i++) {
2574 offset = extractLine(buffer, offset, str);
2575 sigs[i] = new Signature(str[0]);
2576 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002577 mManifestSignatures.put(info.packageName, sigs);
Christopher Tate75a99702011-05-18 16:28:19 -07002578
2579 // Okay, got the manifest info we need...
2580 try {
Christopher Tate75a99702011-05-18 16:28:19 -07002581 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2582 info.packageName, PackageManager.GET_SIGNATURES);
Christopher Tatea858cb02011-06-03 12:27:51 -07002583 // Fall through to IGNORE if the app explicitly disallows backup
2584 final int flags = pkgInfo.applicationInfo.flags;
2585 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
2586 // Verify signatures against any installed version; if they
2587 // don't match, then we fall though and ignore the data. The
2588 // signatureMatch() method explicitly ignores the signature
2589 // check for packages installed on the system partition, because
2590 // such packages are signed with the platform cert instead of
2591 // the app developer's cert, so they're different on every
2592 // device.
2593 if (signaturesMatch(sigs, pkgInfo)) {
2594 if (pkgInfo.versionCode >= version) {
2595 Slog.i(TAG, "Sig + version match; taking data");
2596 policy = RestorePolicy.ACCEPT;
2597 } else {
2598 // The data is from a newer version of the app than
2599 // is presently installed. That means we can only
2600 // use it if the matching apk is also supplied.
2601 Slog.d(TAG, "Data version " + version
2602 + " is newer than installed version "
2603 + pkgInfo.versionCode + " - requiring apk");
2604 policy = RestorePolicy.ACCEPT_IF_APK;
2605 }
Christopher Tate75a99702011-05-18 16:28:19 -07002606 } else {
Christopher Tatea858cb02011-06-03 12:27:51 -07002607 Slog.w(TAG, "Restore manifest signatures do not match "
2608 + "installed application for " + info.packageName);
Christopher Tate75a99702011-05-18 16:28:19 -07002609 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002610 } else {
2611 if (DEBUG) Slog.i(TAG, "Restore manifest from "
2612 + info.packageName + " but allowBackup=false");
Christopher Tate75a99702011-05-18 16:28:19 -07002613 }
2614 } catch (NameNotFoundException e) {
2615 // Okay, the target app isn't installed. We can process
2616 // the restore properly only if the dataset provides the
2617 // apk file and we can successfully install it.
2618 if (DEBUG) Slog.i(TAG, "Package " + info.packageName
2619 + " not installed; requiring apk in dataset");
2620 policy = RestorePolicy.ACCEPT_IF_APK;
2621 }
2622
2623 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
2624 Slog.i(TAG, "Cannot restore package " + info.packageName
2625 + " without the matching .apk");
2626 }
2627 } else {
2628 Slog.i(TAG, "Missing signature on backed-up package "
2629 + info.packageName);
2630 }
2631 } else {
2632 Slog.i(TAG, "Expected package " + info.packageName
2633 + " but restore manifest claims " + manifestPackage);
2634 }
2635 } else {
2636 Slog.i(TAG, "Unknown restore manifest version " + version
2637 + " for package " + info.packageName);
2638 }
2639 } catch (NumberFormatException e) {
2640 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
2641 }
2642
2643 return policy;
2644 }
2645
2646 // Builds a line from a byte buffer starting at 'offset', and returns
2647 // the index of the next unconsumed data in the buffer.
2648 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
2649 final int end = buffer.length;
2650 if (offset >= end) throw new IOException("Incomplete data");
2651
2652 int pos;
2653 for (pos = offset; pos < end; pos++) {
2654 byte c = buffer[pos];
2655 // at LF we declare end of line, and return the next char as the
2656 // starting point for the next time through
2657 if (c == '\n') {
2658 break;
2659 }
2660 }
2661 outStr[0] = new String(buffer, offset, pos - offset);
2662 pos++; // may be pointing an extra byte past the end but that's okay
2663 return pos;
2664 }
2665
2666 void dumpFileMetadata(FileMetadata info) {
2667 if (DEBUG) {
2668 StringBuilder b = new StringBuilder(128);
2669
2670 // mode string
2671 b.append((info.type == FullBackup.TYPE_DIRECTORY) ? 'd' : '-');
2672 b.append(((info.mode & 0400) != 0) ? 'r' : '-');
2673 b.append(((info.mode & 0200) != 0) ? 'w' : '-');
2674 b.append(((info.mode & 0100) != 0) ? 'x' : '-');
2675 b.append(((info.mode & 0040) != 0) ? 'r' : '-');
2676 b.append(((info.mode & 0020) != 0) ? 'w' : '-');
2677 b.append(((info.mode & 0010) != 0) ? 'x' : '-');
2678 b.append(((info.mode & 0004) != 0) ? 'r' : '-');
2679 b.append(((info.mode & 0002) != 0) ? 'w' : '-');
2680 b.append(((info.mode & 0001) != 0) ? 'x' : '-');
2681 b.append(String.format(" %9d ", info.size));
2682
2683 Date stamp = new Date(info.mtime);
2684 b.append(new SimpleDateFormat("MMM dd kk:mm:ss ").format(stamp));
2685
2686 b.append(info.packageName);
2687 b.append(" :: ");
2688 b.append(info.domain);
2689 b.append(" :: ");
2690 b.append(info.path);
2691
2692 Slog.i(TAG, b.toString());
2693 }
2694 }
2695 // Consume a tar file header block [sequence] and accumulate the relevant metadata
2696 FileMetadata readTarHeaders(InputStream instream) throws IOException {
2697 byte[] block = new byte[512];
2698 FileMetadata info = null;
2699
2700 boolean gotHeader = readTarHeader(instream, block);
2701 if (gotHeader) {
2702 // okay, presume we're okay, and extract the various metadata
2703 info = new FileMetadata();
2704 info.size = extractRadix(block, 124, 12, 8);
2705 info.mtime = extractRadix(block, 136, 12, 8);
2706 info.mode = extractRadix(block, 100, 8, 8);
2707
2708 info.path = extractString(block, 345, 155); // prefix
2709 String path = extractString(block, 0, 100);
2710 if (path.length() > 0) {
2711 if (info.path.length() > 0) info.path += '/';
2712 info.path += path;
2713 }
2714
2715 // tar link indicator field: 1 byte at offset 156 in the header.
2716 int typeChar = block[156];
2717 if (typeChar == 'x') {
2718 // pax extended header, so we need to read that
2719 gotHeader = readPaxExtendedHeader(instream, info);
2720 if (gotHeader) {
2721 // and after a pax extended header comes another real header -- read
2722 // that to find the real file type
2723 gotHeader = readTarHeader(instream, block);
2724 }
2725 if (!gotHeader) throw new IOException("Bad or missing pax header");
2726
2727 typeChar = block[156];
2728 }
2729
2730 switch (typeChar) {
2731 case '0': info.type = FullBackup.TYPE_FILE; break;
2732 case '5': info.type = FullBackup.TYPE_DIRECTORY; break;
2733 case 0: {
2734 // presume EOF
2735 return null;
2736 }
2737 default: {
2738 Slog.e(TAG, "Unknown tar entity type: " + typeChar);
2739 throw new IOException("Unknown entity type " + typeChar);
2740 }
2741 }
2742
2743 // Parse out the path
2744 //
2745 // first: apps/shared/unrecognized
2746 if (FullBackup.SHARED_PREFIX.regionMatches(0,
2747 info.path, 0, FullBackup.SHARED_PREFIX.length())) {
2748 // File in shared storage. !!! TODO: implement this.
2749 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
Christopher Tateb0628bf2011-06-02 15:08:13 -07002750 info.packageName = "com.android.sharedstoragebackup";
Christopher Tate75a99702011-05-18 16:28:19 -07002751 info.domain = FullBackup.SHARED_STORAGE_TOKEN;
Christopher Tateb0628bf2011-06-02 15:08:13 -07002752 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
Christopher Tate75a99702011-05-18 16:28:19 -07002753 } else if (FullBackup.APPS_PREFIX.regionMatches(0,
2754 info.path, 0, FullBackup.APPS_PREFIX.length())) {
2755 // App content! Parse out the package name and domain
2756
2757 // strip the apps/ prefix
2758 info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
2759
2760 // extract the package name
2761 int slash = info.path.indexOf('/');
2762 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
2763 info.packageName = info.path.substring(0, slash);
2764 info.path = info.path.substring(slash+1);
2765
2766 // if it's a manifest we're done, otherwise parse out the domains
2767 if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2768 slash = info.path.indexOf('/');
2769 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
2770 info.domain = info.path.substring(0, slash);
2771 // validate that it's one of the domains we understand
2772 if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
2773 && !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
2774 && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
2775 && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
2776 && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
2777 && !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
2778 && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
2779 throw new IOException("Unrecognized domain " + info.domain);
2780 }
2781
2782 info.path = info.path.substring(slash + 1);
2783 }
2784 }
2785 }
2786 return info;
2787 }
2788
2789 boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
2790 int nRead = instream.read(block, 0, 512);
2791 if (nRead > 0 && nRead != 512) {
2792 // if we read only a partial block, then things are
2793 // clearly screwed up. terminate the restore.
2794 throw new IOException("Partial header block: " + nRead);
2795 }
2796 return (nRead > 0);
2797 }
2798
2799 // overwrites 'info' fields based on the pax extended header
2800 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
2801 throws IOException {
2802 // We should never see a pax extended header larger than this
2803 if (info.size > 32*1024) {
2804 Slog.w(TAG, "Suspiciously large pax header size " + info.size
2805 + " - aborting");
2806 throw new IOException("Sanity failure: pax header size " + info.size);
2807 }
2808
2809 // read whole blocks, not just the content size
2810 int numBlocks = (int)((info.size + 511) >> 9);
2811 byte[] data = new byte[numBlocks * 512];
2812 int nRead = instream.read(data);
2813 if (nRead != data.length) {
2814 return false;
2815 }
2816
2817 final int contentSize = (int) info.size;
2818 int offset = 0;
2819 do {
2820 // extract the line at 'offset'
2821 int eol = offset+1;
2822 while (eol < contentSize && data[eol] != ' ') eol++;
2823 if (eol >= contentSize) {
2824 // error: we just hit EOD looking for the end of the size field
2825 throw new IOException("Invalid pax data");
2826 }
2827 // eol points to the space between the count and the key
2828 int linelen = (int) extractRadix(data, offset, eol - offset, 10);
2829 int key = eol + 1; // start of key=value
2830 eol = offset + linelen - 1; // trailing LF
2831 int value;
2832 for (value = key+1; data[value] != '=' && value <= eol; value++);
2833 if (value > eol) {
2834 throw new IOException("Invalid pax declaration");
2835 }
2836
2837 // pax requires that key/value strings be in UTF-8
2838 String keyStr = new String(data, key, value-key, "UTF-8");
2839 // -1 to strip the trailing LF
2840 String valStr = new String(data, value+1, eol-value-1, "UTF-8");
2841
2842 if ("path".equals(keyStr)) {
2843 info.path = valStr;
2844 } else if ("size".equals(keyStr)) {
2845 info.size = Long.parseLong(valStr);
2846 } else {
2847 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
2848 }
2849
2850 offset += linelen;
2851 } while (offset < contentSize);
2852
2853 return true;
2854 }
2855
2856 long extractRadix(byte[] data, int offset, int maxChars, int radix)
2857 throws IOException {
2858 long value = 0;
2859 final int end = offset + maxChars;
2860 for (int i = offset; i < end; i++) {
2861 final byte b = data[i];
Christopher Tate3f6c77b2011-06-07 13:17:17 -07002862 // Numeric fields in tar can terminate with either NUL or SPC
Christopher Tate75a99702011-05-18 16:28:19 -07002863 if (b == 0 || b == ' ') break;
2864 if (b < '0' || b > ('0' + radix - 1)) {
2865 throw new IOException("Invalid number in header");
2866 }
2867 value = radix * value + (b - '0');
2868 }
2869 return value;
2870 }
2871
2872 String extractString(byte[] data, int offset, int maxChars) throws IOException {
2873 final int end = offset + maxChars;
2874 int eos = offset;
Christopher Tate3f6c77b2011-06-07 13:17:17 -07002875 // tar string fields terminate early with a NUL
2876 while (eos < end && data[eos] != 0) eos++;
Christopher Tate75a99702011-05-18 16:28:19 -07002877 return new String(data, offset, eos-offset, "US-ASCII");
2878 }
2879
2880 void sendStartRestore() {
2881 if (mObserver != null) {
2882 try {
2883 mObserver.onStartRestore();
2884 } catch (RemoteException e) {
2885 Slog.w(TAG, "full restore observer went away: startRestore");
2886 mObserver = null;
2887 }
2888 }
2889 }
2890
2891 void sendOnRestorePackage(String name) {
2892 if (mObserver != null) {
2893 try {
2894 // TODO: use a more user-friendly name string
2895 mObserver.onRestorePackage(name);
2896 } catch (RemoteException e) {
2897 Slog.w(TAG, "full restore observer went away: restorePackage");
2898 mObserver = null;
2899 }
2900 }
2901 }
2902
2903 void sendEndRestore() {
2904 if (mObserver != null) {
2905 try {
2906 mObserver.onEndRestore();
2907 } catch (RemoteException e) {
2908 Slog.w(TAG, "full restore observer went away: endRestore");
2909 mObserver = null;
2910 }
2911 }
2912 }
2913 }
2914
Christopher Tatedf01dea2009-06-09 20:45:02 -07002915 // ----- Restore handling -----
2916
Christopher Tate78dd4a72009-11-04 11:49:08 -08002917 private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
2918 // If the target resides on the system partition, we allow it to restore
2919 // data from the like-named package in a restore set even if the signatures
2920 // do not match. (Unlike general applications, those flashed to the system
2921 // partition will be signed with the device's platform certificate, so on
2922 // different phones the same system app will have different signatures.)
2923 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002924 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
Christopher Tate78dd4a72009-11-04 11:49:08 -08002925 return true;
2926 }
2927
Christopher Tate20efdf6b2009-06-18 19:41:36 -07002928 // Allow unsigned apps, but not signed on one device and unsigned on the other
2929 // !!! TODO: is this the right policy?
Christopher Tate78dd4a72009-11-04 11:49:08 -08002930 Signature[] deviceSigs = target.signatures;
Joe Onorato8a9b2202010-02-26 18:56:32 -08002931 if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
Christopher Tate6aa41f42009-06-19 14:14:22 -07002932 + " device=" + deviceSigs);
Christopher Tate20efdf6b2009-06-18 19:41:36 -07002933 if ((storedSigs == null || storedSigs.length == 0)
2934 && (deviceSigs == null || deviceSigs.length == 0)) {
2935 return true;
2936 }
2937 if (storedSigs == null || deviceSigs == null) {
2938 return false;
2939 }
2940
Christopher Tateabce4e82009-06-18 18:35:32 -07002941 // !!! TODO: this demands that every stored signature match one
2942 // that is present on device, and does not demand the converse.
2943 // Is this this right policy?
2944 int nStored = storedSigs.length;
2945 int nDevice = deviceSigs.length;
2946
2947 for (int i=0; i < nStored; i++) {
2948 boolean match = false;
2949 for (int j=0; j < nDevice; j++) {
2950 if (storedSigs[i].equals(deviceSigs[j])) {
2951 match = true;
2952 break;
2953 }
2954 }
2955 if (!match) {
2956 return false;
2957 }
2958 }
2959 return true;
2960 }
2961
Christopher Tate44a27902010-01-27 17:15:49 -08002962 class PerformRestoreTask implements Runnable {
Christopher Tatedf01dea2009-06-09 20:45:02 -07002963 private IBackupTransport mTransport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07002964 private IRestoreObserver mObserver;
Dan Egnor156411d2009-06-26 13:20:02 -07002965 private long mToken;
Christopher Tate84725812010-02-04 15:52:40 -08002966 private PackageInfo mTargetPackage;
Christopher Tate5cb400b2009-06-25 16:03:14 -07002967 private File mStateDir;
Christopher Tate1bb69062010-02-19 17:02:12 -08002968 private int mPmToken;
Chris Tate249345b2010-10-29 12:57:04 -07002969 private boolean mNeedFullBackup;
Christopher Tatedf01dea2009-06-09 20:45:02 -07002970
Christopher Tate5cbbf562009-06-22 16:44:51 -07002971 class RestoreRequest {
2972 public PackageInfo app;
2973 public int storedAppVersion;
2974
2975 RestoreRequest(PackageInfo _app, int _version) {
2976 app = _app;
2977 storedAppVersion = _version;
2978 }
2979 }
2980
Christopher Tate44a27902010-01-27 17:15:49 -08002981 PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
Chris Tate249345b2010-10-29 12:57:04 -07002982 long restoreSetToken, PackageInfo targetPackage, int pmToken,
2983 boolean needFullBackup) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07002984 mTransport = transport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07002985 mObserver = observer;
Christopher Tate9bbc21a2009-06-10 20:23:25 -07002986 mToken = restoreSetToken;
Christopher Tate84725812010-02-04 15:52:40 -08002987 mTargetPackage = targetPackage;
Christopher Tate1bb69062010-02-19 17:02:12 -08002988 mPmToken = pmToken;
Chris Tate249345b2010-10-29 12:57:04 -07002989 mNeedFullBackup = needFullBackup;
Christopher Tate5cb400b2009-06-25 16:03:14 -07002990
2991 try {
2992 mStateDir = new File(mBaseStateDir, transport.transportDirName());
2993 } catch (RemoteException e) {
2994 // can't happen; the transport is local
2995 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07002996 }
2997
Christopher Tatedf01dea2009-06-09 20:45:02 -07002998 public void run() {
Dan Egnorbb9001c2009-07-27 12:20:13 -07002999 long startRealtime = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08003000 if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport
Christopher Tate84725812010-02-04 15:52:40 -08003001 + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
Christopher Tate1bb69062010-02-19 17:02:12 -08003002 + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken);
Christopher Tatedf01dea2009-06-09 20:45:02 -07003003
Christopher Tateb49ceb32010-02-08 16:22:24 -08003004 PackageManagerBackupAgent pmAgent = null;
Christopher Tate7d562ec2009-06-25 18:03:43 -07003005 int error = -1; // assume error
3006
Dan Egnorefe52642009-06-24 00:16:33 -07003007 // build the set of apps to restore
Christopher Tatedf01dea2009-06-09 20:45:02 -07003008 try {
Dan Egnorbb9001c2009-07-27 12:20:13 -07003009 // TODO: Log this before getAvailableRestoreSets, somehow
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003010 EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken);
Christopher Tateabce4e82009-06-18 18:35:32 -07003011
Dan Egnorefe52642009-06-24 00:16:33 -07003012 // Get the list of all packages which have backup enabled.
3013 // (Include the Package Manager metadata pseudo-package first.)
3014 ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
3015 PackageInfo omPackage = new PackageInfo();
3016 omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
3017 restorePackages.add(omPackage);
Christopher Tatedf01dea2009-06-09 20:45:02 -07003018
Dan Egnorefe52642009-06-24 00:16:33 -07003019 List<PackageInfo> agentPackages = allAgentPackages();
Christopher Tate84725812010-02-04 15:52:40 -08003020 if (mTargetPackage == null) {
3021 restorePackages.addAll(agentPackages);
3022 } else {
3023 // Just one package to attempt restore of
3024 restorePackages.add(mTargetPackage);
3025 }
Dan Egnorefe52642009-06-24 00:16:33 -07003026
Christopher Tate7d562ec2009-06-25 18:03:43 -07003027 // let the observer know that we're running
3028 if (mObserver != null) {
3029 try {
3030 // !!! TODO: get an actual count from the transport after
3031 // its startRestore() runs?
3032 mObserver.restoreStarting(restorePackages.size());
3033 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003034 Slog.d(TAG, "Restore observer died at restoreStarting");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003035 mObserver = null;
3036 }
3037 }
3038
Dan Egnor01445162009-09-21 17:04:05 -07003039 if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) !=
3040 BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003041 Slog.e(TAG, "Error starting restore operation");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003042 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003043 return;
3044 }
3045
3046 String packageName = mTransport.nextRestorePackage();
3047 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003048 Slog.e(TAG, "Error getting first restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003049 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003050 return;
3051 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003052 Slog.i(TAG, "No restore data available");
Dan Egnorbb9001c2009-07-27 12:20:13 -07003053 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003054 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
Dan Egnorefe52642009-06-24 00:16:33 -07003055 return;
3056 } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003057 Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
Dan Egnorefe52642009-06-24 00:16:33 -07003058 + "\", found only \"" + packageName + "\"");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003059 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003060 "Package manager data missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003061 return;
3062 }
3063
3064 // Pull the Package Manager metadata from the restore set first
Christopher Tateb49ceb32010-02-08 16:22:24 -08003065 pmAgent = new PackageManagerBackupAgent(
Dan Egnorefe52642009-06-24 00:16:33 -07003066 mPackageManager, agentPackages);
Chris Tate249345b2010-10-29 12:57:04 -07003067 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()),
3068 mNeedFullBackup);
Dan Egnorefe52642009-06-24 00:16:33 -07003069
Christopher Tate8c032472009-07-02 14:28:47 -07003070 // Verify that the backup set includes metadata. If not, we can't do
3071 // signature/version verification etc, so we simply do not proceed with
3072 // the restore operation.
Christopher Tate3d7cd132009-07-07 14:23:07 -07003073 if (!pmAgent.hasMetadata()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003074 Slog.e(TAG, "No restore metadata available, so not restoring settings");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003075 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003076 "Package manager restore metadata missing");
Christopher Tate8c032472009-07-02 14:28:47 -07003077 return;
3078 }
3079
Christopher Tate7d562ec2009-06-25 18:03:43 -07003080 int count = 0;
Dan Egnorefe52642009-06-24 00:16:33 -07003081 for (;;) {
3082 packageName = mTransport.nextRestorePackage();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003083
Dan Egnorefe52642009-06-24 00:16:33 -07003084 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003085 Slog.e(TAG, "Error getting next restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003086 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003087 return;
3088 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003089 if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
Dan Egnorefe52642009-06-24 00:16:33 -07003090 break;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003091 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003092
Christopher Tate7d562ec2009-06-25 18:03:43 -07003093 if (mObserver != null) {
Christopher Tate7d562ec2009-06-25 18:03:43 -07003094 try {
Christopher Tate9c3cee92010-03-25 16:06:43 -07003095 mObserver.onUpdate(count, packageName);
Christopher Tate7d562ec2009-06-25 18:03:43 -07003096 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003097 Slog.d(TAG, "Restore observer died in onUpdate");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003098 mObserver = null;
3099 }
3100 }
3101
Dan Egnorefe52642009-06-24 00:16:33 -07003102 Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
3103 if (metaInfo == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003104 Slog.e(TAG, "Missing metadata for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003105 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003106 "Package metadata missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003107 continue;
3108 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003109
Dan Egnorbb9001c2009-07-27 12:20:13 -07003110 PackageInfo packageInfo;
3111 try {
3112 int flags = PackageManager.GET_SIGNATURES;
3113 packageInfo = mPackageManager.getPackageInfo(packageName, flags);
3114 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003115 Slog.e(TAG, "Invalid package restoring data", e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003116 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003117 "Package missing on device");
3118 continue;
3119 }
3120
Dan Egnorefe52642009-06-24 00:16:33 -07003121 if (metaInfo.versionCode > packageInfo.versionCode) {
Christopher Tate3dda5182010-02-24 16:06:18 -08003122 // Data is from a "newer" version of the app than we have currently
3123 // installed. If the app has not declared that it is prepared to
3124 // handle this case, we do not attempt the restore.
3125 if ((packageInfo.applicationInfo.flags
3126 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
3127 String message = "Version " + metaInfo.versionCode
3128 + " > installed version " + packageInfo.versionCode;
Joe Onorato8a9b2202010-02-26 18:56:32 -08003129 Slog.w(TAG, "Package " + packageName + ": " + message);
Christopher Tate3dda5182010-02-24 16:06:18 -08003130 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
3131 packageName, message);
3132 continue;
3133 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003134 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
Christopher Tate3dda5182010-02-24 16:06:18 -08003135 + " > installed " + packageInfo.versionCode
3136 + " but restoreAnyVersion");
3137 }
Dan Egnorefe52642009-06-24 00:16:33 -07003138 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003139
Christopher Tate78dd4a72009-11-04 11:49:08 -08003140 if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003141 Slog.w(TAG, "Signature mismatch restoring " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003142 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003143 "Signature mismatch");
Dan Egnorefe52642009-06-24 00:16:33 -07003144 continue;
3145 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003146
Joe Onorato8a9b2202010-02-26 18:56:32 -08003147 if (DEBUG) Slog.v(TAG, "Package " + packageName
Dan Egnorefe52642009-06-24 00:16:33 -07003148 + " restore version [" + metaInfo.versionCode
3149 + "] is compatible with installed version ["
3150 + packageInfo.versionCode + "]");
Christopher Tatec7b31e32009-06-10 15:49:30 -07003151
Christopher Tate3de55bc2010-03-12 17:28:08 -08003152 // Then set up and bind the agent
Dan Egnorefe52642009-06-24 00:16:33 -07003153 IBackupAgent agent = bindToAgentSynchronous(
3154 packageInfo.applicationInfo,
Christopher Tate3de55bc2010-03-12 17:28:08 -08003155 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Dan Egnorefe52642009-06-24 00:16:33 -07003156 if (agent == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003157 Slog.w(TAG, "Can't find backup agent 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 "Restore agent missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003160 continue;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003161 }
3162
Christopher Tate5e1ab332009-09-01 20:32:49 -07003163 // And then finally run the restore on this agent
Dan Egnorefe52642009-06-24 00:16:33 -07003164 try {
Chris Tate249345b2010-10-29 12:57:04 -07003165 processOneRestore(packageInfo, metaInfo.versionCode, agent,
3166 mNeedFullBackup);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003167 ++count;
Dan Egnorefe52642009-06-24 00:16:33 -07003168 } finally {
Christopher Tate5e1ab332009-09-01 20:32:49 -07003169 // unbind and tidy up even on timeout or failure, just in case
Dan Egnorefe52642009-06-24 00:16:33 -07003170 mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
Christopher Tate5e1ab332009-09-01 20:32:49 -07003171
3172 // The agent was probably running with a stub Application object,
3173 // which isn't a valid run mode for the main app logic. Shut
3174 // down the app so that next time it's launched, it gets the
Christopher Tate3dda5182010-02-24 16:06:18 -08003175 // usual full initialization. Note that this is only done for
3176 // full-system restores: when a single app has requested a restore,
3177 // it is explicitly not killed following that operation.
3178 if (mTargetPackage == null && (packageInfo.applicationInfo.flags
Christopher Tate5e1ab332009-09-01 20:32:49 -07003179 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003180 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
Christopher Tate5e1ab332009-09-01 20:32:49 -07003181 + packageInfo.applicationInfo.processName);
3182 mActivityManager.killApplicationProcess(
3183 packageInfo.applicationInfo.processName,
3184 packageInfo.applicationInfo.uid);
3185 }
Dan Egnorefe52642009-06-24 00:16:33 -07003186 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003187 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003188
3189 // if we get this far, report success to the observer
3190 error = 0;
Dan Egnorbb9001c2009-07-27 12:20:13 -07003191 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003192 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, count, millis);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003193 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003194 Slog.e(TAG, "Error in restore thread", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003195 } finally {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003196 if (DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003197
Dan Egnorefe52642009-06-24 00:16:33 -07003198 try {
3199 mTransport.finishRestore();
3200 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003201 Slog.e(TAG, "Error finishing restore", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003202 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003203
3204 if (mObserver != null) {
3205 try {
3206 mObserver.restoreFinished(error);
3207 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003208 Slog.d(TAG, "Restore observer died at restoreFinished");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003209 }
3210 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003211
Christopher Tate84725812010-02-04 15:52:40 -08003212 // If this was a restoreAll operation, record that this was our
Christopher Tateb49ceb32010-02-08 16:22:24 -08003213 // ancestral dataset, as well as the set of apps that are possibly
3214 // restoreable from the dataset
3215 if (mTargetPackage == null && pmAgent != null) {
3216 mAncestralPackages = pmAgent.getRestoredPackages();
Christopher Tate84725812010-02-04 15:52:40 -08003217 mAncestralToken = mToken;
3218 writeRestoreTokens();
3219 }
3220
Christopher Tate1bb69062010-02-19 17:02:12 -08003221 // We must under all circumstances tell the Package Manager to
3222 // proceed with install notifications if it's waiting for us.
3223 if (mPmToken > 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003224 if (DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
Christopher Tate1bb69062010-02-19 17:02:12 -08003225 try {
3226 mPackageManagerBinder.finishPackageInstall(mPmToken);
3227 } catch (RemoteException e) { /* can't happen */ }
3228 }
3229
Christopher Tate73a3cb32010-12-13 18:27:26 -08003230 // Furthermore we need to reset the session timeout clock
3231 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
3232 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
3233 TIMEOUT_RESTORE_INTERVAL);
3234
Christopher Tateb6787f22009-07-02 17:40:45 -07003235 // done; we can finally release the wakelock
3236 mWakelock.release();
Christopher Tatedf01dea2009-06-09 20:45:02 -07003237 }
3238 }
3239
Dan Egnorefe52642009-06-24 00:16:33 -07003240 // Do the guts of a restore of one application, using mTransport.getRestoreData().
Chris Tate249345b2010-10-29 12:57:04 -07003241 void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
3242 boolean needFullBackup) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003243 // !!! TODO: actually run the restore through mTransport
Christopher Tatec7b31e32009-06-10 15:49:30 -07003244 final String packageName = app.packageName;
3245
Joe Onorato8a9b2202010-02-26 18:56:32 -08003246 if (DEBUG) Slog.d(TAG, "processOneRestore packageName=" + packageName);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04003247
Christopher Tatec7b31e32009-06-10 15:49:30 -07003248 // !!! TODO: get the dirs from the transport
3249 File backupDataName = new File(mDataDir, packageName + ".restore");
Dan Egnorbb9001c2009-07-27 12:20:13 -07003250 File newStateName = new File(mStateDir, packageName + ".new");
3251 File savedStateName = new File(mStateDir, packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003252
Dan Egnorbb9001c2009-07-27 12:20:13 -07003253 ParcelFileDescriptor backupData = null;
3254 ParcelFileDescriptor newState = null;
3255
Christopher Tate4a627c72011-04-01 14:43:32 -07003256 final int token = generateToken();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003257 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07003258 // Run the transport's restore pass
Dan Egnorbb9001c2009-07-27 12:20:13 -07003259 backupData = ParcelFileDescriptor.open(backupDataName,
3260 ParcelFileDescriptor.MODE_READ_WRITE |
3261 ParcelFileDescriptor.MODE_CREATE |
3262 ParcelFileDescriptor.MODE_TRUNCATE);
3263
Dan Egnor01445162009-09-21 17:04:05 -07003264 if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003265 Slog.e(TAG, "Error getting restore data for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003266 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003267 return;
Christopher Tatec7b31e32009-06-10 15:49:30 -07003268 }
3269
3270 // Okay, we have the data. Now have the agent do the restore.
Dan Egnorbb9001c2009-07-27 12:20:13 -07003271 backupData.close();
Christopher Tatec7b31e32009-06-10 15:49:30 -07003272 backupData = ParcelFileDescriptor.open(backupDataName,
3273 ParcelFileDescriptor.MODE_READ_ONLY);
3274
Dan Egnorbb9001c2009-07-27 12:20:13 -07003275 newState = ParcelFileDescriptor.open(newStateName,
3276 ParcelFileDescriptor.MODE_READ_WRITE |
3277 ParcelFileDescriptor.MODE_CREATE |
3278 ParcelFileDescriptor.MODE_TRUNCATE);
3279
Christopher Tate44a27902010-01-27 17:15:49 -08003280 // Kick off the restore, checking for hung agents
3281 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL);
3282 agent.doRestore(backupData, appVersionCode, newState, token, mBackupManagerBinder);
3283 boolean success = waitUntilOperationComplete(token);
3284
3285 if (!success) {
3286 throw new RuntimeException("restore timeout");
3287 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003288
3289 // if everything went okay, remember the recorded state now
Christopher Tate90967f42009-09-20 15:28:33 -07003290 //
3291 // !!! TODO: the restored data should be migrated on the server
3292 // side into the current dataset. In that case the new state file
3293 // we just created would reflect the data already extant in the
3294 // backend, so there'd be nothing more to do. Until that happens,
3295 // however, we need to make sure that we record the data to the
3296 // current backend dataset. (Yes, this means shipping the data over
3297 // the wire in both directions. That's bad, but consistency comes
3298 // first, then efficiency.) Once we introduce server-side data
3299 // migration to the newly-restored device's dataset, we will change
3300 // the following from a discard of the newly-written state to the
3301 // "correct" operation of renaming into the canonical state blob.
3302 newStateName.delete(); // TODO: remove; see above comment
3303 //newStateName.renameTo(savedStateName); // TODO: replace with this
3304
Dan Egnorbb9001c2009-07-27 12:20:13 -07003305 int size = (int) backupDataName.length();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003306 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, packageName, size);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003307 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003308 Slog.e(TAG, "Error restoring data for " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003309 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
Dan Egnorbb9001c2009-07-27 12:20:13 -07003310
Christopher Tate96733042009-07-20 14:49:13 -07003311 // If the agent fails restore, it might have put the app's data
3312 // into an incoherent state. For consistency we wipe its data
3313 // again in this case before propagating the exception
Christopher Tate96733042009-07-20 14:49:13 -07003314 clearApplicationDataSynchronous(packageName);
Christopher Tate1531dc82009-07-24 16:37:43 -07003315 } finally {
3316 backupDataName.delete();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003317 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
3318 try { if (newState != null) newState.close(); } catch (IOException e) {}
3319 backupData = newState = null;
Christopher Tate4a627c72011-04-01 14:43:32 -07003320 synchronized (mCurrentOperations) {
3321 mCurrentOperations.delete(token);
3322 }
Chris Tate249345b2010-10-29 12:57:04 -07003323
3324 // If we know a priori that we'll need to perform a full post-restore backup
3325 // pass, clear the new state file data. This means we're discarding work that
3326 // was just done by the app's agent, but this way the agent doesn't need to
3327 // take any special action based on global device state.
3328 if (needFullBackup) {
3329 newStateName.delete();
3330 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003331 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003332 }
3333 }
3334
Christopher Tate44a27902010-01-27 17:15:49 -08003335 class PerformClearTask implements Runnable {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003336 IBackupTransport mTransport;
3337 PackageInfo mPackage;
3338
Christopher Tate44a27902010-01-27 17:15:49 -08003339 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003340 mTransport = transport;
3341 mPackage = packageInfo;
3342 }
3343
Christopher Tateee0e78a2009-07-02 11:17:03 -07003344 public void run() {
3345 try {
3346 // Clear the on-device backup state to ensure a full backup next time
3347 File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
3348 File stateFile = new File(stateDir, mPackage.packageName);
3349 stateFile.delete();
3350
3351 // Tell the transport to remove all the persistent storage for the app
Christopher Tate13f4a642009-09-30 20:06:45 -07003352 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003353 mTransport.clearBackupData(mPackage);
3354 } catch (RemoteException e) {
3355 // can't happen; the transport is local
3356 } finally {
3357 try {
Christopher Tate13f4a642009-09-30 20:06:45 -07003358 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003359 mTransport.finishBackup();
3360 } catch (RemoteException e) {
3361 // can't happen; the transport is local
3362 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003363
3364 // Last but not least, release the cpu
3365 mWakelock.release();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003366 }
3367 }
3368 }
3369
Christopher Tate44a27902010-01-27 17:15:49 -08003370 class PerformInitializeTask implements Runnable {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003371 HashSet<String> mQueue;
3372
Christopher Tate44a27902010-01-27 17:15:49 -08003373 PerformInitializeTask(HashSet<String> transportNames) {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003374 mQueue = transportNames;
3375 }
3376
Christopher Tate4cc86e12009-09-21 19:36:51 -07003377 public void run() {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003378 try {
3379 for (String transportName : mQueue) {
3380 IBackupTransport transport = getTransport(transportName);
3381 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003382 Slog.e(TAG, "Requested init for " + transportName + " but not found");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003383 continue;
3384 }
3385
Joe Onorato8a9b2202010-02-26 18:56:32 -08003386 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003387 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
Dan Egnor726247c2009-09-29 19:12:31 -07003388 long startRealtime = SystemClock.elapsedRealtime();
3389 int status = transport.initializeDevice();
Christopher Tate4cc86e12009-09-21 19:36:51 -07003390
Christopher Tate4cc86e12009-09-21 19:36:51 -07003391 if (status == BackupConstants.TRANSPORT_OK) {
3392 status = transport.finishBackup();
3393 }
3394
3395 // Okay, the wipe really happened. Clean up our local bookkeeping.
3396 if (status == BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003397 Slog.i(TAG, "Device init successful");
Dan Egnor726247c2009-09-29 19:12:31 -07003398 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003399 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07003400 resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003401 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003402 synchronized (mQueueLock) {
3403 recordInitPendingLocked(false, transportName);
3404 }
Dan Egnor726247c2009-09-29 19:12:31 -07003405 } else {
3406 // If this didn't work, requeue this one and try again
3407 // after a suitable interval
Joe Onorato8a9b2202010-02-26 18:56:32 -08003408 Slog.e(TAG, "Transport error in initializeDevice()");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003409 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003410 synchronized (mQueueLock) {
3411 recordInitPendingLocked(true, transportName);
3412 }
3413 // do this via another alarm to make sure of the wakelock states
3414 long delay = transport.requestBackupTime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08003415 if (DEBUG) Slog.w(TAG, "init failed on "
Christopher Tate4cc86e12009-09-21 19:36:51 -07003416 + transportName + " resched in " + delay);
3417 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
3418 System.currentTimeMillis() + delay, mRunInitIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003419 }
Christopher Tate4cc86e12009-09-21 19:36:51 -07003420 }
3421 } catch (RemoteException e) {
3422 // can't happen; the transports are local
3423 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003424 Slog.e(TAG, "Unexpected error performing init", e);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003425 } finally {
Christopher Tatec2af5d32010-02-02 15:18:58 -08003426 // Done; release the wakelock
Christopher Tate4cc86e12009-09-21 19:36:51 -07003427 mWakelock.release();
3428 }
3429 }
3430 }
3431
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003432 private void dataChangedImpl(String packageName) {
3433 HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3434 dataChangedImpl(packageName, targets);
3435 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003436
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003437 private void dataChangedImpl(String packageName, HashSet<ApplicationInfo> targets) {
Christopher Tate487529a2009-04-29 14:03:25 -07003438 // Record that we need a backup pass for the caller. Since multiple callers
3439 // may share a uid, we need to note all candidates within that uid and schedule
3440 // a backup pass for each of them.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003441 EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003442
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003443 if (targets == null) {
3444 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3445 + " uid=" + Binder.getCallingUid());
3446 return;
3447 }
3448
3449 synchronized (mQueueLock) {
3450 // Note that this client has made data changes that need to be backed up
3451 for (ApplicationInfo app : targets) {
3452 // validate the caller-supplied package name against the known set of
3453 // packages associated with this uid
3454 if (app.packageName.equals(packageName)) {
3455 // Add the caller to the set of pending backups. If there is
3456 // one already there, then overwrite it, but no harm done.
Christopher Tate4a627c72011-04-01 14:43:32 -07003457 BackupRequest req = new BackupRequest(app);
Christopher Tatec28083a2010-12-14 16:16:44 -08003458 if (mPendingBackups.put(app.packageName, req) == null) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003459 // Journal this request in case of crash. The put()
3460 // operation returned null when this package was not already
3461 // in the set; we want to avoid touching the disk redundantly.
3462 writeToJournalLocked(packageName);
3463
3464 if (DEBUG) {
3465 int numKeys = mPendingBackups.size();
3466 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
3467 for (BackupRequest b : mPendingBackups.values()) {
3468 Slog.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
3469 }
3470 }
3471 }
3472 }
3473 }
3474 }
3475 }
3476
3477 // Note: packageName is currently unused, but may be in the future
3478 private HashSet<ApplicationInfo> dataChangedTargets(String packageName) {
Christopher Tate63d27002009-06-16 17:16:42 -07003479 // If the caller does not hold the BACKUP permission, it can only request a
3480 // backup of its own data.
Dianne Hackborncf098292009-07-01 19:55:20 -07003481 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tate63d27002009-06-16 17:16:42 -07003482 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003483 synchronized (mBackupParticipants) {
3484 return mBackupParticipants.get(Binder.getCallingUid());
3485 }
3486 }
3487
3488 // a caller with full permission can ask to back up any participating app
3489 // !!! TODO: allow backup of ANY app?
3490 HashSet<ApplicationInfo> targets = new HashSet<ApplicationInfo>();
3491 synchronized (mBackupParticipants) {
Christopher Tate63d27002009-06-16 17:16:42 -07003492 int N = mBackupParticipants.size();
3493 for (int i = 0; i < N; i++) {
3494 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3495 if (s != null) {
3496 targets.addAll(s);
3497 }
3498 }
3499 }
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003500 return targets;
Christopher Tate487529a2009-04-29 14:03:25 -07003501 }
Christopher Tate46758122009-05-06 11:22:00 -07003502
Christopher Tatecde87f42009-06-12 12:55:53 -07003503 private void writeToJournalLocked(String str) {
Dan Egnor852f8e42009-09-30 11:20:45 -07003504 RandomAccessFile out = null;
3505 try {
3506 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
3507 out = new RandomAccessFile(mJournal, "rws");
3508 out.seek(out.length());
3509 out.writeUTF(str);
3510 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003511 Slog.e(TAG, "Can't write " + str + " to backup journal", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07003512 mJournal = null;
3513 } finally {
3514 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tatecde87f42009-06-12 12:55:53 -07003515 }
3516 }
3517
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003518 // ----- IBackupManager binder interface -----
3519
3520 public void dataChanged(final String packageName) {
3521 final HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3522 if (targets == null) {
3523 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3524 + " uid=" + Binder.getCallingUid());
3525 return;
3526 }
3527
3528 mBackupHandler.post(new Runnable() {
3529 public void run() {
3530 dataChangedImpl(packageName, targets);
3531 }
3532 });
3533 }
3534
Christopher Tateee0e78a2009-07-02 11:17:03 -07003535 // Clear the given package's backup data from the current transport
3536 public void clearBackupData(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003537 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003538 PackageInfo info;
3539 try {
3540 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
3541 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003542 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003543 return;
3544 }
3545
3546 // If the caller does not hold the BACKUP permission, it can only request a
3547 // wipe of its own backed-up data.
3548 HashSet<ApplicationInfo> apps;
Christopher Tate4e3e50c2009-07-02 12:14:05 -07003549 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tateee0e78a2009-07-02 11:17:03 -07003550 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
3551 apps = mBackupParticipants.get(Binder.getCallingUid());
3552 } else {
3553 // a caller with full permission can ask to back up any participating app
3554 // !!! TODO: allow data-clear of ANY app?
Joe Onorato8a9b2202010-02-26 18:56:32 -08003555 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003556 apps = new HashSet<ApplicationInfo>();
3557 int N = mBackupParticipants.size();
3558 for (int i = 0; i < N; i++) {
3559 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3560 if (s != null) {
3561 apps.addAll(s);
3562 }
3563 }
3564 }
3565
3566 // now find the given package in the set of candidate apps
3567 for (ApplicationInfo app : apps) {
3568 if (app.packageName.equals(packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003569 if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003570 // found it; fire off the clear request
3571 synchronized (mQueueLock) {
Christopher Tateaa93b042009-08-05 18:21:40 -07003572 long oldId = Binder.clearCallingIdentity();
Christopher Tateb6787f22009-07-02 17:40:45 -07003573 mWakelock.acquire();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003574 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
3575 new ClearParams(getTransport(mCurrentTransport), info));
3576 mBackupHandler.sendMessage(msg);
Christopher Tateaa93b042009-08-05 18:21:40 -07003577 Binder.restoreCallingIdentity(oldId);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003578 }
3579 break;
3580 }
3581 }
3582 }
3583
Christopher Tateace7f092009-06-15 18:07:25 -07003584 // Run a backup pass immediately for any applications that have declared
3585 // that they have pending updates.
Dan Egnor852f8e42009-09-30 11:20:45 -07003586 public void backupNow() {
Joe Onorato5933a492009-07-23 18:24:08 -04003587 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
Christopher Tate043dadc2009-06-02 16:11:00 -07003588
Joe Onorato8a9b2202010-02-26 18:56:32 -08003589 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
Christopher Tate46758122009-05-06 11:22:00 -07003590 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07003591 // Because the alarms we are using can jitter, and we want an *immediate*
3592 // backup pass to happen, we restart the timer beginning with "next time,"
3593 // then manually fire the backup trigger intent ourselves.
3594 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tateb6787f22009-07-02 17:40:45 -07003595 try {
Christopher Tateb6787f22009-07-02 17:40:45 -07003596 mRunBackupIntent.send();
3597 } catch (PendingIntent.CanceledException e) {
3598 // should never happen
Joe Onorato8a9b2202010-02-26 18:56:32 -08003599 Slog.e(TAG, "run-backup intent cancelled!");
Christopher Tateb6787f22009-07-02 17:40:45 -07003600 }
Christopher Tate46758122009-05-06 11:22:00 -07003601 }
3602 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003603
Christopher Tate4a627c72011-04-01 14:43:32 -07003604 // Run a *full* backup pass for the given package, writing the resulting data stream
3605 // to the supplied file descriptor. This method is synchronous and does not return
3606 // to the caller until the backup has been completed.
3607 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
3608 boolean doAllApps, String[] pkgList) {
3609 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3610
3611 // Validate
3612 if (!doAllApps) {
3613 if (!includeShared) {
3614 // If we're backing up shared data (sdcard or equivalent), then we can run
3615 // without any supplied app names. Otherwise, we'd be doing no work, so
3616 // report the error.
3617 if (pkgList == null || pkgList.length == 0) {
3618 throw new IllegalArgumentException(
3619 "Backup requested but neither shared nor any apps named");
3620 }
3621 }
3622 }
3623
3624 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
3625 + " shared=" + includeShared + " all=" + doAllApps
3626 + " pkgs=" + pkgList);
3627
3628 long oldId = Binder.clearCallingIdentity();
3629 try {
3630 FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
3631 doAllApps, pkgList);
3632 final int token = generateToken();
3633 synchronized (mFullConfirmations) {
3634 mFullConfirmations.put(token, params);
3635 }
3636
Christopher Tate75a99702011-05-18 16:28:19 -07003637 // start up the confirmation UI
3638 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
3639 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
3640 Slog.e(TAG, "Unable to launch full backup confirmation");
Christopher Tate4a627c72011-04-01 14:43:32 -07003641 mFullConfirmations.delete(token);
3642 return;
3643 }
Christopher Tate75a99702011-05-18 16:28:19 -07003644
3645 // make sure the screen is lit for the user interaction
Christopher Tate4a627c72011-04-01 14:43:32 -07003646 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3647
3648 // start the confirmation countdown
Christopher Tate75a99702011-05-18 16:28:19 -07003649 startConfirmationTimeout(token, params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003650
3651 // wait for the backup to be performed
3652 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
3653 waitForCompletion(params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003654 } finally {
Christopher Tate4a627c72011-04-01 14:43:32 -07003655 try {
3656 fd.close();
3657 } catch (IOException e) {
3658 // just eat it
3659 }
Christopher Tate75a99702011-05-18 16:28:19 -07003660 Binder.restoreCallingIdentity(oldId);
Christopher Tate4a627c72011-04-01 14:43:32 -07003661 }
Christopher Tate75a99702011-05-18 16:28:19 -07003662 if (DEBUG) Slog.d(TAG, "Full backup done; returning to caller");
3663 }
3664
3665 public void fullRestore(ParcelFileDescriptor fd) {
3666 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3667 Slog.i(TAG, "Beginning full restore...");
3668
3669 long oldId = Binder.clearCallingIdentity();
3670
3671 try {
3672 FullRestoreParams params = new FullRestoreParams(fd);
3673 final int token = generateToken();
3674 synchronized (mFullConfirmations) {
3675 mFullConfirmations.put(token, params);
3676 }
3677
3678 // start up the confirmation UI
3679 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
3680 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
3681 Slog.e(TAG, "Unable to launch full restore confirmation");
3682 mFullConfirmations.delete(token);
3683 return;
3684 }
3685
3686 // make sure the screen is lit for the user interaction
3687 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3688
3689 // start the confirmation countdown
3690 startConfirmationTimeout(token, params);
3691
3692 // wait for the restore to be performed
3693 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
3694 waitForCompletion(params);
3695 } finally {
3696 try {
3697 fd.close();
3698 } catch (IOException e) {
3699 Slog.w(TAG, "Error trying to close fd after full restore: " + e);
3700 }
3701 Binder.restoreCallingIdentity(oldId);
3702 Slog.i(TAG, "Full restore completed");
3703 }
3704 }
3705
3706 boolean startConfirmationUi(int token, String action) {
3707 try {
3708 Intent confIntent = new Intent(action);
3709 confIntent.setClassName("com.android.backupconfirm",
3710 "com.android.backupconfirm.BackupRestoreConfirmation");
3711 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
3712 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3713 mContext.startActivity(confIntent);
3714 } catch (ActivityNotFoundException e) {
3715 return false;
3716 }
3717 return true;
3718 }
3719
3720 void startConfirmationTimeout(int token, FullParams params) {
3721 if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
3722 + TIMEOUT_FULL_CONFIRMATION + " millis");
3723 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
3724 token, 0, params);
3725 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
Christopher Tate4a627c72011-04-01 14:43:32 -07003726 }
3727
3728 void waitForCompletion(FullParams params) {
3729 synchronized (params.latch) {
3730 while (params.latch.get() == false) {
3731 try {
3732 params.latch.wait();
3733 } catch (InterruptedException e) { /* never interrupted */ }
3734 }
3735 }
3736 }
3737
3738 void signalFullBackupRestoreCompletion(FullParams params) {
3739 synchronized (params.latch) {
3740 params.latch.set(true);
3741 params.latch.notifyAll();
3742 }
3743 }
3744
3745 // Confirm that the previously-requested full backup/restore operation can proceed. This
3746 // is used to require a user-facing disclosure about the operation.
3747 public void acknowledgeFullBackupOrRestore(int token, boolean allow,
3748 IFullBackupRestoreObserver observer) {
3749 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
3750 + " allow=" + allow);
3751
3752 // TODO: possibly require not just this signature-only permission, but even
3753 // require that the specific designated confirmation-UI app uid is the caller?
3754 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3755
3756 long oldId = Binder.clearCallingIdentity();
3757 try {
3758
3759 FullParams params;
3760 synchronized (mFullConfirmations) {
3761 params = mFullConfirmations.get(token);
3762 if (params != null) {
3763 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
3764 mFullConfirmations.delete(token);
3765
3766 if (allow) {
3767 params.observer = observer;
3768 final int verb = params instanceof FullBackupParams
Christopher Tate75a99702011-05-18 16:28:19 -07003769 ? MSG_RUN_FULL_BACKUP
Christopher Tate4a627c72011-04-01 14:43:32 -07003770 : MSG_RUN_FULL_RESTORE;
3771
Christopher Tate75a99702011-05-18 16:28:19 -07003772 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
Christopher Tate4a627c72011-04-01 14:43:32 -07003773 mWakelock.acquire();
3774 Message msg = mBackupHandler.obtainMessage(verb, params);
3775 mBackupHandler.sendMessage(msg);
3776 } else {
3777 Slog.w(TAG, "User rejected full backup/restore operation");
3778 // indicate completion without having actually transferred any data
3779 signalFullBackupRestoreCompletion(params);
3780 }
3781 } else {
3782 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
3783 }
3784 }
3785 } finally {
3786 Binder.restoreCallingIdentity(oldId);
3787 }
3788 }
3789
Christopher Tate8031a3d2009-07-06 16:36:05 -07003790 // Enable/disable the backup service
Christopher Tate6ef58a12009-06-29 14:56:28 -07003791 public void setBackupEnabled(boolean enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003792 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4a627c72011-04-01 14:43:32 -07003793 "setBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07003794
Joe Onorato8a9b2202010-02-26 18:56:32 -08003795 Slog.i(TAG, "Backup enabled => " + enable);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003796
Christopher Tate6ef58a12009-06-29 14:56:28 -07003797 boolean wasEnabled = mEnabled;
3798 synchronized (this) {
Dianne Hackborncf098292009-07-01 19:55:20 -07003799 Settings.Secure.putInt(mContext.getContentResolver(),
3800 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
Christopher Tate6ef58a12009-06-29 14:56:28 -07003801 mEnabled = enable;
3802 }
3803
Christopher Tate49401dd2009-07-01 12:34:29 -07003804 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07003805 if (enable && !wasEnabled && mProvisioned) {
Christopher Tate49401dd2009-07-01 12:34:29 -07003806 // if we've just been enabled, start scheduling backup passes
Christopher Tate8031a3d2009-07-06 16:36:05 -07003807 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tate49401dd2009-07-01 12:34:29 -07003808 } else if (!enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003809 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08003810 if (DEBUG) Slog.i(TAG, "Opting out of backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003811
Christopher Tateb6787f22009-07-02 17:40:45 -07003812 mAlarmManager.cancel(mRunBackupIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003813
3814 // This also constitutes an opt-out, so we wipe any data for
3815 // this device from the backend. We start that process with
3816 // an alarm in order to guarantee wakelock states.
3817 if (wasEnabled && mProvisioned) {
3818 // NOTE: we currently flush every registered transport, not just
3819 // the currently-active one.
3820 HashSet<String> allTransports;
3821 synchronized (mTransports) {
3822 allTransports = new HashSet<String>(mTransports.keySet());
3823 }
3824 // build the set of transports for which we are posting an init
3825 for (String transport : allTransports) {
3826 recordInitPendingLocked(true, transport);
3827 }
3828 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
3829 mRunInitIntent);
3830 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07003831 }
3832 }
Christopher Tate49401dd2009-07-01 12:34:29 -07003833 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07003834
Christopher Tatecce9da52010-02-03 15:11:15 -08003835 // Enable/disable automatic restore of app data at install time
3836 public void setAutoRestore(boolean doAutoRestore) {
3837 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3838 "setBackupEnabled");
3839
Joe Onorato8a9b2202010-02-26 18:56:32 -08003840 Slog.i(TAG, "Auto restore => " + doAutoRestore);
Christopher Tatecce9da52010-02-03 15:11:15 -08003841
3842 synchronized (this) {
3843 Settings.Secure.putInt(mContext.getContentResolver(),
3844 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
3845 mAutoRestore = doAutoRestore;
3846 }
3847 }
3848
Christopher Tate8031a3d2009-07-06 16:36:05 -07003849 // Mark the backup service as having been provisioned
3850 public void setBackupProvisioned(boolean available) {
3851 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3852 "setBackupProvisioned");
3853
3854 boolean wasProvisioned = mProvisioned;
3855 synchronized (this) {
3856 Settings.Secure.putInt(mContext.getContentResolver(),
3857 Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
3858 mProvisioned = available;
3859 }
3860
3861 synchronized (mQueueLock) {
3862 if (available && !wasProvisioned && mEnabled) {
3863 // we're now good to go, so start the backup alarms
3864 startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
3865 } else if (!available) {
3866 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08003867 Slog.w(TAG, "Backup service no longer provisioned");
Christopher Tate8031a3d2009-07-06 16:36:05 -07003868 mAlarmManager.cancel(mRunBackupIntent);
3869 }
3870 }
3871 }
3872
3873 private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
Dan Egnorc1c49c02009-10-30 17:35:39 -07003874 // We used to use setInexactRepeating(), but that may be linked to
3875 // backups running at :00 more often than not, creating load spikes.
3876 // Schedule at an exact time for now, and also add a bit of "fuzz".
3877
3878 Random random = new Random();
3879 long when = System.currentTimeMillis() + delayBeforeFirstBackup +
3880 random.nextInt(FUZZ_MILLIS);
3881 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
3882 BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
Christopher Tate55f931a2009-09-29 17:17:34 -07003883 mNextBackupPass = when;
Christopher Tate8031a3d2009-07-06 16:36:05 -07003884 }
3885
Christopher Tate6ef58a12009-06-29 14:56:28 -07003886 // Report whether the backup mechanism is currently enabled
3887 public boolean isBackupEnabled() {
Joe Onorato5933a492009-07-23 18:24:08 -04003888 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07003889 return mEnabled; // no need to synchronize just to read it
3890 }
3891
Christopher Tate91717492009-06-26 21:07:13 -07003892 // Report the name of the currently active transport
3893 public String getCurrentTransport() {
Joe Onorato5933a492009-07-23 18:24:08 -04003894 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4e3e50c2009-07-02 12:14:05 -07003895 "getCurrentTransport");
Joe Onorato8a9b2202010-02-26 18:56:32 -08003896 if (DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -07003897 return mCurrentTransport;
Christopher Tateace7f092009-06-15 18:07:25 -07003898 }
3899
Christopher Tate91717492009-06-26 21:07:13 -07003900 // Report all known, available backup transports
3901 public String[] listAllTransports() {
Christopher Tate34ebd0e2009-07-06 15:44:54 -07003902 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
Christopher Tate043dadc2009-06-02 16:11:00 -07003903
Christopher Tate91717492009-06-26 21:07:13 -07003904 String[] list = null;
3905 ArrayList<String> known = new ArrayList<String>();
3906 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
3907 if (entry.getValue() != null) {
3908 known.add(entry.getKey());
3909 }
3910 }
3911
3912 if (known.size() > 0) {
3913 list = new String[known.size()];
3914 known.toArray(list);
3915 }
3916 return list;
3917 }
3918
3919 // Select which transport to use for the next backup operation. If the given
3920 // name is not one of the available transports, no action is taken and the method
3921 // returns null.
3922 public String selectBackupTransport(String transport) {
Joe Onorato5933a492009-07-23 18:24:08 -04003923 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
Christopher Tate91717492009-06-26 21:07:13 -07003924
3925 synchronized (mTransports) {
3926 String prevTransport = null;
3927 if (mTransports.get(transport) != null) {
3928 prevTransport = mCurrentTransport;
3929 mCurrentTransport = transport;
Dianne Hackborncf098292009-07-01 19:55:20 -07003930 Settings.Secure.putString(mContext.getContentResolver(),
3931 Settings.Secure.BACKUP_TRANSPORT, transport);
Joe Onorato8a9b2202010-02-26 18:56:32 -08003932 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
Christopher Tate91717492009-06-26 21:07:13 -07003933 + " returning " + prevTransport);
3934 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003935 Slog.w(TAG, "Attempt to select unavailable transport " + transport);
Christopher Tate91717492009-06-26 21:07:13 -07003936 }
3937 return prevTransport;
3938 }
Christopher Tate043dadc2009-06-02 16:11:00 -07003939 }
3940
Christopher Tatef5e1c292010-12-08 18:40:26 -08003941 // Supply the configuration Intent for the given transport. If the name is not one
3942 // of the available transports, or if the transport does not supply any configuration
3943 // UI, the method returns null.
3944 public Intent getConfigurationIntent(String transportName) {
3945 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3946 "getConfigurationIntent");
3947
3948 synchronized (mTransports) {
3949 final IBackupTransport transport = mTransports.get(transportName);
3950 if (transport != null) {
3951 try {
3952 final Intent intent = transport.configurationIntent();
3953 if (DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
3954 + intent);
3955 return intent;
3956 } catch (RemoteException e) {
3957 /* fall through to return null */
3958 }
3959 }
3960 }
3961
3962 return null;
3963 }
3964
3965 // Supply the configuration summary string for the given transport. If the name is
3966 // not one of the available transports, or if the transport does not supply any
3967 // summary / destination string, the method can return null.
3968 //
3969 // This string is used VERBATIM as the summary text of the relevant Settings item!
3970 public String getDestinationString(String transportName) {
3971 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3972 "getConfigurationIntent");
3973
3974 synchronized (mTransports) {
3975 final IBackupTransport transport = mTransports.get(transportName);
3976 if (transport != null) {
3977 try {
3978 final String text = transport.currentDestinationString();
3979 if (DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
3980 return text;
3981 } catch (RemoteException e) {
3982 /* fall through to return null */
3983 }
3984 }
3985 }
3986
3987 return null;
3988 }
3989
Christopher Tate043dadc2009-06-02 16:11:00 -07003990 // Callback: a requested backup agent has been instantiated. This should only
3991 // be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07003992 public void agentConnected(String packageName, IBinder agentBinder) {
Christopher Tate043dadc2009-06-02 16:11:00 -07003993 synchronized(mAgentConnectLock) {
3994 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003995 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
Christopher Tate043dadc2009-06-02 16:11:00 -07003996 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
3997 mConnectedAgent = agent;
3998 mConnecting = false;
3999 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004000 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07004001 + " claiming agent connected");
4002 }
4003 mAgentConnectLock.notifyAll();
4004 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004005 }
4006
4007 // Callback: a backup agent has failed to come up, or has unexpectedly quit.
4008 // If the agent failed to come up in the first place, the agentBinder argument
Christopher Tate043dadc2009-06-02 16:11:00 -07004009 // will be null. This should only be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07004010 public void agentDisconnected(String packageName) {
4011 // TODO: handle backup being interrupted
Christopher Tate043dadc2009-06-02 16:11:00 -07004012 synchronized(mAgentConnectLock) {
4013 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
4014 mConnectedAgent = null;
4015 mConnecting = false;
4016 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004017 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07004018 + " claiming agent disconnected");
4019 }
4020 mAgentConnectLock.notifyAll();
4021 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004022 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004023
Christopher Tate1bb69062010-02-19 17:02:12 -08004024 // An application being installed will need a restore pass, then the Package Manager
4025 // will need to be told when the restore is finished.
4026 public void restoreAtInstall(String packageName, int token) {
4027 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004028 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate1bb69062010-02-19 17:02:12 -08004029 + " attemping install-time restore");
4030 return;
4031 }
4032
4033 long restoreSet = getAvailableRestoreToken(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08004034 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
Christopher Tate1bb69062010-02-19 17:02:12 -08004035 + " token=" + Integer.toHexString(token));
4036
Christopher Tatef0872722010-02-25 15:22:48 -08004037 if (mAutoRestore && mProvisioned && restoreSet != 0) {
Christopher Tate1bb69062010-02-19 17:02:12 -08004038 // okay, we're going to attempt a restore of this package from this restore set.
4039 // The eventual message back into the Package Manager to run the post-install
4040 // steps for 'token' will be issued from the restore handling code.
4041
4042 // We can use a synthetic PackageInfo here because:
4043 // 1. We know it's valid, since the Package Manager supplied the name
4044 // 2. Only the packageName field will be used by the restore code
4045 PackageInfo pkg = new PackageInfo();
4046 pkg.packageName = packageName;
4047
4048 mWakelock.acquire();
4049 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
4050 msg.obj = new RestoreParams(getTransport(mCurrentTransport), null,
Chris Tate249345b2010-10-29 12:57:04 -07004051 restoreSet, pkg, token, true);
Christopher Tate1bb69062010-02-19 17:02:12 -08004052 mBackupHandler.sendMessage(msg);
4053 } else {
Christopher Tatef0872722010-02-25 15:22:48 -08004054 // Auto-restore disabled or no way to attempt a restore; just tell the Package
4055 // Manager to proceed with the post-install handling for this package.
Joe Onorato8a9b2202010-02-26 18:56:32 -08004056 if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore");
Christopher Tate1bb69062010-02-19 17:02:12 -08004057 try {
4058 mPackageManagerBinder.finishPackageInstall(token);
4059 } catch (RemoteException e) { /* can't happen */ }
4060 }
4061 }
4062
Christopher Tate8c850b72009-06-07 19:33:20 -07004063 // Hand off a restore session
Chris Tate44ab8452010-11-16 15:10:49 -08004064 public IRestoreSession beginRestoreSession(String packageName, String transport) {
4065 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
4066 + " transport=" + transport);
4067
4068 boolean needPermission = true;
4069 if (transport == null) {
4070 transport = mCurrentTransport;
4071
4072 if (packageName != null) {
4073 PackageInfo app = null;
4074 try {
4075 app = mPackageManager.getPackageInfo(packageName, 0);
4076 } catch (NameNotFoundException nnf) {
4077 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
4078 throw new IllegalArgumentException("Package " + packageName + " not found");
4079 }
4080
4081 if (app.applicationInfo.uid == Binder.getCallingUid()) {
4082 // So: using the current active transport, and the caller has asked
4083 // that its own package will be restored. In this narrow use case
4084 // we do not require the caller to hold the permission.
4085 needPermission = false;
4086 }
4087 }
4088 }
4089
4090 if (needPermission) {
4091 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4092 "beginRestoreSession");
4093 } else {
4094 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
4095 }
Christopher Tatef68eb502009-06-16 11:02:01 -07004096
4097 synchronized(this) {
4098 if (mActiveRestoreSession != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004099 Slog.d(TAG, "Restore session requested but one already active");
Christopher Tatef68eb502009-06-16 11:02:01 -07004100 return null;
4101 }
Chris Tate44ab8452010-11-16 15:10:49 -08004102 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
Christopher Tate73a3cb32010-12-13 18:27:26 -08004103 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
Christopher Tatef68eb502009-06-16 11:02:01 -07004104 }
4105 return mActiveRestoreSession;
Christopher Tate8c850b72009-06-07 19:33:20 -07004106 }
Christopher Tate043dadc2009-06-02 16:11:00 -07004107
Christopher Tate73a3cb32010-12-13 18:27:26 -08004108 void clearRestoreSession(ActiveRestoreSession currentSession) {
4109 synchronized(this) {
4110 if (currentSession != mActiveRestoreSession) {
4111 Slog.e(TAG, "ending non-current restore session");
4112 } else {
4113 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
4114 mActiveRestoreSession = null;
4115 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
4116 }
4117 }
4118 }
4119
Christopher Tate44a27902010-01-27 17:15:49 -08004120 // Note that a currently-active backup agent has notified us that it has
4121 // completed the given outstanding asynchronous backup/restore operation.
4122 public void opComplete(int token) {
4123 synchronized (mCurrentOpLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004124 if (DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
Christopher Tate44a27902010-01-27 17:15:49 -08004125 mCurrentOperations.put(token, OP_ACKNOWLEDGED);
4126 mCurrentOpLock.notifyAll();
4127 }
4128 }
4129
Christopher Tate9b3905c2009-06-08 15:24:01 -07004130 // ----- Restore session -----
4131
Christopher Tate80202c82010-01-25 19:37:47 -08004132 class ActiveRestoreSession extends IRestoreSession.Stub {
Christopher Tatef68eb502009-06-16 11:02:01 -07004133 private static final String TAG = "RestoreSession";
4134
Chris Tate44ab8452010-11-16 15:10:49 -08004135 private String mPackageName;
Christopher Tate9b3905c2009-06-08 15:24:01 -07004136 private IBackupTransport mRestoreTransport = null;
4137 RestoreSet[] mRestoreSets = null;
Christopher Tate73a3cb32010-12-13 18:27:26 -08004138 boolean mEnded = false;
Christopher Tate9b3905c2009-06-08 15:24:01 -07004139
Chris Tate44ab8452010-11-16 15:10:49 -08004140 ActiveRestoreSession(String packageName, String transport) {
4141 mPackageName = packageName;
Christopher Tate91717492009-06-26 21:07:13 -07004142 mRestoreTransport = getTransport(transport);
Christopher Tate9b3905c2009-06-08 15:24:01 -07004143 }
4144
4145 // --- Binder interface ---
Christopher Tate2d449afe2010-03-29 19:14:24 -07004146 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
Joe Onorato5933a492009-07-23 18:24:08 -04004147 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004148 "getAvailableRestoreSets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004149 if (observer == null) {
4150 throw new IllegalArgumentException("Observer must not be null");
4151 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004152
Christopher Tate73a3cb32010-12-13 18:27:26 -08004153 if (mEnded) {
4154 throw new IllegalStateException("Restore session already ended");
4155 }
4156
Christopher Tate1bb69062010-02-19 17:02:12 -08004157 long oldId = Binder.clearCallingIdentity();
Christopher Tatef68eb502009-06-16 11:02:01 -07004158 try {
Christopher Tate43383042009-07-13 15:17:13 -07004159 if (mRestoreTransport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004160 Slog.w(TAG, "Null transport getting restore sets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004161 return -1;
Dan Egnor0084da52009-07-29 12:57:16 -07004162 }
Christopher Tate2d449afe2010-03-29 19:14:24 -07004163 // spin off the transport request to our service thread
4164 mWakelock.acquire();
4165 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
4166 new RestoreGetSetsParams(mRestoreTransport, this, observer));
4167 mBackupHandler.sendMessage(msg);
4168 return 0;
Dan Egnor0084da52009-07-29 12:57:16 -07004169 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004170 Slog.e(TAG, "Error in getAvailableRestoreSets", e);
Christopher Tate2d449afe2010-03-29 19:14:24 -07004171 return -1;
Christopher Tate1bb69062010-02-19 17:02:12 -08004172 } finally {
4173 Binder.restoreCallingIdentity(oldId);
Christopher Tatef68eb502009-06-16 11:02:01 -07004174 }
Christopher Tate9b3905c2009-06-08 15:24:01 -07004175 }
4176
Christopher Tate84725812010-02-04 15:52:40 -08004177 public synchronized int restoreAll(long token, IRestoreObserver observer) {
Dan Egnor0084da52009-07-29 12:57:16 -07004178 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4179 "performRestore");
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004180
Chris Tate44ab8452010-11-16 15:10:49 -08004181 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
Christopher Tatef2c321a2009-08-10 15:43:36 -07004182 + " observer=" + observer);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004183
Christopher Tate73a3cb32010-12-13 18:27:26 -08004184 if (mEnded) {
4185 throw new IllegalStateException("Restore session already ended");
4186 }
4187
Dan Egnor0084da52009-07-29 12:57:16 -07004188 if (mRestoreTransport == null || mRestoreSets == null) {
Chris Tate44ab8452010-11-16 15:10:49 -08004189 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
4190 return -1;
4191 }
4192
4193 if (mPackageName != null) {
4194 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
Dan Egnor0084da52009-07-29 12:57:16 -07004195 return -1;
4196 }
4197
Christopher Tate21ab6a52009-09-24 18:01:46 -07004198 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07004199 for (int i = 0; i < mRestoreSets.length; i++) {
4200 if (token == mRestoreSets[i].token) {
4201 long oldId = Binder.clearCallingIdentity();
Christopher Tate21ab6a52009-09-24 18:01:46 -07004202 mWakelock.acquire();
4203 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004204 msg.obj = new RestoreParams(mRestoreTransport, observer, token, true);
Christopher Tate21ab6a52009-09-24 18:01:46 -07004205 mBackupHandler.sendMessage(msg);
4206 Binder.restoreCallingIdentity(oldId);
4207 return 0;
4208 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004209 }
4210 }
Christopher Tate0e0b4ae2009-08-10 16:13:47 -07004211
Joe Onorato8a9b2202010-02-26 18:56:32 -08004212 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
Christopher Tate9b3905c2009-06-08 15:24:01 -07004213 return -1;
4214 }
4215
Christopher Tate84725812010-02-04 15:52:40 -08004216 public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004217 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
Christopher Tate84725812010-02-04 15:52:40 -08004218
Christopher Tate73a3cb32010-12-13 18:27:26 -08004219 if (mEnded) {
4220 throw new IllegalStateException("Restore session already ended");
4221 }
4222
Chris Tate44ab8452010-11-16 15:10:49 -08004223 if (mPackageName != null) {
4224 if (! mPackageName.equals(packageName)) {
4225 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
4226 + " on session for package " + mPackageName);
4227 return -1;
4228 }
4229 }
4230
Christopher Tate84725812010-02-04 15:52:40 -08004231 PackageInfo app = null;
4232 try {
4233 app = mPackageManager.getPackageInfo(packageName, 0);
4234 } catch (NameNotFoundException nnf) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004235 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004236 return -1;
4237 }
4238
4239 // If the caller is not privileged and is not coming from the target
4240 // app's uid, throw a permission exception back to the caller.
4241 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
4242 Binder.getCallingPid(), Binder.getCallingUid());
4243 if ((perm == PackageManager.PERMISSION_DENIED) &&
4244 (app.applicationInfo.uid != Binder.getCallingUid())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004245 Slog.w(TAG, "restorePackage: bad packageName=" + packageName
Christopher Tate84725812010-02-04 15:52:40 -08004246 + " or calling uid=" + Binder.getCallingUid());
4247 throw new SecurityException("No permission to restore other packages");
4248 }
4249
Christopher Tate7d411a32010-02-26 11:27:08 -08004250 // If the package has no backup agent, we obviously cannot proceed
4251 if (app.applicationInfo.backupAgentName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004252 Slog.w(TAG, "Asked to restore package " + packageName + " with no agent");
Christopher Tate7d411a32010-02-26 11:27:08 -08004253 return -1;
4254 }
4255
Christopher Tate84725812010-02-04 15:52:40 -08004256 // So far so good; we're allowed to try to restore this package. Now
4257 // check whether there is data for it in the current dataset, falling back
4258 // to the ancestral dataset if not.
Christopher Tate1bb69062010-02-19 17:02:12 -08004259 long token = getAvailableRestoreToken(packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004260
4261 // If we didn't come up with a place to look -- no ancestral dataset and
4262 // the app has never been backed up from this device -- there's nothing
4263 // to do but return failure.
4264 if (token == 0) {
Chris Tate44ab8452010-11-16 15:10:49 -08004265 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
Christopher Tate84725812010-02-04 15:52:40 -08004266 return -1;
4267 }
4268
4269 // Ready to go: enqueue the restore request and claim success
4270 long oldId = Binder.clearCallingIdentity();
4271 mWakelock.acquire();
4272 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004273 msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0, false);
Christopher Tate84725812010-02-04 15:52:40 -08004274 mBackupHandler.sendMessage(msg);
4275 Binder.restoreCallingIdentity(oldId);
4276 return 0;
4277 }
4278
Christopher Tate73a3cb32010-12-13 18:27:26 -08004279 // Posted to the handler to tear down a restore session in a cleanly synchronized way
4280 class EndRestoreRunnable implements Runnable {
4281 BackupManagerService mBackupManager;
4282 ActiveRestoreSession mSession;
4283
4284 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
4285 mBackupManager = manager;
4286 mSession = session;
4287 }
4288
4289 public void run() {
4290 // clean up the session's bookkeeping
4291 synchronized (mSession) {
4292 try {
4293 if (mSession.mRestoreTransport != null) {
4294 mSession.mRestoreTransport.finishRestore();
4295 }
4296 } catch (Exception e) {
4297 Slog.e(TAG, "Error in finishRestore", e);
4298 } finally {
4299 mSession.mRestoreTransport = null;
4300 mSession.mEnded = true;
4301 }
4302 }
4303
4304 // clean up the BackupManagerService side of the bookkeeping
4305 // and cancel any pending timeout message
4306 mBackupManager.clearRestoreSession(mSession);
4307 }
4308 }
4309
Dan Egnor0084da52009-07-29 12:57:16 -07004310 public synchronized void endRestoreSession() {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004311 if (DEBUG) Slog.d(TAG, "endRestoreSession");
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004312
Christopher Tate73a3cb32010-12-13 18:27:26 -08004313 if (mEnded) {
4314 throw new IllegalStateException("Restore session already ended");
Dan Egnor0084da52009-07-29 12:57:16 -07004315 }
4316
Christopher Tate73a3cb32010-12-13 18:27:26 -08004317 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
Christopher Tate9b3905c2009-06-08 15:24:01 -07004318 }
4319 }
4320
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004321 @Override
4322 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004323 long identityToken = Binder.clearCallingIdentity();
4324 try {
4325 dumpInternal(pw);
4326 } finally {
4327 Binder.restoreCallingIdentity(identityToken);
4328 }
4329 }
4330
4331 private void dumpInternal(PrintWriter pw) {
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004332 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07004333 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
Christopher Tate55f931a2009-09-29 17:17:34 -07004334 + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
Christopher Tatec2af5d32010-02-02 15:18:58 -08004335 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
Christopher Tateae06ed92010-02-25 17:13:28 -08004336 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
Christopher Tate55f931a2009-09-29 17:17:34 -07004337 pw.println("Last backup pass: " + mLastBackupPass
4338 + " (now = " + System.currentTimeMillis() + ')');
4339 pw.println(" next scheduled: " + mNextBackupPass);
4340
Christopher Tate91717492009-06-26 21:07:13 -07004341 pw.println("Available transports:");
4342 for (String t : listAllTransports()) {
Dan Egnor852f8e42009-09-30 11:20:45 -07004343 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
4344 try {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004345 IBackupTransport transport = getTransport(t);
4346 File dir = new File(mBaseStateDir, transport.transportDirName());
4347 pw.println(" destination: " + transport.currentDestinationString());
4348 pw.println(" intent: " + transport.configurationIntent());
Dan Egnor852f8e42009-09-30 11:20:45 -07004349 for (File f : dir.listFiles()) {
4350 pw.println(" " + f.getName() + " - " + f.length() + " state bytes");
4351 }
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004352 } catch (Exception e) {
4353 Slog.e(TAG, "Error in transport", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07004354 pw.println(" Error: " + e);
4355 }
Christopher Tate91717492009-06-26 21:07:13 -07004356 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004357
4358 pw.println("Pending init: " + mPendingInits.size());
4359 for (String s : mPendingInits) {
4360 pw.println(" " + s);
4361 }
4362
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004363 int N = mBackupParticipants.size();
Christopher Tate55f931a2009-09-29 17:17:34 -07004364 pw.println("Participants:");
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004365 for (int i=0; i<N; i++) {
4366 int uid = mBackupParticipants.keyAt(i);
4367 pw.print(" uid: ");
4368 pw.println(uid);
Christopher Tate181fafa2009-05-14 11:12:14 -07004369 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
4370 for (ApplicationInfo app: participants) {
Christopher Tate55f931a2009-09-29 17:17:34 -07004371 pw.println(" " + app.packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004372 }
4373 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004374
Christopher Tateb49ceb32010-02-08 16:22:24 -08004375 pw.println("Ancestral packages: "
4376 + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
Christopher Tate5923c972010-04-04 17:45:35 -07004377 if (mAncestralPackages != null) {
4378 for (String pkg : mAncestralPackages) {
4379 pw.println(" " + pkg);
4380 }
Christopher Tateb49ceb32010-02-08 16:22:24 -08004381 }
4382
Christopher Tate73e02522009-07-15 14:18:26 -07004383 pw.println("Ever backed up: " + mEverStoredApps.size());
4384 for (String pkg : mEverStoredApps) {
4385 pw.println(" " + pkg);
4386 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004387
4388 pw.println("Pending backup: " + mPendingBackups.size());
Christopher Tate6aa41f42009-06-19 14:14:22 -07004389 for (BackupRequest req : mPendingBackups.values()) {
Christopher Tate6ef58a12009-06-29 14:56:28 -07004390 pw.println(" " + req);
Christopher Tate181fafa2009-05-14 11:12:14 -07004391 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004392 }
4393 }
Christopher Tate487529a2009-04-29 14:03:25 -07004394}