blob: 3aa1239c8df9c42eafade25ed3a8ebfbc8d3f83b [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
Christopher Tatee9e78ec2011-06-08 20:09:31 -07001942
1943 @Override
1944 public String toString() {
1945 StringBuilder sb = new StringBuilder(128);
1946 sb.append("FileMetadata{");
1947 sb.append(packageName); sb.append(',');
1948 sb.append(type); sb.append(',');
1949 sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
1950 sb.append(size);
1951 sb.append('}');
1952 return sb.toString();
1953 }
Christopher Tate75a99702011-05-18 16:28:19 -07001954 }
1955
1956 enum RestorePolicy {
1957 IGNORE,
1958 ACCEPT,
1959 ACCEPT_IF_APK
1960 }
1961
1962 class PerformFullRestoreTask implements Runnable {
1963 ParcelFileDescriptor mInputFile;
1964 IFullBackupRestoreObserver mObserver;
1965 AtomicBoolean mLatchObject;
1966 IBackupAgent mAgent;
1967 String mAgentPackage;
1968 ApplicationInfo mTargetApp;
1969 ParcelFileDescriptor[] mPipes = null;
1970
Christopher Tatee9e78ec2011-06-08 20:09:31 -07001971 long mBytes;
1972
Christopher Tate75a99702011-05-18 16:28:19 -07001973 // possible handling states for a given package in the restore dataset
1974 final HashMap<String, RestorePolicy> mPackagePolicies
1975 = new HashMap<String, RestorePolicy>();
1976
1977 // installer package names for each encountered app, derived from the manifests
1978 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
1979
1980 // Signatures for a given package found in its manifest file
1981 final HashMap<String, Signature[]> mManifestSignatures
1982 = new HashMap<String, Signature[]>();
1983
1984 // Packages we've already wiped data on when restoring their first file
1985 final HashSet<String> mClearedPackages = new HashSet<String>();
1986
1987 PerformFullRestoreTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
1988 AtomicBoolean latch) {
1989 mInputFile = fd;
1990 mObserver = observer;
1991 mLatchObject = latch;
1992 mAgent = null;
1993 mAgentPackage = null;
1994 mTargetApp = null;
1995
1996 // Which packages we've already wiped data on. We prepopulate this
1997 // with a whitelist of packages known to be unclearable.
1998 mClearedPackages.add("android");
Christopher Tate75a99702011-05-18 16:28:19 -07001999 mClearedPackages.add("com.android.providers.settings");
Christopher Tateb0628bf2011-06-02 15:08:13 -07002000
Christopher Tate75a99702011-05-18 16:28:19 -07002001 }
2002
2003 class RestoreFileRunnable implements Runnable {
2004 IBackupAgent mAgent;
2005 FileMetadata mInfo;
2006 ParcelFileDescriptor mSocket;
2007 int mToken;
2008
2009 RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
2010 ParcelFileDescriptor socket, int token) throws IOException {
2011 mAgent = agent;
2012 mInfo = info;
2013 mToken = token;
2014
2015 // This class is used strictly for process-local binder invocations. The
2016 // semantics of ParcelFileDescriptor differ in this case; in particular, we
2017 // do not automatically get a 'dup'ed descriptor that we can can continue
2018 // to use asynchronously from the caller. So, we make sure to dup it ourselves
2019 // before proceeding to do the restore.
2020 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
2021 }
2022
2023 @Override
2024 public void run() {
2025 try {
2026 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
2027 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
2028 mToken, mBackupManagerBinder);
2029 } catch (RemoteException e) {
2030 // never happens; this is used strictly for local binder calls
2031 }
2032 }
2033 }
2034
2035 @Override
2036 public void run() {
2037 Slog.i(TAG, "--- Performing full-dataset restore ---");
2038 sendStartRestore();
2039
Christopher Tateb0628bf2011-06-02 15:08:13 -07002040 // Are we able to restore shared-storage data?
2041 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
2042 mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
2043 }
2044
Christopher Tate75a99702011-05-18 16:28:19 -07002045 try {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002046 mBytes = 0;
Christopher Tate75a99702011-05-18 16:28:19 -07002047 byte[] buffer = new byte[32 * 1024];
2048 FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
2049
2050 boolean didRestore;
2051 do {
2052 didRestore = restoreOneFile(instream, buffer);
2053 } while (didRestore);
2054
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002055 if (DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
Christopher Tate75a99702011-05-18 16:28:19 -07002056 } finally {
2057 tearDownPipes();
2058 tearDownAgent(mTargetApp);
2059
2060 try {
2061 mInputFile.close();
2062 } catch (IOException e) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002063 Slog.w(TAG, "Close of restore data pipe threw", e);
Christopher Tate75a99702011-05-18 16:28:19 -07002064 /* nothing we can do about this */
2065 }
2066 synchronized (mCurrentOpLock) {
2067 mCurrentOperations.clear();
2068 }
2069 synchronized (mLatchObject) {
2070 mLatchObject.set(true);
2071 mLatchObject.notifyAll();
2072 }
2073 sendEndRestore();
2074 mWakelock.release();
2075 if (DEBUG) Slog.d(TAG, "Full restore pass complete.");
2076 }
2077 }
2078
2079 boolean restoreOneFile(InputStream instream, byte[] buffer) {
2080 FileMetadata info;
2081 try {
2082 info = readTarHeaders(instream);
2083 if (info != null) {
2084 if (DEBUG) {
2085 dumpFileMetadata(info);
2086 }
2087
2088 final String pkg = info.packageName;
2089 if (!pkg.equals(mAgentPackage)) {
2090 // okay, change in package; set up our various
2091 // bookkeeping if we haven't seen it yet
2092 if (!mPackagePolicies.containsKey(pkg)) {
2093 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2094 }
2095
2096 // Clean up the previous agent relationship if necessary,
2097 // and let the observer know we're considering a new app.
2098 if (mAgent != null) {
2099 if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
2100 tearDownPipes();
2101 tearDownAgent(mTargetApp);
2102 mTargetApp = null;
2103 mAgentPackage = null;
2104 }
2105 }
2106
2107 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2108 mPackagePolicies.put(pkg, readAppManifest(info, instream));
2109 mPackageInstallers.put(pkg, info.installerPackageName);
2110 // We've read only the manifest content itself at this point,
2111 // so consume the footer before looping around to the next
2112 // input file
2113 skipTarPadding(info.size, instream);
2114 sendOnRestorePackage(pkg);
2115 } else {
2116 // Non-manifest, so it's actual file data. Is this a package
2117 // we're ignoring?
2118 boolean okay = true;
2119 RestorePolicy policy = mPackagePolicies.get(pkg);
2120 switch (policy) {
2121 case IGNORE:
2122 okay = false;
2123 break;
2124
2125 case ACCEPT_IF_APK:
2126 // If we're in accept-if-apk state, then the first file we
2127 // see MUST be the apk.
2128 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2129 if (DEBUG) Slog.d(TAG, "APK file; installing");
2130 // Try to install the app.
2131 String installerName = mPackageInstallers.get(pkg);
2132 okay = installApk(info, installerName, instream);
2133 // good to go; promote to ACCEPT
2134 mPackagePolicies.put(pkg, (okay)
2135 ? RestorePolicy.ACCEPT
2136 : RestorePolicy.IGNORE);
2137 // At this point we've consumed this file entry
2138 // ourselves, so just strip the tar footer and
2139 // go on to the next file in the input stream
2140 skipTarPadding(info.size, instream);
2141 return true;
2142 } else {
2143 // File data before (or without) the apk. We can't
2144 // handle it coherently in this case so ignore it.
2145 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2146 okay = false;
2147 }
2148 break;
2149
2150 case ACCEPT:
2151 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2152 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
2153 // we can take the data without the apk, so we
2154 // *want* to do so. skip the apk by declaring this
2155 // one file not-okay without changing the restore
2156 // policy for the package.
2157 okay = false;
2158 }
2159 break;
2160
2161 default:
2162 // Something has gone dreadfully wrong when determining
2163 // the restore policy from the manifest. Ignore the
2164 // rest of this package's data.
2165 Slog.e(TAG, "Invalid policy from manifest");
2166 okay = false;
2167 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2168 break;
2169 }
2170
2171 // If the policy is satisfied, go ahead and set up to pipe the
2172 // data to the agent.
2173 if (DEBUG && okay && mAgent != null) {
2174 Slog.i(TAG, "Reusing existing agent instance");
2175 }
2176 if (okay && mAgent == null) {
2177 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
2178
2179 try {
2180 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
2181
2182 // If we haven't sent any data to this app yet, we probably
2183 // need to clear it first. Check that.
2184 if (!mClearedPackages.contains(pkg)) {
2185 // apps with their own full backup agents are
2186 // responsible for coherently managing a full
2187 // restore.
2188 if (mTargetApp.fullBackupAgentName == null) {
2189 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
2190 clearApplicationDataSynchronous(pkg);
2191 } else {
2192 if (DEBUG) Slog.d(TAG, "full backup agent ("
2193 + mTargetApp.fullBackupAgentName + ") => no clear");
2194 }
2195 mClearedPackages.add(pkg);
2196 } else {
2197 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
2198 }
2199
2200 // All set; now set up the IPC and launch the agent
2201 setUpPipes();
2202 mAgent = bindToAgentSynchronous(mTargetApp,
2203 IApplicationThread.BACKUP_MODE_RESTORE_FULL);
2204 mAgentPackage = pkg;
2205 } catch (IOException e) {
2206 // fall through to error handling
2207 } catch (NameNotFoundException e) {
2208 // fall through to error handling
2209 }
2210
2211 if (mAgent == null) {
2212 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
2213 okay = false;
2214 tearDownPipes();
2215 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2216 }
2217 }
2218
2219 // Sanity check: make sure we never give data to the wrong app. This
2220 // should never happen but a little paranoia here won't go amiss.
2221 if (okay && !pkg.equals(mAgentPackage)) {
2222 Slog.e(TAG, "Restoring data for " + pkg
2223 + " but agent is for " + mAgentPackage);
2224 okay = false;
2225 }
2226
2227 // At this point we have an agent ready to handle the full
2228 // restore data as well as a pipe for sending data to
2229 // that agent. Tell the agent to start reading from the
2230 // pipe.
2231 if (okay) {
2232 boolean agentSuccess = true;
2233 long toCopy = info.size;
2234 final int token = generateToken();
2235 try {
2236 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
2237 + info.path);
2238 prepareOperationTimeout(token,
2239 TIMEOUT_FULL_BACKUP_INTERVAL);
2240 // fire up the app's agent listening on the socket. If
2241 // the agent is running in the system process we can't
2242 // just invoke it asynchronously, so we provide a thread
2243 // for it here.
2244 if (mTargetApp.processName.equals("system")) {
2245 Slog.d(TAG, "system process agent - spinning a thread");
2246 RestoreFileRunnable runner = new RestoreFileRunnable(
2247 mAgent, info, mPipes[0], token);
2248 new Thread(runner).start();
2249 } else {
2250 mAgent.doRestoreFile(mPipes[0], info.size, info.type,
2251 info.domain, info.path, info.mode, info.mtime,
2252 token, mBackupManagerBinder);
2253 }
2254 } catch (IOException e) {
2255 // couldn't dup the socket for a process-local restore
2256 Slog.d(TAG, "Couldn't establish restore");
2257 agentSuccess = false;
2258 okay = false;
2259 } catch (RemoteException e) {
2260 // whoops, remote agent went away. We'll eat the content
2261 // ourselves, then, and not copy it over.
2262 Slog.e(TAG, "Agent crashed during full restore");
2263 agentSuccess = false;
2264 okay = false;
2265 }
2266
2267 // Copy over the data if the agent is still good
2268 if (okay) {
2269 boolean pipeOkay = true;
2270 FileOutputStream pipe = new FileOutputStream(
2271 mPipes[1].getFileDescriptor());
2272 if (DEBUG) Slog.d(TAG, "Piping data to agent");
2273 while (toCopy > 0) {
2274 int toRead = (toCopy > buffer.length)
2275 ? buffer.length : (int)toCopy;
2276 int nRead = instream.read(buffer, 0, toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002277 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002278 if (nRead <= 0) break;
2279 toCopy -= nRead;
2280
2281 // send it to the output pipe as long as things
2282 // are still good
2283 if (pipeOkay) {
2284 try {
2285 pipe.write(buffer, 0, nRead);
2286 } catch (IOException e) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002287 Slog.e(TAG, "Failed to write to restore pipe", e);
Christopher Tate75a99702011-05-18 16:28:19 -07002288 pipeOkay = false;
2289 }
2290 }
2291 }
2292
2293 // done sending that file! Now we just need to consume
2294 // the delta from info.size to the end of block.
2295 skipTarPadding(info.size, instream);
2296
2297 // and now that we've sent it all, wait for the remote
2298 // side to acknowledge receipt
2299 agentSuccess = waitUntilOperationComplete(token);
2300 }
2301
2302 // okay, if the remote end failed at any point, deal with
2303 // it by ignoring the rest of the restore on it
2304 if (!agentSuccess) {
2305 mBackupHandler.removeMessages(MSG_TIMEOUT);
2306 tearDownPipes();
2307 tearDownAgent(mTargetApp);
2308 mAgent = null;
2309 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2310 }
2311 }
2312
2313 // Problems setting up the agent communication, or an already-
2314 // ignored package: skip to the next tar stream entry by
2315 // reading and discarding this file.
2316 if (!okay) {
2317 if (DEBUG) Slog.d(TAG, "[discarding file content]");
2318 long bytesToConsume = (info.size + 511) & ~511;
2319 while (bytesToConsume > 0) {
2320 int toRead = (bytesToConsume > buffer.length)
2321 ? buffer.length : (int)bytesToConsume;
2322 long nRead = instream.read(buffer, 0, toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002323 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002324 if (nRead <= 0) break;
2325 bytesToConsume -= nRead;
2326 }
2327 }
2328 }
2329 }
2330 } catch (IOException e) {
2331 Slog.w(TAG, "io exception on restore socket read", e);
2332 // treat as EOF
2333 info = null;
2334 }
2335
2336 return (info != null);
2337 }
2338
2339 void setUpPipes() throws IOException {
2340 mPipes = ParcelFileDescriptor.createPipe();
2341 }
2342
2343 void tearDownPipes() {
2344 if (mPipes != null) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002345 try {
2346 mPipes[0].close();
2347 mPipes[0] = null;
2348 mPipes[1].close();
2349 mPipes[1] = null;
2350 } catch (IOException e) {
2351 Slog.w(TAG, "Couldn't close agent pipes", e);
Christopher Tate75a99702011-05-18 16:28:19 -07002352 }
2353 mPipes = null;
2354 }
2355 }
2356
2357 void tearDownAgent(ApplicationInfo app) {
2358 if (mAgent != null) {
2359 try {
2360 // unbind and tidy up even on timeout or failure, just in case
2361 mActivityManager.unbindBackupAgent(app);
2362
2363 // The agent was running with a stub Application object, so shut it down.
2364 // !!! We hardcode the confirmation UI's package name here rather than use a
2365 // manifest flag! TODO something less direct.
2366 if (app.uid != Process.SYSTEM_UID
2367 && !app.packageName.equals("com.android.backupconfirm")) {
2368 if (DEBUG) Slog.d(TAG, "Killing host process");
2369 mActivityManager.killApplicationProcess(app.processName, app.uid);
2370 } else {
2371 if (DEBUG) Slog.d(TAG, "Not killing after full restore");
2372 }
2373 } catch (RemoteException e) {
2374 Slog.d(TAG, "Lost app trying to shut down");
2375 }
2376 mAgent = null;
2377 }
2378 }
2379
2380 class RestoreInstallObserver extends IPackageInstallObserver.Stub {
2381 final AtomicBoolean mDone = new AtomicBoolean();
Christopher Tatea858cb02011-06-03 12:27:51 -07002382 String mPackageName;
Christopher Tate75a99702011-05-18 16:28:19 -07002383 int mResult;
2384
2385 public void reset() {
2386 synchronized (mDone) {
2387 mDone.set(false);
2388 }
2389 }
2390
2391 public void waitForCompletion() {
2392 synchronized (mDone) {
2393 while (mDone.get() == false) {
2394 try {
2395 mDone.wait();
2396 } catch (InterruptedException e) { }
2397 }
2398 }
2399 }
2400
2401 int getResult() {
2402 return mResult;
2403 }
2404
2405 @Override
2406 public void packageInstalled(String packageName, int returnCode)
2407 throws RemoteException {
2408 synchronized (mDone) {
2409 mResult = returnCode;
Christopher Tatea858cb02011-06-03 12:27:51 -07002410 mPackageName = packageName;
Christopher Tate75a99702011-05-18 16:28:19 -07002411 mDone.set(true);
2412 mDone.notifyAll();
2413 }
2414 }
2415 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002416
2417 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
2418 final AtomicBoolean mDone = new AtomicBoolean();
2419 int mResult;
2420
2421 public void reset() {
2422 synchronized (mDone) {
2423 mDone.set(false);
2424 }
2425 }
2426
2427 public void waitForCompletion() {
2428 synchronized (mDone) {
2429 while (mDone.get() == false) {
2430 try {
2431 mDone.wait();
2432 } catch (InterruptedException e) { }
2433 }
2434 }
2435 }
2436
2437 @Override
2438 public void packageDeleted(String packageName, int returnCode) throws RemoteException {
2439 synchronized (mDone) {
2440 mResult = returnCode;
2441 mDone.set(true);
2442 mDone.notifyAll();
2443 }
2444 }
2445 }
2446
Christopher Tate75a99702011-05-18 16:28:19 -07002447 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
Christopher Tatea858cb02011-06-03 12:27:51 -07002448 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
Christopher Tate75a99702011-05-18 16:28:19 -07002449
2450 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
2451 boolean okay = true;
2452
2453 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
2454
2455 // The file content is an .apk file. Copy it out to a staging location and
2456 // attempt to install it.
2457 File apkFile = new File(mDataDir, info.packageName);
2458 try {
2459 FileOutputStream apkStream = new FileOutputStream(apkFile);
2460 byte[] buffer = new byte[32 * 1024];
2461 long size = info.size;
2462 while (size > 0) {
2463 long toRead = (buffer.length < size) ? buffer.length : size;
2464 int didRead = instream.read(buffer, 0, (int)toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002465 if (didRead >= 0) mBytes += didRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002466 apkStream.write(buffer, 0, didRead);
2467 size -= didRead;
2468 }
2469 apkStream.close();
2470
2471 // make sure the installer can read it
2472 apkFile.setReadable(true, false);
2473
2474 // Now install it
2475 Uri packageUri = Uri.fromFile(apkFile);
2476 mInstallObserver.reset();
2477 mPackageManager.installPackage(packageUri, mInstallObserver,
2478 PackageManager.INSTALL_REPLACE_EXISTING, installerPackage);
2479 mInstallObserver.waitForCompletion();
2480
2481 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
2482 // The only time we continue to accept install of data even if the
2483 // apk install failed is if we had already determined that we could
2484 // accept the data regardless.
2485 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
2486 okay = false;
2487 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002488 } else {
2489 // Okay, the install succeeded. Make sure it was the right app.
2490 boolean uninstall = false;
2491 if (!mInstallObserver.mPackageName.equals(info.packageName)) {
2492 Slog.w(TAG, "Restore stream claimed to include apk for "
2493 + info.packageName + " but apk was really "
2494 + mInstallObserver.mPackageName);
2495 // delete the package we just put in place; it might be fraudulent
2496 okay = false;
2497 uninstall = true;
2498 } else {
2499 try {
2500 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
2501 PackageManager.GET_SIGNATURES);
2502 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
2503 Slog.w(TAG, "Restore stream contains apk of package "
2504 + info.packageName + " but it disallows backup/restore");
2505 okay = false;
2506 } else {
2507 // So far so good -- do the signatures match the manifest?
2508 Signature[] sigs = mManifestSignatures.get(info.packageName);
2509 if (!signaturesMatch(sigs, pkg)) {
2510 Slog.w(TAG, "Installed app " + info.packageName
2511 + " signatures do not match restore manifest");
2512 okay = false;
2513 uninstall = true;
2514 }
2515 }
2516 } catch (NameNotFoundException e) {
2517 Slog.w(TAG, "Install of package " + info.packageName
2518 + " succeeded but now not found");
2519 okay = false;
2520 }
2521 }
2522
2523 // If we're not okay at this point, we need to delete the package
2524 // that we just installed.
2525 if (uninstall) {
2526 mDeleteObserver.reset();
2527 mPackageManager.deletePackage(mInstallObserver.mPackageName,
2528 mDeleteObserver, 0);
2529 mDeleteObserver.waitForCompletion();
2530 }
Christopher Tate75a99702011-05-18 16:28:19 -07002531 }
2532 } catch (IOException e) {
2533 Slog.e(TAG, "Unable to transcribe restored apk for install");
2534 okay = false;
2535 } finally {
2536 apkFile.delete();
2537 }
2538
2539 return okay;
2540 }
2541
2542 // Given an actual file content size, consume the post-content padding mandated
2543 // by the tar format.
2544 void skipTarPadding(long size, InputStream instream) throws IOException {
2545 long partial = (size + 512) % 512;
2546 if (partial > 0) {
2547 byte[] buffer = new byte[512];
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002548 int nRead = instream.read(buffer, 0, 512 - (int)partial);
2549 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002550 }
2551 }
2552
2553 // Returns a policy constant; takes a buffer arg to reduce memory churn
2554 RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
2555 throws IOException {
2556 // Fail on suspiciously large manifest files
2557 if (info.size > 64 * 1024) {
2558 throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
2559 }
2560 byte[] buffer = new byte[(int) info.size];
2561 int nRead = 0;
2562 while (nRead < info.size) {
2563 nRead += instream.read(buffer, nRead, (int)info.size - nRead);
2564 }
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002565 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002566
2567 RestorePolicy policy = RestorePolicy.IGNORE;
2568 String[] str = new String[1];
2569 int offset = 0;
2570
2571 try {
2572 offset = extractLine(buffer, offset, str);
2573 int version = Integer.parseInt(str[0]);
2574 if (version == BACKUP_MANIFEST_VERSION) {
2575 offset = extractLine(buffer, offset, str);
2576 String manifestPackage = str[0];
2577 // TODO: handle <original-package>
2578 if (manifestPackage.equals(info.packageName)) {
2579 offset = extractLine(buffer, offset, str);
2580 version = Integer.parseInt(str[0]); // app version
2581 offset = extractLine(buffer, offset, str);
2582 int platformVersion = Integer.parseInt(str[0]);
2583 offset = extractLine(buffer, offset, str);
2584 info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
2585 offset = extractLine(buffer, offset, str);
2586 boolean hasApk = str[0].equals("1");
2587 offset = extractLine(buffer, offset, str);
2588 int numSigs = Integer.parseInt(str[0]);
Christopher Tate75a99702011-05-18 16:28:19 -07002589 if (numSigs > 0) {
Christopher Tatea858cb02011-06-03 12:27:51 -07002590 Signature[] sigs = new Signature[numSigs];
Christopher Tate75a99702011-05-18 16:28:19 -07002591 for (int i = 0; i < numSigs; i++) {
2592 offset = extractLine(buffer, offset, str);
2593 sigs[i] = new Signature(str[0]);
2594 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002595 mManifestSignatures.put(info.packageName, sigs);
Christopher Tate75a99702011-05-18 16:28:19 -07002596
2597 // Okay, got the manifest info we need...
2598 try {
Christopher Tate75a99702011-05-18 16:28:19 -07002599 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2600 info.packageName, PackageManager.GET_SIGNATURES);
Christopher Tatea858cb02011-06-03 12:27:51 -07002601 // Fall through to IGNORE if the app explicitly disallows backup
2602 final int flags = pkgInfo.applicationInfo.flags;
2603 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
2604 // Verify signatures against any installed version; if they
2605 // don't match, then we fall though and ignore the data. The
2606 // signatureMatch() method explicitly ignores the signature
2607 // check for packages installed on the system partition, because
2608 // such packages are signed with the platform cert instead of
2609 // the app developer's cert, so they're different on every
2610 // device.
2611 if (signaturesMatch(sigs, pkgInfo)) {
2612 if (pkgInfo.versionCode >= version) {
2613 Slog.i(TAG, "Sig + version match; taking data");
2614 policy = RestorePolicy.ACCEPT;
2615 } else {
2616 // The data is from a newer version of the app than
2617 // is presently installed. That means we can only
2618 // use it if the matching apk is also supplied.
2619 Slog.d(TAG, "Data version " + version
2620 + " is newer than installed version "
2621 + pkgInfo.versionCode + " - requiring apk");
2622 policy = RestorePolicy.ACCEPT_IF_APK;
2623 }
Christopher Tate75a99702011-05-18 16:28:19 -07002624 } else {
Christopher Tatea858cb02011-06-03 12:27:51 -07002625 Slog.w(TAG, "Restore manifest signatures do not match "
2626 + "installed application for " + info.packageName);
Christopher Tate75a99702011-05-18 16:28:19 -07002627 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002628 } else {
2629 if (DEBUG) Slog.i(TAG, "Restore manifest from "
2630 + info.packageName + " but allowBackup=false");
Christopher Tate75a99702011-05-18 16:28:19 -07002631 }
2632 } catch (NameNotFoundException e) {
2633 // Okay, the target app isn't installed. We can process
2634 // the restore properly only if the dataset provides the
2635 // apk file and we can successfully install it.
2636 if (DEBUG) Slog.i(TAG, "Package " + info.packageName
2637 + " not installed; requiring apk in dataset");
2638 policy = RestorePolicy.ACCEPT_IF_APK;
2639 }
2640
2641 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
2642 Slog.i(TAG, "Cannot restore package " + info.packageName
2643 + " without the matching .apk");
2644 }
2645 } else {
2646 Slog.i(TAG, "Missing signature on backed-up package "
2647 + info.packageName);
2648 }
2649 } else {
2650 Slog.i(TAG, "Expected package " + info.packageName
2651 + " but restore manifest claims " + manifestPackage);
2652 }
2653 } else {
2654 Slog.i(TAG, "Unknown restore manifest version " + version
2655 + " for package " + info.packageName);
2656 }
2657 } catch (NumberFormatException e) {
2658 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
2659 }
2660
2661 return policy;
2662 }
2663
2664 // Builds a line from a byte buffer starting at 'offset', and returns
2665 // the index of the next unconsumed data in the buffer.
2666 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
2667 final int end = buffer.length;
2668 if (offset >= end) throw new IOException("Incomplete data");
2669
2670 int pos;
2671 for (pos = offset; pos < end; pos++) {
2672 byte c = buffer[pos];
2673 // at LF we declare end of line, and return the next char as the
2674 // starting point for the next time through
2675 if (c == '\n') {
2676 break;
2677 }
2678 }
2679 outStr[0] = new String(buffer, offset, pos - offset);
2680 pos++; // may be pointing an extra byte past the end but that's okay
2681 return pos;
2682 }
2683
2684 void dumpFileMetadata(FileMetadata info) {
2685 if (DEBUG) {
2686 StringBuilder b = new StringBuilder(128);
2687
2688 // mode string
2689 b.append((info.type == FullBackup.TYPE_DIRECTORY) ? 'd' : '-');
2690 b.append(((info.mode & 0400) != 0) ? 'r' : '-');
2691 b.append(((info.mode & 0200) != 0) ? 'w' : '-');
2692 b.append(((info.mode & 0100) != 0) ? 'x' : '-');
2693 b.append(((info.mode & 0040) != 0) ? 'r' : '-');
2694 b.append(((info.mode & 0020) != 0) ? 'w' : '-');
2695 b.append(((info.mode & 0010) != 0) ? 'x' : '-');
2696 b.append(((info.mode & 0004) != 0) ? 'r' : '-');
2697 b.append(((info.mode & 0002) != 0) ? 'w' : '-');
2698 b.append(((info.mode & 0001) != 0) ? 'x' : '-');
2699 b.append(String.format(" %9d ", info.size));
2700
2701 Date stamp = new Date(info.mtime);
2702 b.append(new SimpleDateFormat("MMM dd kk:mm:ss ").format(stamp));
2703
2704 b.append(info.packageName);
2705 b.append(" :: ");
2706 b.append(info.domain);
2707 b.append(" :: ");
2708 b.append(info.path);
2709
2710 Slog.i(TAG, b.toString());
2711 }
2712 }
2713 // Consume a tar file header block [sequence] and accumulate the relevant metadata
2714 FileMetadata readTarHeaders(InputStream instream) throws IOException {
2715 byte[] block = new byte[512];
2716 FileMetadata info = null;
2717
2718 boolean gotHeader = readTarHeader(instream, block);
2719 if (gotHeader) {
2720 // okay, presume we're okay, and extract the various metadata
2721 info = new FileMetadata();
2722 info.size = extractRadix(block, 124, 12, 8);
2723 info.mtime = extractRadix(block, 136, 12, 8);
2724 info.mode = extractRadix(block, 100, 8, 8);
2725
2726 info.path = extractString(block, 345, 155); // prefix
2727 String path = extractString(block, 0, 100);
2728 if (path.length() > 0) {
2729 if (info.path.length() > 0) info.path += '/';
2730 info.path += path;
2731 }
2732
2733 // tar link indicator field: 1 byte at offset 156 in the header.
2734 int typeChar = block[156];
2735 if (typeChar == 'x') {
2736 // pax extended header, so we need to read that
2737 gotHeader = readPaxExtendedHeader(instream, info);
2738 if (gotHeader) {
2739 // and after a pax extended header comes another real header -- read
2740 // that to find the real file type
2741 gotHeader = readTarHeader(instream, block);
2742 }
2743 if (!gotHeader) throw new IOException("Bad or missing pax header");
2744
2745 typeChar = block[156];
2746 }
2747
2748 switch (typeChar) {
2749 case '0': info.type = FullBackup.TYPE_FILE; break;
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002750 case '5': {
2751 info.type = FullBackup.TYPE_DIRECTORY;
2752 if (info.size != 0) {
2753 Slog.w(TAG, "Directory entry with nonzero size in header");
2754 info.size = 0;
2755 }
2756 break;
2757 }
Christopher Tate75a99702011-05-18 16:28:19 -07002758 case 0: {
2759 // presume EOF
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002760 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
Christopher Tate75a99702011-05-18 16:28:19 -07002761 return null;
2762 }
2763 default: {
2764 Slog.e(TAG, "Unknown tar entity type: " + typeChar);
2765 throw new IOException("Unknown entity type " + typeChar);
2766 }
2767 }
2768
2769 // Parse out the path
2770 //
2771 // first: apps/shared/unrecognized
2772 if (FullBackup.SHARED_PREFIX.regionMatches(0,
2773 info.path, 0, FullBackup.SHARED_PREFIX.length())) {
2774 // File in shared storage. !!! TODO: implement this.
2775 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
Christopher Tateb0628bf2011-06-02 15:08:13 -07002776 info.packageName = "com.android.sharedstoragebackup";
Christopher Tate75a99702011-05-18 16:28:19 -07002777 info.domain = FullBackup.SHARED_STORAGE_TOKEN;
Christopher Tateb0628bf2011-06-02 15:08:13 -07002778 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
Christopher Tate75a99702011-05-18 16:28:19 -07002779 } else if (FullBackup.APPS_PREFIX.regionMatches(0,
2780 info.path, 0, FullBackup.APPS_PREFIX.length())) {
2781 // App content! Parse out the package name and domain
2782
2783 // strip the apps/ prefix
2784 info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
2785
2786 // extract the package name
2787 int slash = info.path.indexOf('/');
2788 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
2789 info.packageName = info.path.substring(0, slash);
2790 info.path = info.path.substring(slash+1);
2791
2792 // if it's a manifest we're done, otherwise parse out the domains
2793 if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2794 slash = info.path.indexOf('/');
2795 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
2796 info.domain = info.path.substring(0, slash);
2797 // validate that it's one of the domains we understand
2798 if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
2799 && !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
2800 && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
2801 && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
2802 && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
2803 && !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
2804 && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
2805 throw new IOException("Unrecognized domain " + info.domain);
2806 }
2807
2808 info.path = info.path.substring(slash + 1);
2809 }
2810 }
2811 }
2812 return info;
2813 }
2814
2815 boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
2816 int nRead = instream.read(block, 0, 512);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002817 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002818 if (nRead > 0 && nRead != 512) {
2819 // if we read only a partial block, then things are
2820 // clearly screwed up. terminate the restore.
2821 throw new IOException("Partial header block: " + nRead);
2822 }
2823 return (nRead > 0);
2824 }
2825
2826 // overwrites 'info' fields based on the pax extended header
2827 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
2828 throws IOException {
2829 // We should never see a pax extended header larger than this
2830 if (info.size > 32*1024) {
2831 Slog.w(TAG, "Suspiciously large pax header size " + info.size
2832 + " - aborting");
2833 throw new IOException("Sanity failure: pax header size " + info.size);
2834 }
2835
2836 // read whole blocks, not just the content size
2837 int numBlocks = (int)((info.size + 511) >> 9);
2838 byte[] data = new byte[numBlocks * 512];
2839 int nRead = instream.read(data);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002840 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002841 if (nRead != data.length) {
2842 return false;
2843 }
2844
2845 final int contentSize = (int) info.size;
2846 int offset = 0;
2847 do {
2848 // extract the line at 'offset'
2849 int eol = offset+1;
2850 while (eol < contentSize && data[eol] != ' ') eol++;
2851 if (eol >= contentSize) {
2852 // error: we just hit EOD looking for the end of the size field
2853 throw new IOException("Invalid pax data");
2854 }
2855 // eol points to the space between the count and the key
2856 int linelen = (int) extractRadix(data, offset, eol - offset, 10);
2857 int key = eol + 1; // start of key=value
2858 eol = offset + linelen - 1; // trailing LF
2859 int value;
2860 for (value = key+1; data[value] != '=' && value <= eol; value++);
2861 if (value > eol) {
2862 throw new IOException("Invalid pax declaration");
2863 }
2864
2865 // pax requires that key/value strings be in UTF-8
2866 String keyStr = new String(data, key, value-key, "UTF-8");
2867 // -1 to strip the trailing LF
2868 String valStr = new String(data, value+1, eol-value-1, "UTF-8");
2869
2870 if ("path".equals(keyStr)) {
2871 info.path = valStr;
2872 } else if ("size".equals(keyStr)) {
2873 info.size = Long.parseLong(valStr);
2874 } else {
2875 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
2876 }
2877
2878 offset += linelen;
2879 } while (offset < contentSize);
2880
2881 return true;
2882 }
2883
2884 long extractRadix(byte[] data, int offset, int maxChars, int radix)
2885 throws IOException {
2886 long value = 0;
2887 final int end = offset + maxChars;
2888 for (int i = offset; i < end; i++) {
2889 final byte b = data[i];
Christopher Tate3f6c77b2011-06-07 13:17:17 -07002890 // Numeric fields in tar can terminate with either NUL or SPC
Christopher Tate75a99702011-05-18 16:28:19 -07002891 if (b == 0 || b == ' ') break;
2892 if (b < '0' || b > ('0' + radix - 1)) {
2893 throw new IOException("Invalid number in header");
2894 }
2895 value = radix * value + (b - '0');
2896 }
2897 return value;
2898 }
2899
2900 String extractString(byte[] data, int offset, int maxChars) throws IOException {
2901 final int end = offset + maxChars;
2902 int eos = offset;
Christopher Tate3f6c77b2011-06-07 13:17:17 -07002903 // tar string fields terminate early with a NUL
2904 while (eos < end && data[eos] != 0) eos++;
Christopher Tate75a99702011-05-18 16:28:19 -07002905 return new String(data, offset, eos-offset, "US-ASCII");
2906 }
2907
2908 void sendStartRestore() {
2909 if (mObserver != null) {
2910 try {
2911 mObserver.onStartRestore();
2912 } catch (RemoteException e) {
2913 Slog.w(TAG, "full restore observer went away: startRestore");
2914 mObserver = null;
2915 }
2916 }
2917 }
2918
2919 void sendOnRestorePackage(String name) {
2920 if (mObserver != null) {
2921 try {
2922 // TODO: use a more user-friendly name string
2923 mObserver.onRestorePackage(name);
2924 } catch (RemoteException e) {
2925 Slog.w(TAG, "full restore observer went away: restorePackage");
2926 mObserver = null;
2927 }
2928 }
2929 }
2930
2931 void sendEndRestore() {
2932 if (mObserver != null) {
2933 try {
2934 mObserver.onEndRestore();
2935 } catch (RemoteException e) {
2936 Slog.w(TAG, "full restore observer went away: endRestore");
2937 mObserver = null;
2938 }
2939 }
2940 }
2941 }
2942
Christopher Tatedf01dea2009-06-09 20:45:02 -07002943 // ----- Restore handling -----
2944
Christopher Tate78dd4a72009-11-04 11:49:08 -08002945 private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
2946 // If the target resides on the system partition, we allow it to restore
2947 // data from the like-named package in a restore set even if the signatures
2948 // do not match. (Unlike general applications, those flashed to the system
2949 // partition will be signed with the device's platform certificate, so on
2950 // different phones the same system app will have different signatures.)
2951 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002952 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
Christopher Tate78dd4a72009-11-04 11:49:08 -08002953 return true;
2954 }
2955
Christopher Tate20efdf6b2009-06-18 19:41:36 -07002956 // Allow unsigned apps, but not signed on one device and unsigned on the other
2957 // !!! TODO: is this the right policy?
Christopher Tate78dd4a72009-11-04 11:49:08 -08002958 Signature[] deviceSigs = target.signatures;
Joe Onorato8a9b2202010-02-26 18:56:32 -08002959 if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
Christopher Tate6aa41f42009-06-19 14:14:22 -07002960 + " device=" + deviceSigs);
Christopher Tate20efdf6b2009-06-18 19:41:36 -07002961 if ((storedSigs == null || storedSigs.length == 0)
2962 && (deviceSigs == null || deviceSigs.length == 0)) {
2963 return true;
2964 }
2965 if (storedSigs == null || deviceSigs == null) {
2966 return false;
2967 }
2968
Christopher Tateabce4e82009-06-18 18:35:32 -07002969 // !!! TODO: this demands that every stored signature match one
2970 // that is present on device, and does not demand the converse.
2971 // Is this this right policy?
2972 int nStored = storedSigs.length;
2973 int nDevice = deviceSigs.length;
2974
2975 for (int i=0; i < nStored; i++) {
2976 boolean match = false;
2977 for (int j=0; j < nDevice; j++) {
2978 if (storedSigs[i].equals(deviceSigs[j])) {
2979 match = true;
2980 break;
2981 }
2982 }
2983 if (!match) {
2984 return false;
2985 }
2986 }
2987 return true;
2988 }
2989
Christopher Tate44a27902010-01-27 17:15:49 -08002990 class PerformRestoreTask implements Runnable {
Christopher Tatedf01dea2009-06-09 20:45:02 -07002991 private IBackupTransport mTransport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07002992 private IRestoreObserver mObserver;
Dan Egnor156411d2009-06-26 13:20:02 -07002993 private long mToken;
Christopher Tate84725812010-02-04 15:52:40 -08002994 private PackageInfo mTargetPackage;
Christopher Tate5cb400b2009-06-25 16:03:14 -07002995 private File mStateDir;
Christopher Tate1bb69062010-02-19 17:02:12 -08002996 private int mPmToken;
Chris Tate249345b2010-10-29 12:57:04 -07002997 private boolean mNeedFullBackup;
Christopher Tatedf01dea2009-06-09 20:45:02 -07002998
Christopher Tate5cbbf562009-06-22 16:44:51 -07002999 class RestoreRequest {
3000 public PackageInfo app;
3001 public int storedAppVersion;
3002
3003 RestoreRequest(PackageInfo _app, int _version) {
3004 app = _app;
3005 storedAppVersion = _version;
3006 }
3007 }
3008
Christopher Tate44a27902010-01-27 17:15:49 -08003009 PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
Chris Tate249345b2010-10-29 12:57:04 -07003010 long restoreSetToken, PackageInfo targetPackage, int pmToken,
3011 boolean needFullBackup) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003012 mTransport = transport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07003013 mObserver = observer;
Christopher Tate9bbc21a2009-06-10 20:23:25 -07003014 mToken = restoreSetToken;
Christopher Tate84725812010-02-04 15:52:40 -08003015 mTargetPackage = targetPackage;
Christopher Tate1bb69062010-02-19 17:02:12 -08003016 mPmToken = pmToken;
Chris Tate249345b2010-10-29 12:57:04 -07003017 mNeedFullBackup = needFullBackup;
Christopher Tate5cb400b2009-06-25 16:03:14 -07003018
3019 try {
3020 mStateDir = new File(mBaseStateDir, transport.transportDirName());
3021 } catch (RemoteException e) {
3022 // can't happen; the transport is local
3023 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003024 }
3025
Christopher Tatedf01dea2009-06-09 20:45:02 -07003026 public void run() {
Dan Egnorbb9001c2009-07-27 12:20:13 -07003027 long startRealtime = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08003028 if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport
Christopher Tate84725812010-02-04 15:52:40 -08003029 + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
Christopher Tate1bb69062010-02-19 17:02:12 -08003030 + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken);
Christopher Tatedf01dea2009-06-09 20:45:02 -07003031
Christopher Tateb49ceb32010-02-08 16:22:24 -08003032 PackageManagerBackupAgent pmAgent = null;
Christopher Tate7d562ec2009-06-25 18:03:43 -07003033 int error = -1; // assume error
3034
Dan Egnorefe52642009-06-24 00:16:33 -07003035 // build the set of apps to restore
Christopher Tatedf01dea2009-06-09 20:45:02 -07003036 try {
Dan Egnorbb9001c2009-07-27 12:20:13 -07003037 // TODO: Log this before getAvailableRestoreSets, somehow
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003038 EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken);
Christopher Tateabce4e82009-06-18 18:35:32 -07003039
Dan Egnorefe52642009-06-24 00:16:33 -07003040 // Get the list of all packages which have backup enabled.
3041 // (Include the Package Manager metadata pseudo-package first.)
3042 ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
3043 PackageInfo omPackage = new PackageInfo();
3044 omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
3045 restorePackages.add(omPackage);
Christopher Tatedf01dea2009-06-09 20:45:02 -07003046
Dan Egnorefe52642009-06-24 00:16:33 -07003047 List<PackageInfo> agentPackages = allAgentPackages();
Christopher Tate84725812010-02-04 15:52:40 -08003048 if (mTargetPackage == null) {
3049 restorePackages.addAll(agentPackages);
3050 } else {
3051 // Just one package to attempt restore of
3052 restorePackages.add(mTargetPackage);
3053 }
Dan Egnorefe52642009-06-24 00:16:33 -07003054
Christopher Tate7d562ec2009-06-25 18:03:43 -07003055 // let the observer know that we're running
3056 if (mObserver != null) {
3057 try {
3058 // !!! TODO: get an actual count from the transport after
3059 // its startRestore() runs?
3060 mObserver.restoreStarting(restorePackages.size());
3061 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003062 Slog.d(TAG, "Restore observer died at restoreStarting");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003063 mObserver = null;
3064 }
3065 }
3066
Dan Egnor01445162009-09-21 17:04:05 -07003067 if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) !=
3068 BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003069 Slog.e(TAG, "Error starting restore operation");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003070 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003071 return;
3072 }
3073
3074 String packageName = mTransport.nextRestorePackage();
3075 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003076 Slog.e(TAG, "Error getting first restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003077 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003078 return;
3079 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003080 Slog.i(TAG, "No restore data available");
Dan Egnorbb9001c2009-07-27 12:20:13 -07003081 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003082 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
Dan Egnorefe52642009-06-24 00:16:33 -07003083 return;
3084 } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003085 Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
Dan Egnorefe52642009-06-24 00:16:33 -07003086 + "\", found only \"" + packageName + "\"");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003087 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003088 "Package manager data missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003089 return;
3090 }
3091
3092 // Pull the Package Manager metadata from the restore set first
Christopher Tateb49ceb32010-02-08 16:22:24 -08003093 pmAgent = new PackageManagerBackupAgent(
Dan Egnorefe52642009-06-24 00:16:33 -07003094 mPackageManager, agentPackages);
Chris Tate249345b2010-10-29 12:57:04 -07003095 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()),
3096 mNeedFullBackup);
Dan Egnorefe52642009-06-24 00:16:33 -07003097
Christopher Tate8c032472009-07-02 14:28:47 -07003098 // Verify that the backup set includes metadata. If not, we can't do
3099 // signature/version verification etc, so we simply do not proceed with
3100 // the restore operation.
Christopher Tate3d7cd132009-07-07 14:23:07 -07003101 if (!pmAgent.hasMetadata()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003102 Slog.e(TAG, "No restore metadata available, so not restoring settings");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003103 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003104 "Package manager restore metadata missing");
Christopher Tate8c032472009-07-02 14:28:47 -07003105 return;
3106 }
3107
Christopher Tate7d562ec2009-06-25 18:03:43 -07003108 int count = 0;
Dan Egnorefe52642009-06-24 00:16:33 -07003109 for (;;) {
3110 packageName = mTransport.nextRestorePackage();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003111
Dan Egnorefe52642009-06-24 00:16:33 -07003112 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003113 Slog.e(TAG, "Error getting next restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003114 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003115 return;
3116 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003117 if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
Dan Egnorefe52642009-06-24 00:16:33 -07003118 break;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003119 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003120
Christopher Tate7d562ec2009-06-25 18:03:43 -07003121 if (mObserver != null) {
Christopher Tate7d562ec2009-06-25 18:03:43 -07003122 try {
Christopher Tate9c3cee92010-03-25 16:06:43 -07003123 mObserver.onUpdate(count, packageName);
Christopher Tate7d562ec2009-06-25 18:03:43 -07003124 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003125 Slog.d(TAG, "Restore observer died in onUpdate");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003126 mObserver = null;
3127 }
3128 }
3129
Dan Egnorefe52642009-06-24 00:16:33 -07003130 Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
3131 if (metaInfo == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003132 Slog.e(TAG, "Missing metadata for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003133 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003134 "Package metadata missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003135 continue;
3136 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003137
Dan Egnorbb9001c2009-07-27 12:20:13 -07003138 PackageInfo packageInfo;
3139 try {
3140 int flags = PackageManager.GET_SIGNATURES;
3141 packageInfo = mPackageManager.getPackageInfo(packageName, flags);
3142 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003143 Slog.e(TAG, "Invalid package restoring data", e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003144 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003145 "Package missing on device");
3146 continue;
3147 }
3148
Dan Egnorefe52642009-06-24 00:16:33 -07003149 if (metaInfo.versionCode > packageInfo.versionCode) {
Christopher Tate3dda5182010-02-24 16:06:18 -08003150 // Data is from a "newer" version of the app than we have currently
3151 // installed. If the app has not declared that it is prepared to
3152 // handle this case, we do not attempt the restore.
3153 if ((packageInfo.applicationInfo.flags
3154 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
3155 String message = "Version " + metaInfo.versionCode
3156 + " > installed version " + packageInfo.versionCode;
Joe Onorato8a9b2202010-02-26 18:56:32 -08003157 Slog.w(TAG, "Package " + packageName + ": " + message);
Christopher Tate3dda5182010-02-24 16:06:18 -08003158 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
3159 packageName, message);
3160 continue;
3161 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003162 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
Christopher Tate3dda5182010-02-24 16:06:18 -08003163 + " > installed " + packageInfo.versionCode
3164 + " but restoreAnyVersion");
3165 }
Dan Egnorefe52642009-06-24 00:16:33 -07003166 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003167
Christopher Tate78dd4a72009-11-04 11:49:08 -08003168 if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003169 Slog.w(TAG, "Signature mismatch restoring " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003170 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003171 "Signature mismatch");
Dan Egnorefe52642009-06-24 00:16:33 -07003172 continue;
3173 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003174
Joe Onorato8a9b2202010-02-26 18:56:32 -08003175 if (DEBUG) Slog.v(TAG, "Package " + packageName
Dan Egnorefe52642009-06-24 00:16:33 -07003176 + " restore version [" + metaInfo.versionCode
3177 + "] is compatible with installed version ["
3178 + packageInfo.versionCode + "]");
Christopher Tatec7b31e32009-06-10 15:49:30 -07003179
Christopher Tate3de55bc2010-03-12 17:28:08 -08003180 // Then set up and bind the agent
Dan Egnorefe52642009-06-24 00:16:33 -07003181 IBackupAgent agent = bindToAgentSynchronous(
3182 packageInfo.applicationInfo,
Christopher Tate3de55bc2010-03-12 17:28:08 -08003183 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Dan Egnorefe52642009-06-24 00:16:33 -07003184 if (agent == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003185 Slog.w(TAG, "Can't find backup agent for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003186 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003187 "Restore agent missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003188 continue;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003189 }
3190
Christopher Tate5e1ab332009-09-01 20:32:49 -07003191 // And then finally run the restore on this agent
Dan Egnorefe52642009-06-24 00:16:33 -07003192 try {
Chris Tate249345b2010-10-29 12:57:04 -07003193 processOneRestore(packageInfo, metaInfo.versionCode, agent,
3194 mNeedFullBackup);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003195 ++count;
Dan Egnorefe52642009-06-24 00:16:33 -07003196 } finally {
Christopher Tate5e1ab332009-09-01 20:32:49 -07003197 // unbind and tidy up even on timeout or failure, just in case
Dan Egnorefe52642009-06-24 00:16:33 -07003198 mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
Christopher Tate5e1ab332009-09-01 20:32:49 -07003199
3200 // The agent was probably running with a stub Application object,
3201 // which isn't a valid run mode for the main app logic. Shut
3202 // down the app so that next time it's launched, it gets the
Christopher Tate3dda5182010-02-24 16:06:18 -08003203 // usual full initialization. Note that this is only done for
3204 // full-system restores: when a single app has requested a restore,
3205 // it is explicitly not killed following that operation.
3206 if (mTargetPackage == null && (packageInfo.applicationInfo.flags
Christopher Tate5e1ab332009-09-01 20:32:49 -07003207 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003208 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
Christopher Tate5e1ab332009-09-01 20:32:49 -07003209 + packageInfo.applicationInfo.processName);
3210 mActivityManager.killApplicationProcess(
3211 packageInfo.applicationInfo.processName,
3212 packageInfo.applicationInfo.uid);
3213 }
Dan Egnorefe52642009-06-24 00:16:33 -07003214 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003215 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003216
3217 // if we get this far, report success to the observer
3218 error = 0;
Dan Egnorbb9001c2009-07-27 12:20:13 -07003219 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003220 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, count, millis);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003221 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003222 Slog.e(TAG, "Error in restore thread", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003223 } finally {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003224 if (DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003225
Dan Egnorefe52642009-06-24 00:16:33 -07003226 try {
3227 mTransport.finishRestore();
3228 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003229 Slog.e(TAG, "Error finishing restore", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003230 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003231
3232 if (mObserver != null) {
3233 try {
3234 mObserver.restoreFinished(error);
3235 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003236 Slog.d(TAG, "Restore observer died at restoreFinished");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003237 }
3238 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003239
Christopher Tate84725812010-02-04 15:52:40 -08003240 // If this was a restoreAll operation, record that this was our
Christopher Tateb49ceb32010-02-08 16:22:24 -08003241 // ancestral dataset, as well as the set of apps that are possibly
3242 // restoreable from the dataset
3243 if (mTargetPackage == null && pmAgent != null) {
3244 mAncestralPackages = pmAgent.getRestoredPackages();
Christopher Tate84725812010-02-04 15:52:40 -08003245 mAncestralToken = mToken;
3246 writeRestoreTokens();
3247 }
3248
Christopher Tate1bb69062010-02-19 17:02:12 -08003249 // We must under all circumstances tell the Package Manager to
3250 // proceed with install notifications if it's waiting for us.
3251 if (mPmToken > 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003252 if (DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
Christopher Tate1bb69062010-02-19 17:02:12 -08003253 try {
3254 mPackageManagerBinder.finishPackageInstall(mPmToken);
3255 } catch (RemoteException e) { /* can't happen */ }
3256 }
3257
Christopher Tate73a3cb32010-12-13 18:27:26 -08003258 // Furthermore we need to reset the session timeout clock
3259 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
3260 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
3261 TIMEOUT_RESTORE_INTERVAL);
3262
Christopher Tateb6787f22009-07-02 17:40:45 -07003263 // done; we can finally release the wakelock
3264 mWakelock.release();
Christopher Tatedf01dea2009-06-09 20:45:02 -07003265 }
3266 }
3267
Dan Egnorefe52642009-06-24 00:16:33 -07003268 // Do the guts of a restore of one application, using mTransport.getRestoreData().
Chris Tate249345b2010-10-29 12:57:04 -07003269 void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
3270 boolean needFullBackup) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003271 // !!! TODO: actually run the restore through mTransport
Christopher Tatec7b31e32009-06-10 15:49:30 -07003272 final String packageName = app.packageName;
3273
Joe Onorato8a9b2202010-02-26 18:56:32 -08003274 if (DEBUG) Slog.d(TAG, "processOneRestore packageName=" + packageName);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04003275
Christopher Tatec7b31e32009-06-10 15:49:30 -07003276 // !!! TODO: get the dirs from the transport
3277 File backupDataName = new File(mDataDir, packageName + ".restore");
Dan Egnorbb9001c2009-07-27 12:20:13 -07003278 File newStateName = new File(mStateDir, packageName + ".new");
3279 File savedStateName = new File(mStateDir, packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003280
Dan Egnorbb9001c2009-07-27 12:20:13 -07003281 ParcelFileDescriptor backupData = null;
3282 ParcelFileDescriptor newState = null;
3283
Christopher Tate4a627c72011-04-01 14:43:32 -07003284 final int token = generateToken();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003285 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07003286 // Run the transport's restore pass
Dan Egnorbb9001c2009-07-27 12:20:13 -07003287 backupData = ParcelFileDescriptor.open(backupDataName,
3288 ParcelFileDescriptor.MODE_READ_WRITE |
3289 ParcelFileDescriptor.MODE_CREATE |
3290 ParcelFileDescriptor.MODE_TRUNCATE);
3291
Dan Egnor01445162009-09-21 17:04:05 -07003292 if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003293 Slog.e(TAG, "Error getting restore data for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003294 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003295 return;
Christopher Tatec7b31e32009-06-10 15:49:30 -07003296 }
3297
3298 // Okay, we have the data. Now have the agent do the restore.
Dan Egnorbb9001c2009-07-27 12:20:13 -07003299 backupData.close();
Christopher Tatec7b31e32009-06-10 15:49:30 -07003300 backupData = ParcelFileDescriptor.open(backupDataName,
3301 ParcelFileDescriptor.MODE_READ_ONLY);
3302
Dan Egnorbb9001c2009-07-27 12:20:13 -07003303 newState = ParcelFileDescriptor.open(newStateName,
3304 ParcelFileDescriptor.MODE_READ_WRITE |
3305 ParcelFileDescriptor.MODE_CREATE |
3306 ParcelFileDescriptor.MODE_TRUNCATE);
3307
Christopher Tate44a27902010-01-27 17:15:49 -08003308 // Kick off the restore, checking for hung agents
3309 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL);
3310 agent.doRestore(backupData, appVersionCode, newState, token, mBackupManagerBinder);
3311 boolean success = waitUntilOperationComplete(token);
3312
3313 if (!success) {
3314 throw new RuntimeException("restore timeout");
3315 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003316
3317 // if everything went okay, remember the recorded state now
Christopher Tate90967f42009-09-20 15:28:33 -07003318 //
3319 // !!! TODO: the restored data should be migrated on the server
3320 // side into the current dataset. In that case the new state file
3321 // we just created would reflect the data already extant in the
3322 // backend, so there'd be nothing more to do. Until that happens,
3323 // however, we need to make sure that we record the data to the
3324 // current backend dataset. (Yes, this means shipping the data over
3325 // the wire in both directions. That's bad, but consistency comes
3326 // first, then efficiency.) Once we introduce server-side data
3327 // migration to the newly-restored device's dataset, we will change
3328 // the following from a discard of the newly-written state to the
3329 // "correct" operation of renaming into the canonical state blob.
3330 newStateName.delete(); // TODO: remove; see above comment
3331 //newStateName.renameTo(savedStateName); // TODO: replace with this
3332
Dan Egnorbb9001c2009-07-27 12:20:13 -07003333 int size = (int) backupDataName.length();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003334 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, packageName, size);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003335 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003336 Slog.e(TAG, "Error restoring data for " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003337 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
Dan Egnorbb9001c2009-07-27 12:20:13 -07003338
Christopher Tate96733042009-07-20 14:49:13 -07003339 // If the agent fails restore, it might have put the app's data
3340 // into an incoherent state. For consistency we wipe its data
3341 // again in this case before propagating the exception
Christopher Tate96733042009-07-20 14:49:13 -07003342 clearApplicationDataSynchronous(packageName);
Christopher Tate1531dc82009-07-24 16:37:43 -07003343 } finally {
3344 backupDataName.delete();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003345 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
3346 try { if (newState != null) newState.close(); } catch (IOException e) {}
3347 backupData = newState = null;
Christopher Tate4a627c72011-04-01 14:43:32 -07003348 synchronized (mCurrentOperations) {
3349 mCurrentOperations.delete(token);
3350 }
Chris Tate249345b2010-10-29 12:57:04 -07003351
3352 // If we know a priori that we'll need to perform a full post-restore backup
3353 // pass, clear the new state file data. This means we're discarding work that
3354 // was just done by the app's agent, but this way the agent doesn't need to
3355 // take any special action based on global device state.
3356 if (needFullBackup) {
3357 newStateName.delete();
3358 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003359 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003360 }
3361 }
3362
Christopher Tate44a27902010-01-27 17:15:49 -08003363 class PerformClearTask implements Runnable {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003364 IBackupTransport mTransport;
3365 PackageInfo mPackage;
3366
Christopher Tate44a27902010-01-27 17:15:49 -08003367 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003368 mTransport = transport;
3369 mPackage = packageInfo;
3370 }
3371
Christopher Tateee0e78a2009-07-02 11:17:03 -07003372 public void run() {
3373 try {
3374 // Clear the on-device backup state to ensure a full backup next time
3375 File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
3376 File stateFile = new File(stateDir, mPackage.packageName);
3377 stateFile.delete();
3378
3379 // Tell the transport to remove all the persistent storage for the app
Christopher Tate13f4a642009-09-30 20:06:45 -07003380 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003381 mTransport.clearBackupData(mPackage);
3382 } catch (RemoteException e) {
3383 // can't happen; the transport is local
3384 } finally {
3385 try {
Christopher Tate13f4a642009-09-30 20:06:45 -07003386 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003387 mTransport.finishBackup();
3388 } catch (RemoteException e) {
3389 // can't happen; the transport is local
3390 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003391
3392 // Last but not least, release the cpu
3393 mWakelock.release();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003394 }
3395 }
3396 }
3397
Christopher Tate44a27902010-01-27 17:15:49 -08003398 class PerformInitializeTask implements Runnable {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003399 HashSet<String> mQueue;
3400
Christopher Tate44a27902010-01-27 17:15:49 -08003401 PerformInitializeTask(HashSet<String> transportNames) {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003402 mQueue = transportNames;
3403 }
3404
Christopher Tate4cc86e12009-09-21 19:36:51 -07003405 public void run() {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003406 try {
3407 for (String transportName : mQueue) {
3408 IBackupTransport transport = getTransport(transportName);
3409 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003410 Slog.e(TAG, "Requested init for " + transportName + " but not found");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003411 continue;
3412 }
3413
Joe Onorato8a9b2202010-02-26 18:56:32 -08003414 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003415 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
Dan Egnor726247c2009-09-29 19:12:31 -07003416 long startRealtime = SystemClock.elapsedRealtime();
3417 int status = transport.initializeDevice();
Christopher Tate4cc86e12009-09-21 19:36:51 -07003418
Christopher Tate4cc86e12009-09-21 19:36:51 -07003419 if (status == BackupConstants.TRANSPORT_OK) {
3420 status = transport.finishBackup();
3421 }
3422
3423 // Okay, the wipe really happened. Clean up our local bookkeeping.
3424 if (status == BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003425 Slog.i(TAG, "Device init successful");
Dan Egnor726247c2009-09-29 19:12:31 -07003426 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003427 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07003428 resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003429 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003430 synchronized (mQueueLock) {
3431 recordInitPendingLocked(false, transportName);
3432 }
Dan Egnor726247c2009-09-29 19:12:31 -07003433 } else {
3434 // If this didn't work, requeue this one and try again
3435 // after a suitable interval
Joe Onorato8a9b2202010-02-26 18:56:32 -08003436 Slog.e(TAG, "Transport error in initializeDevice()");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003437 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003438 synchronized (mQueueLock) {
3439 recordInitPendingLocked(true, transportName);
3440 }
3441 // do this via another alarm to make sure of the wakelock states
3442 long delay = transport.requestBackupTime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08003443 if (DEBUG) Slog.w(TAG, "init failed on "
Christopher Tate4cc86e12009-09-21 19:36:51 -07003444 + transportName + " resched in " + delay);
3445 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
3446 System.currentTimeMillis() + delay, mRunInitIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003447 }
Christopher Tate4cc86e12009-09-21 19:36:51 -07003448 }
3449 } catch (RemoteException e) {
3450 // can't happen; the transports are local
3451 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003452 Slog.e(TAG, "Unexpected error performing init", e);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003453 } finally {
Christopher Tatec2af5d32010-02-02 15:18:58 -08003454 // Done; release the wakelock
Christopher Tate4cc86e12009-09-21 19:36:51 -07003455 mWakelock.release();
3456 }
3457 }
3458 }
3459
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003460 private void dataChangedImpl(String packageName) {
3461 HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3462 dataChangedImpl(packageName, targets);
3463 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003464
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003465 private void dataChangedImpl(String packageName, HashSet<ApplicationInfo> targets) {
Christopher Tate487529a2009-04-29 14:03:25 -07003466 // Record that we need a backup pass for the caller. Since multiple callers
3467 // may share a uid, we need to note all candidates within that uid and schedule
3468 // a backup pass for each of them.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003469 EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003470
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003471 if (targets == null) {
3472 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3473 + " uid=" + Binder.getCallingUid());
3474 return;
3475 }
3476
3477 synchronized (mQueueLock) {
3478 // Note that this client has made data changes that need to be backed up
3479 for (ApplicationInfo app : targets) {
3480 // validate the caller-supplied package name against the known set of
3481 // packages associated with this uid
3482 if (app.packageName.equals(packageName)) {
3483 // Add the caller to the set of pending backups. If there is
3484 // one already there, then overwrite it, but no harm done.
Christopher Tate4a627c72011-04-01 14:43:32 -07003485 BackupRequest req = new BackupRequest(app);
Christopher Tatec28083a2010-12-14 16:16:44 -08003486 if (mPendingBackups.put(app.packageName, req) == null) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003487 // Journal this request in case of crash. The put()
3488 // operation returned null when this package was not already
3489 // in the set; we want to avoid touching the disk redundantly.
3490 writeToJournalLocked(packageName);
3491
3492 if (DEBUG) {
3493 int numKeys = mPendingBackups.size();
3494 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
3495 for (BackupRequest b : mPendingBackups.values()) {
3496 Slog.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
3497 }
3498 }
3499 }
3500 }
3501 }
3502 }
3503 }
3504
3505 // Note: packageName is currently unused, but may be in the future
3506 private HashSet<ApplicationInfo> dataChangedTargets(String packageName) {
Christopher Tate63d27002009-06-16 17:16:42 -07003507 // If the caller does not hold the BACKUP permission, it can only request a
3508 // backup of its own data.
Dianne Hackborncf098292009-07-01 19:55:20 -07003509 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tate63d27002009-06-16 17:16:42 -07003510 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003511 synchronized (mBackupParticipants) {
3512 return mBackupParticipants.get(Binder.getCallingUid());
3513 }
3514 }
3515
3516 // a caller with full permission can ask to back up any participating app
3517 // !!! TODO: allow backup of ANY app?
3518 HashSet<ApplicationInfo> targets = new HashSet<ApplicationInfo>();
3519 synchronized (mBackupParticipants) {
Christopher Tate63d27002009-06-16 17:16:42 -07003520 int N = mBackupParticipants.size();
3521 for (int i = 0; i < N; i++) {
3522 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3523 if (s != null) {
3524 targets.addAll(s);
3525 }
3526 }
3527 }
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003528 return targets;
Christopher Tate487529a2009-04-29 14:03:25 -07003529 }
Christopher Tate46758122009-05-06 11:22:00 -07003530
Christopher Tatecde87f42009-06-12 12:55:53 -07003531 private void writeToJournalLocked(String str) {
Dan Egnor852f8e42009-09-30 11:20:45 -07003532 RandomAccessFile out = null;
3533 try {
3534 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
3535 out = new RandomAccessFile(mJournal, "rws");
3536 out.seek(out.length());
3537 out.writeUTF(str);
3538 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003539 Slog.e(TAG, "Can't write " + str + " to backup journal", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07003540 mJournal = null;
3541 } finally {
3542 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tatecde87f42009-06-12 12:55:53 -07003543 }
3544 }
3545
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003546 // ----- IBackupManager binder interface -----
3547
3548 public void dataChanged(final String packageName) {
3549 final HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3550 if (targets == null) {
3551 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3552 + " uid=" + Binder.getCallingUid());
3553 return;
3554 }
3555
3556 mBackupHandler.post(new Runnable() {
3557 public void run() {
3558 dataChangedImpl(packageName, targets);
3559 }
3560 });
3561 }
3562
Christopher Tateee0e78a2009-07-02 11:17:03 -07003563 // Clear the given package's backup data from the current transport
3564 public void clearBackupData(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003565 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003566 PackageInfo info;
3567 try {
3568 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
3569 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003570 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003571 return;
3572 }
3573
3574 // If the caller does not hold the BACKUP permission, it can only request a
3575 // wipe of its own backed-up data.
3576 HashSet<ApplicationInfo> apps;
Christopher Tate4e3e50c2009-07-02 12:14:05 -07003577 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tateee0e78a2009-07-02 11:17:03 -07003578 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
3579 apps = mBackupParticipants.get(Binder.getCallingUid());
3580 } else {
3581 // a caller with full permission can ask to back up any participating app
3582 // !!! TODO: allow data-clear of ANY app?
Joe Onorato8a9b2202010-02-26 18:56:32 -08003583 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003584 apps = new HashSet<ApplicationInfo>();
3585 int N = mBackupParticipants.size();
3586 for (int i = 0; i < N; i++) {
3587 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3588 if (s != null) {
3589 apps.addAll(s);
3590 }
3591 }
3592 }
3593
3594 // now find the given package in the set of candidate apps
3595 for (ApplicationInfo app : apps) {
3596 if (app.packageName.equals(packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003597 if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003598 // found it; fire off the clear request
3599 synchronized (mQueueLock) {
Christopher Tateaa93b042009-08-05 18:21:40 -07003600 long oldId = Binder.clearCallingIdentity();
Christopher Tateb6787f22009-07-02 17:40:45 -07003601 mWakelock.acquire();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003602 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
3603 new ClearParams(getTransport(mCurrentTransport), info));
3604 mBackupHandler.sendMessage(msg);
Christopher Tateaa93b042009-08-05 18:21:40 -07003605 Binder.restoreCallingIdentity(oldId);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003606 }
3607 break;
3608 }
3609 }
3610 }
3611
Christopher Tateace7f092009-06-15 18:07:25 -07003612 // Run a backup pass immediately for any applications that have declared
3613 // that they have pending updates.
Dan Egnor852f8e42009-09-30 11:20:45 -07003614 public void backupNow() {
Joe Onorato5933a492009-07-23 18:24:08 -04003615 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
Christopher Tate043dadc2009-06-02 16:11:00 -07003616
Joe Onorato8a9b2202010-02-26 18:56:32 -08003617 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
Christopher Tate46758122009-05-06 11:22:00 -07003618 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07003619 // Because the alarms we are using can jitter, and we want an *immediate*
3620 // backup pass to happen, we restart the timer beginning with "next time,"
3621 // then manually fire the backup trigger intent ourselves.
3622 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tateb6787f22009-07-02 17:40:45 -07003623 try {
Christopher Tateb6787f22009-07-02 17:40:45 -07003624 mRunBackupIntent.send();
3625 } catch (PendingIntent.CanceledException e) {
3626 // should never happen
Joe Onorato8a9b2202010-02-26 18:56:32 -08003627 Slog.e(TAG, "run-backup intent cancelled!");
Christopher Tateb6787f22009-07-02 17:40:45 -07003628 }
Christopher Tate46758122009-05-06 11:22:00 -07003629 }
3630 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003631
Christopher Tate4a627c72011-04-01 14:43:32 -07003632 // Run a *full* backup pass for the given package, writing the resulting data stream
3633 // to the supplied file descriptor. This method is synchronous and does not return
3634 // to the caller until the backup has been completed.
3635 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
3636 boolean doAllApps, String[] pkgList) {
3637 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3638
3639 // Validate
3640 if (!doAllApps) {
3641 if (!includeShared) {
3642 // If we're backing up shared data (sdcard or equivalent), then we can run
3643 // without any supplied app names. Otherwise, we'd be doing no work, so
3644 // report the error.
3645 if (pkgList == null || pkgList.length == 0) {
3646 throw new IllegalArgumentException(
3647 "Backup requested but neither shared nor any apps named");
3648 }
3649 }
3650 }
3651
3652 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
3653 + " shared=" + includeShared + " all=" + doAllApps
3654 + " pkgs=" + pkgList);
3655
3656 long oldId = Binder.clearCallingIdentity();
3657 try {
3658 FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
3659 doAllApps, pkgList);
3660 final int token = generateToken();
3661 synchronized (mFullConfirmations) {
3662 mFullConfirmations.put(token, params);
3663 }
3664
Christopher Tate75a99702011-05-18 16:28:19 -07003665 // start up the confirmation UI
3666 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
3667 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
3668 Slog.e(TAG, "Unable to launch full backup confirmation");
Christopher Tate4a627c72011-04-01 14:43:32 -07003669 mFullConfirmations.delete(token);
3670 return;
3671 }
Christopher Tate75a99702011-05-18 16:28:19 -07003672
3673 // make sure the screen is lit for the user interaction
Christopher Tate4a627c72011-04-01 14:43:32 -07003674 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3675
3676 // start the confirmation countdown
Christopher Tate75a99702011-05-18 16:28:19 -07003677 startConfirmationTimeout(token, params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003678
3679 // wait for the backup to be performed
3680 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
3681 waitForCompletion(params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003682 } finally {
Christopher Tate4a627c72011-04-01 14:43:32 -07003683 try {
3684 fd.close();
3685 } catch (IOException e) {
3686 // just eat it
3687 }
Christopher Tate75a99702011-05-18 16:28:19 -07003688 Binder.restoreCallingIdentity(oldId);
Christopher Tate4a627c72011-04-01 14:43:32 -07003689 }
Christopher Tate75a99702011-05-18 16:28:19 -07003690 if (DEBUG) Slog.d(TAG, "Full backup done; returning to caller");
3691 }
3692
3693 public void fullRestore(ParcelFileDescriptor fd) {
3694 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3695 Slog.i(TAG, "Beginning full restore...");
3696
3697 long oldId = Binder.clearCallingIdentity();
3698
3699 try {
3700 FullRestoreParams params = new FullRestoreParams(fd);
3701 final int token = generateToken();
3702 synchronized (mFullConfirmations) {
3703 mFullConfirmations.put(token, params);
3704 }
3705
3706 // start up the confirmation UI
3707 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
3708 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
3709 Slog.e(TAG, "Unable to launch full restore confirmation");
3710 mFullConfirmations.delete(token);
3711 return;
3712 }
3713
3714 // make sure the screen is lit for the user interaction
3715 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3716
3717 // start the confirmation countdown
3718 startConfirmationTimeout(token, params);
3719
3720 // wait for the restore to be performed
3721 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
3722 waitForCompletion(params);
3723 } finally {
3724 try {
3725 fd.close();
3726 } catch (IOException e) {
3727 Slog.w(TAG, "Error trying to close fd after full restore: " + e);
3728 }
3729 Binder.restoreCallingIdentity(oldId);
3730 Slog.i(TAG, "Full restore completed");
3731 }
3732 }
3733
3734 boolean startConfirmationUi(int token, String action) {
3735 try {
3736 Intent confIntent = new Intent(action);
3737 confIntent.setClassName("com.android.backupconfirm",
3738 "com.android.backupconfirm.BackupRestoreConfirmation");
3739 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
3740 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3741 mContext.startActivity(confIntent);
3742 } catch (ActivityNotFoundException e) {
3743 return false;
3744 }
3745 return true;
3746 }
3747
3748 void startConfirmationTimeout(int token, FullParams params) {
3749 if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
3750 + TIMEOUT_FULL_CONFIRMATION + " millis");
3751 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
3752 token, 0, params);
3753 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
Christopher Tate4a627c72011-04-01 14:43:32 -07003754 }
3755
3756 void waitForCompletion(FullParams params) {
3757 synchronized (params.latch) {
3758 while (params.latch.get() == false) {
3759 try {
3760 params.latch.wait();
3761 } catch (InterruptedException e) { /* never interrupted */ }
3762 }
3763 }
3764 }
3765
3766 void signalFullBackupRestoreCompletion(FullParams params) {
3767 synchronized (params.latch) {
3768 params.latch.set(true);
3769 params.latch.notifyAll();
3770 }
3771 }
3772
3773 // Confirm that the previously-requested full backup/restore operation can proceed. This
3774 // is used to require a user-facing disclosure about the operation.
3775 public void acknowledgeFullBackupOrRestore(int token, boolean allow,
3776 IFullBackupRestoreObserver observer) {
3777 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
3778 + " allow=" + allow);
3779
3780 // TODO: possibly require not just this signature-only permission, but even
3781 // require that the specific designated confirmation-UI app uid is the caller?
3782 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3783
3784 long oldId = Binder.clearCallingIdentity();
3785 try {
3786
3787 FullParams params;
3788 synchronized (mFullConfirmations) {
3789 params = mFullConfirmations.get(token);
3790 if (params != null) {
3791 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
3792 mFullConfirmations.delete(token);
3793
3794 if (allow) {
3795 params.observer = observer;
3796 final int verb = params instanceof FullBackupParams
Christopher Tate75a99702011-05-18 16:28:19 -07003797 ? MSG_RUN_FULL_BACKUP
Christopher Tate4a627c72011-04-01 14:43:32 -07003798 : MSG_RUN_FULL_RESTORE;
3799
Christopher Tate75a99702011-05-18 16:28:19 -07003800 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
Christopher Tate4a627c72011-04-01 14:43:32 -07003801 mWakelock.acquire();
3802 Message msg = mBackupHandler.obtainMessage(verb, params);
3803 mBackupHandler.sendMessage(msg);
3804 } else {
3805 Slog.w(TAG, "User rejected full backup/restore operation");
3806 // indicate completion without having actually transferred any data
3807 signalFullBackupRestoreCompletion(params);
3808 }
3809 } else {
3810 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
3811 }
3812 }
3813 } finally {
3814 Binder.restoreCallingIdentity(oldId);
3815 }
3816 }
3817
Christopher Tate8031a3d2009-07-06 16:36:05 -07003818 // Enable/disable the backup service
Christopher Tate6ef58a12009-06-29 14:56:28 -07003819 public void setBackupEnabled(boolean enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003820 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4a627c72011-04-01 14:43:32 -07003821 "setBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07003822
Joe Onorato8a9b2202010-02-26 18:56:32 -08003823 Slog.i(TAG, "Backup enabled => " + enable);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003824
Christopher Tate6ef58a12009-06-29 14:56:28 -07003825 boolean wasEnabled = mEnabled;
3826 synchronized (this) {
Dianne Hackborncf098292009-07-01 19:55:20 -07003827 Settings.Secure.putInt(mContext.getContentResolver(),
3828 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
Christopher Tate6ef58a12009-06-29 14:56:28 -07003829 mEnabled = enable;
3830 }
3831
Christopher Tate49401dd2009-07-01 12:34:29 -07003832 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07003833 if (enable && !wasEnabled && mProvisioned) {
Christopher Tate49401dd2009-07-01 12:34:29 -07003834 // if we've just been enabled, start scheduling backup passes
Christopher Tate8031a3d2009-07-06 16:36:05 -07003835 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tate49401dd2009-07-01 12:34:29 -07003836 } else if (!enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003837 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08003838 if (DEBUG) Slog.i(TAG, "Opting out of backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003839
Christopher Tateb6787f22009-07-02 17:40:45 -07003840 mAlarmManager.cancel(mRunBackupIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003841
3842 // This also constitutes an opt-out, so we wipe any data for
3843 // this device from the backend. We start that process with
3844 // an alarm in order to guarantee wakelock states.
3845 if (wasEnabled && mProvisioned) {
3846 // NOTE: we currently flush every registered transport, not just
3847 // the currently-active one.
3848 HashSet<String> allTransports;
3849 synchronized (mTransports) {
3850 allTransports = new HashSet<String>(mTransports.keySet());
3851 }
3852 // build the set of transports for which we are posting an init
3853 for (String transport : allTransports) {
3854 recordInitPendingLocked(true, transport);
3855 }
3856 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
3857 mRunInitIntent);
3858 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07003859 }
3860 }
Christopher Tate49401dd2009-07-01 12:34:29 -07003861 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07003862
Christopher Tatecce9da52010-02-03 15:11:15 -08003863 // Enable/disable automatic restore of app data at install time
3864 public void setAutoRestore(boolean doAutoRestore) {
3865 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3866 "setBackupEnabled");
3867
Joe Onorato8a9b2202010-02-26 18:56:32 -08003868 Slog.i(TAG, "Auto restore => " + doAutoRestore);
Christopher Tatecce9da52010-02-03 15:11:15 -08003869
3870 synchronized (this) {
3871 Settings.Secure.putInt(mContext.getContentResolver(),
3872 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
3873 mAutoRestore = doAutoRestore;
3874 }
3875 }
3876
Christopher Tate8031a3d2009-07-06 16:36:05 -07003877 // Mark the backup service as having been provisioned
3878 public void setBackupProvisioned(boolean available) {
3879 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3880 "setBackupProvisioned");
3881
3882 boolean wasProvisioned = mProvisioned;
3883 synchronized (this) {
3884 Settings.Secure.putInt(mContext.getContentResolver(),
3885 Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
3886 mProvisioned = available;
3887 }
3888
3889 synchronized (mQueueLock) {
3890 if (available && !wasProvisioned && mEnabled) {
3891 // we're now good to go, so start the backup alarms
3892 startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
3893 } else if (!available) {
3894 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08003895 Slog.w(TAG, "Backup service no longer provisioned");
Christopher Tate8031a3d2009-07-06 16:36:05 -07003896 mAlarmManager.cancel(mRunBackupIntent);
3897 }
3898 }
3899 }
3900
3901 private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
Dan Egnorc1c49c02009-10-30 17:35:39 -07003902 // We used to use setInexactRepeating(), but that may be linked to
3903 // backups running at :00 more often than not, creating load spikes.
3904 // Schedule at an exact time for now, and also add a bit of "fuzz".
3905
3906 Random random = new Random();
3907 long when = System.currentTimeMillis() + delayBeforeFirstBackup +
3908 random.nextInt(FUZZ_MILLIS);
3909 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
3910 BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
Christopher Tate55f931a2009-09-29 17:17:34 -07003911 mNextBackupPass = when;
Christopher Tate8031a3d2009-07-06 16:36:05 -07003912 }
3913
Christopher Tate6ef58a12009-06-29 14:56:28 -07003914 // Report whether the backup mechanism is currently enabled
3915 public boolean isBackupEnabled() {
Joe Onorato5933a492009-07-23 18:24:08 -04003916 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07003917 return mEnabled; // no need to synchronize just to read it
3918 }
3919
Christopher Tate91717492009-06-26 21:07:13 -07003920 // Report the name of the currently active transport
3921 public String getCurrentTransport() {
Joe Onorato5933a492009-07-23 18:24:08 -04003922 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4e3e50c2009-07-02 12:14:05 -07003923 "getCurrentTransport");
Joe Onorato8a9b2202010-02-26 18:56:32 -08003924 if (DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -07003925 return mCurrentTransport;
Christopher Tateace7f092009-06-15 18:07:25 -07003926 }
3927
Christopher Tate91717492009-06-26 21:07:13 -07003928 // Report all known, available backup transports
3929 public String[] listAllTransports() {
Christopher Tate34ebd0e2009-07-06 15:44:54 -07003930 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
Christopher Tate043dadc2009-06-02 16:11:00 -07003931
Christopher Tate91717492009-06-26 21:07:13 -07003932 String[] list = null;
3933 ArrayList<String> known = new ArrayList<String>();
3934 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
3935 if (entry.getValue() != null) {
3936 known.add(entry.getKey());
3937 }
3938 }
3939
3940 if (known.size() > 0) {
3941 list = new String[known.size()];
3942 known.toArray(list);
3943 }
3944 return list;
3945 }
3946
3947 // Select which transport to use for the next backup operation. If the given
3948 // name is not one of the available transports, no action is taken and the method
3949 // returns null.
3950 public String selectBackupTransport(String transport) {
Joe Onorato5933a492009-07-23 18:24:08 -04003951 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
Christopher Tate91717492009-06-26 21:07:13 -07003952
3953 synchronized (mTransports) {
3954 String prevTransport = null;
3955 if (mTransports.get(transport) != null) {
3956 prevTransport = mCurrentTransport;
3957 mCurrentTransport = transport;
Dianne Hackborncf098292009-07-01 19:55:20 -07003958 Settings.Secure.putString(mContext.getContentResolver(),
3959 Settings.Secure.BACKUP_TRANSPORT, transport);
Joe Onorato8a9b2202010-02-26 18:56:32 -08003960 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
Christopher Tate91717492009-06-26 21:07:13 -07003961 + " returning " + prevTransport);
3962 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003963 Slog.w(TAG, "Attempt to select unavailable transport " + transport);
Christopher Tate91717492009-06-26 21:07:13 -07003964 }
3965 return prevTransport;
3966 }
Christopher Tate043dadc2009-06-02 16:11:00 -07003967 }
3968
Christopher Tatef5e1c292010-12-08 18:40:26 -08003969 // Supply the configuration Intent for the given transport. If the name is not one
3970 // of the available transports, or if the transport does not supply any configuration
3971 // UI, the method returns null.
3972 public Intent getConfigurationIntent(String transportName) {
3973 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3974 "getConfigurationIntent");
3975
3976 synchronized (mTransports) {
3977 final IBackupTransport transport = mTransports.get(transportName);
3978 if (transport != null) {
3979 try {
3980 final Intent intent = transport.configurationIntent();
3981 if (DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
3982 + intent);
3983 return intent;
3984 } catch (RemoteException e) {
3985 /* fall through to return null */
3986 }
3987 }
3988 }
3989
3990 return null;
3991 }
3992
3993 // Supply the configuration summary string for the given transport. If the name is
3994 // not one of the available transports, or if the transport does not supply any
3995 // summary / destination string, the method can return null.
3996 //
3997 // This string is used VERBATIM as the summary text of the relevant Settings item!
3998 public String getDestinationString(String transportName) {
3999 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4000 "getConfigurationIntent");
4001
4002 synchronized (mTransports) {
4003 final IBackupTransport transport = mTransports.get(transportName);
4004 if (transport != null) {
4005 try {
4006 final String text = transport.currentDestinationString();
4007 if (DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
4008 return text;
4009 } catch (RemoteException e) {
4010 /* fall through to return null */
4011 }
4012 }
4013 }
4014
4015 return null;
4016 }
4017
Christopher Tate043dadc2009-06-02 16:11:00 -07004018 // Callback: a requested backup agent has been instantiated. This should only
4019 // be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07004020 public void agentConnected(String packageName, IBinder agentBinder) {
Christopher Tate043dadc2009-06-02 16:11:00 -07004021 synchronized(mAgentConnectLock) {
4022 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004023 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
Christopher Tate043dadc2009-06-02 16:11:00 -07004024 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
4025 mConnectedAgent = agent;
4026 mConnecting = false;
4027 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004028 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07004029 + " claiming agent connected");
4030 }
4031 mAgentConnectLock.notifyAll();
4032 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004033 }
4034
4035 // Callback: a backup agent has failed to come up, or has unexpectedly quit.
4036 // If the agent failed to come up in the first place, the agentBinder argument
Christopher Tate043dadc2009-06-02 16:11:00 -07004037 // will be null. This should only be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07004038 public void agentDisconnected(String packageName) {
4039 // TODO: handle backup being interrupted
Christopher Tate043dadc2009-06-02 16:11:00 -07004040 synchronized(mAgentConnectLock) {
4041 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
4042 mConnectedAgent = null;
4043 mConnecting = false;
4044 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004045 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07004046 + " claiming agent disconnected");
4047 }
4048 mAgentConnectLock.notifyAll();
4049 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004050 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004051
Christopher Tate1bb69062010-02-19 17:02:12 -08004052 // An application being installed will need a restore pass, then the Package Manager
4053 // will need to be told when the restore is finished.
4054 public void restoreAtInstall(String packageName, int token) {
4055 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004056 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate1bb69062010-02-19 17:02:12 -08004057 + " attemping install-time restore");
4058 return;
4059 }
4060
4061 long restoreSet = getAvailableRestoreToken(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08004062 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
Christopher Tate1bb69062010-02-19 17:02:12 -08004063 + " token=" + Integer.toHexString(token));
4064
Christopher Tatef0872722010-02-25 15:22:48 -08004065 if (mAutoRestore && mProvisioned && restoreSet != 0) {
Christopher Tate1bb69062010-02-19 17:02:12 -08004066 // okay, we're going to attempt a restore of this package from this restore set.
4067 // The eventual message back into the Package Manager to run the post-install
4068 // steps for 'token' will be issued from the restore handling code.
4069
4070 // We can use a synthetic PackageInfo here because:
4071 // 1. We know it's valid, since the Package Manager supplied the name
4072 // 2. Only the packageName field will be used by the restore code
4073 PackageInfo pkg = new PackageInfo();
4074 pkg.packageName = packageName;
4075
4076 mWakelock.acquire();
4077 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
4078 msg.obj = new RestoreParams(getTransport(mCurrentTransport), null,
Chris Tate249345b2010-10-29 12:57:04 -07004079 restoreSet, pkg, token, true);
Christopher Tate1bb69062010-02-19 17:02:12 -08004080 mBackupHandler.sendMessage(msg);
4081 } else {
Christopher Tatef0872722010-02-25 15:22:48 -08004082 // Auto-restore disabled or no way to attempt a restore; just tell the Package
4083 // Manager to proceed with the post-install handling for this package.
Joe Onorato8a9b2202010-02-26 18:56:32 -08004084 if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore");
Christopher Tate1bb69062010-02-19 17:02:12 -08004085 try {
4086 mPackageManagerBinder.finishPackageInstall(token);
4087 } catch (RemoteException e) { /* can't happen */ }
4088 }
4089 }
4090
Christopher Tate8c850b72009-06-07 19:33:20 -07004091 // Hand off a restore session
Chris Tate44ab8452010-11-16 15:10:49 -08004092 public IRestoreSession beginRestoreSession(String packageName, String transport) {
4093 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
4094 + " transport=" + transport);
4095
4096 boolean needPermission = true;
4097 if (transport == null) {
4098 transport = mCurrentTransport;
4099
4100 if (packageName != null) {
4101 PackageInfo app = null;
4102 try {
4103 app = mPackageManager.getPackageInfo(packageName, 0);
4104 } catch (NameNotFoundException nnf) {
4105 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
4106 throw new IllegalArgumentException("Package " + packageName + " not found");
4107 }
4108
4109 if (app.applicationInfo.uid == Binder.getCallingUid()) {
4110 // So: using the current active transport, and the caller has asked
4111 // that its own package will be restored. In this narrow use case
4112 // we do not require the caller to hold the permission.
4113 needPermission = false;
4114 }
4115 }
4116 }
4117
4118 if (needPermission) {
4119 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4120 "beginRestoreSession");
4121 } else {
4122 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
4123 }
Christopher Tatef68eb502009-06-16 11:02:01 -07004124
4125 synchronized(this) {
4126 if (mActiveRestoreSession != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004127 Slog.d(TAG, "Restore session requested but one already active");
Christopher Tatef68eb502009-06-16 11:02:01 -07004128 return null;
4129 }
Chris Tate44ab8452010-11-16 15:10:49 -08004130 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
Christopher Tate73a3cb32010-12-13 18:27:26 -08004131 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
Christopher Tatef68eb502009-06-16 11:02:01 -07004132 }
4133 return mActiveRestoreSession;
Christopher Tate8c850b72009-06-07 19:33:20 -07004134 }
Christopher Tate043dadc2009-06-02 16:11:00 -07004135
Christopher Tate73a3cb32010-12-13 18:27:26 -08004136 void clearRestoreSession(ActiveRestoreSession currentSession) {
4137 synchronized(this) {
4138 if (currentSession != mActiveRestoreSession) {
4139 Slog.e(TAG, "ending non-current restore session");
4140 } else {
4141 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
4142 mActiveRestoreSession = null;
4143 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
4144 }
4145 }
4146 }
4147
Christopher Tate44a27902010-01-27 17:15:49 -08004148 // Note that a currently-active backup agent has notified us that it has
4149 // completed the given outstanding asynchronous backup/restore operation.
4150 public void opComplete(int token) {
4151 synchronized (mCurrentOpLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004152 if (DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
Christopher Tate44a27902010-01-27 17:15:49 -08004153 mCurrentOperations.put(token, OP_ACKNOWLEDGED);
4154 mCurrentOpLock.notifyAll();
4155 }
4156 }
4157
Christopher Tate9b3905c2009-06-08 15:24:01 -07004158 // ----- Restore session -----
4159
Christopher Tate80202c82010-01-25 19:37:47 -08004160 class ActiveRestoreSession extends IRestoreSession.Stub {
Christopher Tatef68eb502009-06-16 11:02:01 -07004161 private static final String TAG = "RestoreSession";
4162
Chris Tate44ab8452010-11-16 15:10:49 -08004163 private String mPackageName;
Christopher Tate9b3905c2009-06-08 15:24:01 -07004164 private IBackupTransport mRestoreTransport = null;
4165 RestoreSet[] mRestoreSets = null;
Christopher Tate73a3cb32010-12-13 18:27:26 -08004166 boolean mEnded = false;
Christopher Tate9b3905c2009-06-08 15:24:01 -07004167
Chris Tate44ab8452010-11-16 15:10:49 -08004168 ActiveRestoreSession(String packageName, String transport) {
4169 mPackageName = packageName;
Christopher Tate91717492009-06-26 21:07:13 -07004170 mRestoreTransport = getTransport(transport);
Christopher Tate9b3905c2009-06-08 15:24:01 -07004171 }
4172
4173 // --- Binder interface ---
Christopher Tate2d449afe2010-03-29 19:14:24 -07004174 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
Joe Onorato5933a492009-07-23 18:24:08 -04004175 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004176 "getAvailableRestoreSets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004177 if (observer == null) {
4178 throw new IllegalArgumentException("Observer must not be null");
4179 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004180
Christopher Tate73a3cb32010-12-13 18:27:26 -08004181 if (mEnded) {
4182 throw new IllegalStateException("Restore session already ended");
4183 }
4184
Christopher Tate1bb69062010-02-19 17:02:12 -08004185 long oldId = Binder.clearCallingIdentity();
Christopher Tatef68eb502009-06-16 11:02:01 -07004186 try {
Christopher Tate43383042009-07-13 15:17:13 -07004187 if (mRestoreTransport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004188 Slog.w(TAG, "Null transport getting restore sets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004189 return -1;
Dan Egnor0084da52009-07-29 12:57:16 -07004190 }
Christopher Tate2d449afe2010-03-29 19:14:24 -07004191 // spin off the transport request to our service thread
4192 mWakelock.acquire();
4193 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
4194 new RestoreGetSetsParams(mRestoreTransport, this, observer));
4195 mBackupHandler.sendMessage(msg);
4196 return 0;
Dan Egnor0084da52009-07-29 12:57:16 -07004197 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004198 Slog.e(TAG, "Error in getAvailableRestoreSets", e);
Christopher Tate2d449afe2010-03-29 19:14:24 -07004199 return -1;
Christopher Tate1bb69062010-02-19 17:02:12 -08004200 } finally {
4201 Binder.restoreCallingIdentity(oldId);
Christopher Tatef68eb502009-06-16 11:02:01 -07004202 }
Christopher Tate9b3905c2009-06-08 15:24:01 -07004203 }
4204
Christopher Tate84725812010-02-04 15:52:40 -08004205 public synchronized int restoreAll(long token, IRestoreObserver observer) {
Dan Egnor0084da52009-07-29 12:57:16 -07004206 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4207 "performRestore");
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004208
Chris Tate44ab8452010-11-16 15:10:49 -08004209 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
Christopher Tatef2c321a2009-08-10 15:43:36 -07004210 + " observer=" + observer);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004211
Christopher Tate73a3cb32010-12-13 18:27:26 -08004212 if (mEnded) {
4213 throw new IllegalStateException("Restore session already ended");
4214 }
4215
Dan Egnor0084da52009-07-29 12:57:16 -07004216 if (mRestoreTransport == null || mRestoreSets == null) {
Chris Tate44ab8452010-11-16 15:10:49 -08004217 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
4218 return -1;
4219 }
4220
4221 if (mPackageName != null) {
4222 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
Dan Egnor0084da52009-07-29 12:57:16 -07004223 return -1;
4224 }
4225
Christopher Tate21ab6a52009-09-24 18:01:46 -07004226 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07004227 for (int i = 0; i < mRestoreSets.length; i++) {
4228 if (token == mRestoreSets[i].token) {
4229 long oldId = Binder.clearCallingIdentity();
Christopher Tate21ab6a52009-09-24 18:01:46 -07004230 mWakelock.acquire();
4231 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004232 msg.obj = new RestoreParams(mRestoreTransport, observer, token, true);
Christopher Tate21ab6a52009-09-24 18:01:46 -07004233 mBackupHandler.sendMessage(msg);
4234 Binder.restoreCallingIdentity(oldId);
4235 return 0;
4236 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004237 }
4238 }
Christopher Tate0e0b4ae2009-08-10 16:13:47 -07004239
Joe Onorato8a9b2202010-02-26 18:56:32 -08004240 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
Christopher Tate9b3905c2009-06-08 15:24:01 -07004241 return -1;
4242 }
4243
Christopher Tate84725812010-02-04 15:52:40 -08004244 public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004245 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
Christopher Tate84725812010-02-04 15:52:40 -08004246
Christopher Tate73a3cb32010-12-13 18:27:26 -08004247 if (mEnded) {
4248 throw new IllegalStateException("Restore session already ended");
4249 }
4250
Chris Tate44ab8452010-11-16 15:10:49 -08004251 if (mPackageName != null) {
4252 if (! mPackageName.equals(packageName)) {
4253 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
4254 + " on session for package " + mPackageName);
4255 return -1;
4256 }
4257 }
4258
Christopher Tate84725812010-02-04 15:52:40 -08004259 PackageInfo app = null;
4260 try {
4261 app = mPackageManager.getPackageInfo(packageName, 0);
4262 } catch (NameNotFoundException nnf) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004263 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004264 return -1;
4265 }
4266
4267 // If the caller is not privileged and is not coming from the target
4268 // app's uid, throw a permission exception back to the caller.
4269 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
4270 Binder.getCallingPid(), Binder.getCallingUid());
4271 if ((perm == PackageManager.PERMISSION_DENIED) &&
4272 (app.applicationInfo.uid != Binder.getCallingUid())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004273 Slog.w(TAG, "restorePackage: bad packageName=" + packageName
Christopher Tate84725812010-02-04 15:52:40 -08004274 + " or calling uid=" + Binder.getCallingUid());
4275 throw new SecurityException("No permission to restore other packages");
4276 }
4277
Christopher Tate7d411a32010-02-26 11:27:08 -08004278 // If the package has no backup agent, we obviously cannot proceed
4279 if (app.applicationInfo.backupAgentName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004280 Slog.w(TAG, "Asked to restore package " + packageName + " with no agent");
Christopher Tate7d411a32010-02-26 11:27:08 -08004281 return -1;
4282 }
4283
Christopher Tate84725812010-02-04 15:52:40 -08004284 // So far so good; we're allowed to try to restore this package. Now
4285 // check whether there is data for it in the current dataset, falling back
4286 // to the ancestral dataset if not.
Christopher Tate1bb69062010-02-19 17:02:12 -08004287 long token = getAvailableRestoreToken(packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004288
4289 // If we didn't come up with a place to look -- no ancestral dataset and
4290 // the app has never been backed up from this device -- there's nothing
4291 // to do but return failure.
4292 if (token == 0) {
Chris Tate44ab8452010-11-16 15:10:49 -08004293 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
Christopher Tate84725812010-02-04 15:52:40 -08004294 return -1;
4295 }
4296
4297 // Ready to go: enqueue the restore request and claim success
4298 long oldId = Binder.clearCallingIdentity();
4299 mWakelock.acquire();
4300 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004301 msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0, false);
Christopher Tate84725812010-02-04 15:52:40 -08004302 mBackupHandler.sendMessage(msg);
4303 Binder.restoreCallingIdentity(oldId);
4304 return 0;
4305 }
4306
Christopher Tate73a3cb32010-12-13 18:27:26 -08004307 // Posted to the handler to tear down a restore session in a cleanly synchronized way
4308 class EndRestoreRunnable implements Runnable {
4309 BackupManagerService mBackupManager;
4310 ActiveRestoreSession mSession;
4311
4312 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
4313 mBackupManager = manager;
4314 mSession = session;
4315 }
4316
4317 public void run() {
4318 // clean up the session's bookkeeping
4319 synchronized (mSession) {
4320 try {
4321 if (mSession.mRestoreTransport != null) {
4322 mSession.mRestoreTransport.finishRestore();
4323 }
4324 } catch (Exception e) {
4325 Slog.e(TAG, "Error in finishRestore", e);
4326 } finally {
4327 mSession.mRestoreTransport = null;
4328 mSession.mEnded = true;
4329 }
4330 }
4331
4332 // clean up the BackupManagerService side of the bookkeeping
4333 // and cancel any pending timeout message
4334 mBackupManager.clearRestoreSession(mSession);
4335 }
4336 }
4337
Dan Egnor0084da52009-07-29 12:57:16 -07004338 public synchronized void endRestoreSession() {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004339 if (DEBUG) Slog.d(TAG, "endRestoreSession");
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004340
Christopher Tate73a3cb32010-12-13 18:27:26 -08004341 if (mEnded) {
4342 throw new IllegalStateException("Restore session already ended");
Dan Egnor0084da52009-07-29 12:57:16 -07004343 }
4344
Christopher Tate73a3cb32010-12-13 18:27:26 -08004345 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
Christopher Tate9b3905c2009-06-08 15:24:01 -07004346 }
4347 }
4348
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004349 @Override
4350 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004351 long identityToken = Binder.clearCallingIdentity();
4352 try {
4353 dumpInternal(pw);
4354 } finally {
4355 Binder.restoreCallingIdentity(identityToken);
4356 }
4357 }
4358
4359 private void dumpInternal(PrintWriter pw) {
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004360 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07004361 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
Christopher Tate55f931a2009-09-29 17:17:34 -07004362 + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
Christopher Tatec2af5d32010-02-02 15:18:58 -08004363 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
Christopher Tateae06ed92010-02-25 17:13:28 -08004364 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
Christopher Tate55f931a2009-09-29 17:17:34 -07004365 pw.println("Last backup pass: " + mLastBackupPass
4366 + " (now = " + System.currentTimeMillis() + ')');
4367 pw.println(" next scheduled: " + mNextBackupPass);
4368
Christopher Tate91717492009-06-26 21:07:13 -07004369 pw.println("Available transports:");
4370 for (String t : listAllTransports()) {
Dan Egnor852f8e42009-09-30 11:20:45 -07004371 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
4372 try {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004373 IBackupTransport transport = getTransport(t);
4374 File dir = new File(mBaseStateDir, transport.transportDirName());
4375 pw.println(" destination: " + transport.currentDestinationString());
4376 pw.println(" intent: " + transport.configurationIntent());
Dan Egnor852f8e42009-09-30 11:20:45 -07004377 for (File f : dir.listFiles()) {
4378 pw.println(" " + f.getName() + " - " + f.length() + " state bytes");
4379 }
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004380 } catch (Exception e) {
4381 Slog.e(TAG, "Error in transport", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07004382 pw.println(" Error: " + e);
4383 }
Christopher Tate91717492009-06-26 21:07:13 -07004384 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004385
4386 pw.println("Pending init: " + mPendingInits.size());
4387 for (String s : mPendingInits) {
4388 pw.println(" " + s);
4389 }
4390
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004391 int N = mBackupParticipants.size();
Christopher Tate55f931a2009-09-29 17:17:34 -07004392 pw.println("Participants:");
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004393 for (int i=0; i<N; i++) {
4394 int uid = mBackupParticipants.keyAt(i);
4395 pw.print(" uid: ");
4396 pw.println(uid);
Christopher Tate181fafa2009-05-14 11:12:14 -07004397 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
4398 for (ApplicationInfo app: participants) {
Christopher Tate55f931a2009-09-29 17:17:34 -07004399 pw.println(" " + app.packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004400 }
4401 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004402
Christopher Tateb49ceb32010-02-08 16:22:24 -08004403 pw.println("Ancestral packages: "
4404 + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
Christopher Tate5923c972010-04-04 17:45:35 -07004405 if (mAncestralPackages != null) {
4406 for (String pkg : mAncestralPackages) {
4407 pw.println(" " + pkg);
4408 }
Christopher Tateb49ceb32010-02-08 16:22:24 -08004409 }
4410
Christopher Tate73e02522009-07-15 14:18:26 -07004411 pw.println("Ever backed up: " + mEverStoredApps.size());
4412 for (String pkg : mEverStoredApps) {
4413 pw.println(" " + pkg);
4414 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004415
4416 pw.println("Pending backup: " + mPendingBackups.size());
Christopher Tate6aa41f42009-06-19 14:14:22 -07004417 for (BackupRequest req : mPendingBackups.values()) {
Christopher Tate6ef58a12009-06-29 14:56:28 -07004418 pw.println(" " + req);
Christopher Tate181fafa2009-05-14 11:12:14 -07004419 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004420 }
4421 }
Christopher Tate487529a2009-04-29 14:03:25 -07004422}