blob: cd58b9b98f9a3f67205206e3e32581ad4839fe22 [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 Tate75a99702011-05-18 16:28:19 -070042import android.content.pm.IPackageInstallObserver;
Christopher Tate1bb69062010-02-19 17:02:12 -080043import android.content.pm.IPackageManager;
Christopher Tate7b881282009-06-07 13:52:37 -070044import android.content.pm.PackageInfo;
Dan Egnor87a02bc2009-06-17 02:30:10 -070045import android.content.pm.PackageManager;
Jason parks1125d782011-01-12 09:47:26 -060046import android.content.pm.Signature;
Jason parksa3cdaa52011-01-13 14:15:43 -060047import android.content.pm.PackageManager.NameNotFoundException;
Christopher Tate3799bc22009-05-06 16:13:56 -070048import android.net.Uri;
Christopher Tate487529a2009-04-29 14:03:25 -070049import android.os.Binder;
Christopher Tate75a99702011-05-18 16:28:19 -070050import android.os.Build;
Christopher Tate3799bc22009-05-06 16:13:56 -070051import android.os.Bundle;
Christopher Tate22b87872009-05-04 16:41:53 -070052import android.os.Environment;
Christopher Tate487529a2009-04-29 14:03:25 -070053import android.os.Handler;
Christopher Tate44a27902010-01-27 17:15:49 -080054import android.os.HandlerThread;
Christopher Tate487529a2009-04-29 14:03:25 -070055import android.os.IBinder;
Christopher Tate44a27902010-01-27 17:15:49 -080056import android.os.Looper;
Christopher Tate487529a2009-04-29 14:03:25 -070057import android.os.Message;
Christopher Tate22b87872009-05-04 16:41:53 -070058import android.os.ParcelFileDescriptor;
Christopher Tateb6787f22009-07-02 17:40:45 -070059import android.os.PowerManager;
Christopher Tate043dadc2009-06-02 16:11:00 -070060import android.os.Process;
Christopher Tate487529a2009-04-29 14:03:25 -070061import android.os.RemoteException;
Dan Egnorbb9001c2009-07-27 12:20:13 -070062import android.os.SystemClock;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070063import android.os.WorkSource;
Oscar Montemayora8529f62009-11-18 10:14:20 -080064import android.provider.Settings;
Dan Egnorbb9001c2009-07-27 12:20:13 -070065import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080066import android.util.Slog;
Christopher Tate487529a2009-04-29 14:03:25 -070067import android.util.SparseArray;
Christopher Tate44a27902010-01-27 17:15:49 -080068import android.util.SparseIntArray;
Christopher Tate4a627c72011-04-01 14:43:32 -070069import android.util.StringBuilderPrinter;
70
Jason parksa3cdaa52011-01-13 14:15:43 -060071import com.android.internal.backup.BackupConstants;
72import com.android.internal.backup.IBackupTransport;
73import com.android.internal.backup.LocalTransport;
74import com.android.server.PackageManagerBackupAgent.Metadata;
75
Christopher Tatecde87f42009-06-12 12:55:53 -070076import java.io.EOFException;
Christopher Tate22b87872009-05-04 16:41:53 -070077import java.io.File;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070078import java.io.FileDescriptor;
Christopher Tate75a99702011-05-18 16:28:19 -070079import java.io.FileInputStream;
Christopher Tate1168baa2010-02-17 13:03:40 -080080import java.io.FileNotFoundException;
Christopher Tate4cc86e12009-09-21 19:36:51 -070081import java.io.FileOutputStream;
Christopher Tatec7b31e32009-06-10 15:49:30 -070082import java.io.IOException;
Christopher Tate75a99702011-05-18 16:28:19 -070083import java.io.InputStream;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070084import java.io.PrintWriter;
Christopher Tatecde87f42009-06-12 12:55:53 -070085import java.io.RandomAccessFile;
Christopher Tate75a99702011-05-18 16:28:19 -070086import java.text.SimpleDateFormat;
Joe Onorato8ad02812009-05-13 01:41:44 -040087import java.util.ArrayList;
Christopher Tate75a99702011-05-18 16:28:19 -070088import java.util.Date;
Joe Onorato8ad02812009-05-13 01:41:44 -040089import java.util.HashMap;
Christopher Tate487529a2009-04-29 14:03:25 -070090import java.util.HashSet;
91import java.util.List;
Christopher Tate91717492009-06-26 21:07:13 -070092import java.util.Map;
Dan Egnorc1c49c02009-10-30 17:35:39 -070093import java.util.Random;
Christopher Tateb49ceb32010-02-08 16:22:24 -080094import java.util.Set;
Christopher Tate4a627c72011-04-01 14:43:32 -070095import java.util.concurrent.atomic.AtomicBoolean;
Christopher Tate487529a2009-04-29 14:03:25 -070096
97class BackupManagerService extends IBackupManager.Stub {
98 private static final String TAG = "BackupManagerService";
Christopher Tate4a627c72011-04-01 14:43:32 -070099 private static final boolean DEBUG = true;
100
101 // Name and current contents version of the full-backup manifest file
102 static final String BACKUP_MANIFEST_FILENAME = "_manifest";
103 static final int BACKUP_MANIFEST_VERSION = 1;
Christopher Tateaa088442009-06-16 18:25:46 -0700104
Christopher Tate49401dd2009-07-01 12:34:29 -0700105 // How often we perform a backup pass. Privileged external callers can
106 // trigger an immediate pass.
Christopher Tateb6787f22009-07-02 17:40:45 -0700107 private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
Christopher Tate487529a2009-04-29 14:03:25 -0700108
Dan Egnorc1c49c02009-10-30 17:35:39 -0700109 // Random variation in backup scheduling time to avoid server load spikes
110 private static final int FUZZ_MILLIS = 5 * 60 * 1000;
111
Christopher Tate8031a3d2009-07-06 16:36:05 -0700112 // The amount of time between the initial provisioning of the device and
113 // the first backup pass.
114 private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
115
Christopher Tate45281862010-03-05 15:46:30 -0800116 private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
117 private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
118 private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
Christopher Tate487529a2009-04-29 14:03:25 -0700119 private static final int MSG_RUN_BACKUP = 1;
Christopher Tate043dadc2009-06-02 16:11:00 -0700120 private static final int MSG_RUN_FULL_BACKUP = 2;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700121 private static final int MSG_RUN_RESTORE = 3;
Christopher Tateee0e78a2009-07-02 11:17:03 -0700122 private static final int MSG_RUN_CLEAR = 4;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700123 private static final int MSG_RUN_INITIALIZE = 5;
Christopher Tate2d449afe2010-03-29 19:14:24 -0700124 private static final int MSG_RUN_GET_RESTORE_SETS = 6;
125 private static final int MSG_TIMEOUT = 7;
Christopher Tate73a3cb32010-12-13 18:27:26 -0800126 private static final int MSG_RESTORE_TIMEOUT = 8;
Christopher Tate4a627c72011-04-01 14:43:32 -0700127 private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
128 private static final int MSG_RUN_FULL_RESTORE = 10;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700129
130 // Timeout interval for deciding that a bind or clear-data has taken too long
131 static final long TIMEOUT_INTERVAL = 10 * 1000;
132
Christopher Tate44a27902010-01-27 17:15:49 -0800133 // Timeout intervals for agent backup & restore operations
134 static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
Christopher Tate4a627c72011-04-01 14:43:32 -0700135 static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
Christopher Tate44a27902010-01-27 17:15:49 -0800136 static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
137
Christopher Tate4a627c72011-04-01 14:43:32 -0700138 // User confirmation timeout for a full backup/restore operation
139 static final long TIMEOUT_FULL_CONFIRMATION = 30 * 1000;
140
Christopher Tate487529a2009-04-29 14:03:25 -0700141 private Context mContext;
142 private PackageManager mPackageManager;
Christopher Tate1bb69062010-02-19 17:02:12 -0800143 IPackageManager mPackageManagerBinder;
Christopher Tate6ef58a12009-06-29 14:56:28 -0700144 private IActivityManager mActivityManager;
Christopher Tateb6787f22009-07-02 17:40:45 -0700145 private PowerManager mPowerManager;
146 private AlarmManager mAlarmManager;
Christopher Tate44a27902010-01-27 17:15:49 -0800147 IBackupManager mBackupManagerBinder;
Christopher Tateb6787f22009-07-02 17:40:45 -0700148
Christopher Tate73e02522009-07-15 14:18:26 -0700149 boolean mEnabled; // access to this is synchronized on 'this'
150 boolean mProvisioned;
Christopher Tatecce9da52010-02-03 15:11:15 -0800151 boolean mAutoRestore;
Christopher Tate73e02522009-07-15 14:18:26 -0700152 PowerManager.WakeLock mWakelock;
Christopher Tate44a27902010-01-27 17:15:49 -0800153 HandlerThread mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
154 BackupHandler mBackupHandler;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700155 PendingIntent mRunBackupIntent, mRunInitIntent;
156 BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
Christopher Tate487529a2009-04-29 14:03:25 -0700157 // map UIDs to the set of backup client services within that UID's app set
Christopher Tate73e02522009-07-15 14:18:26 -0700158 final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
Christopher Tate181fafa2009-05-14 11:12:14 -0700159 = new SparseArray<HashSet<ApplicationInfo>>();
Christopher Tate487529a2009-04-29 14:03:25 -0700160 // set of backup services that have pending changes
Christopher Tate73e02522009-07-15 14:18:26 -0700161 class BackupRequest {
Christopher Tate181fafa2009-05-14 11:12:14 -0700162 public ApplicationInfo appInfo;
Christopher Tateaa088442009-06-16 18:25:46 -0700163
Christopher Tate4a627c72011-04-01 14:43:32 -0700164 BackupRequest(ApplicationInfo app) {
Christopher Tate181fafa2009-05-14 11:12:14 -0700165 appInfo = app;
Christopher Tate46758122009-05-06 11:22:00 -0700166 }
Christopher Tate181fafa2009-05-14 11:12:14 -0700167
168 public String toString() {
Christopher Tate4a627c72011-04-01 14:43:32 -0700169 return "BackupRequest{app=" + appInfo + "}";
Christopher Tate181fafa2009-05-14 11:12:14 -0700170 }
Christopher Tate46758122009-05-06 11:22:00 -0700171 }
Christopher Tatec28083a2010-12-14 16:16:44 -0800172 // Backups that we haven't started yet. Keys are package names.
173 HashMap<String,BackupRequest> mPendingBackups
174 = new HashMap<String,BackupRequest>();
Christopher Tate5cb400b2009-06-25 16:03:14 -0700175
176 // Pseudoname that we use for the Package Manager metadata "package"
Christopher Tate73e02522009-07-15 14:18:26 -0700177 static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
Christopher Tate6aa41f42009-06-19 14:14:22 -0700178
179 // locking around the pending-backup management
Christopher Tate73e02522009-07-15 14:18:26 -0700180 final Object mQueueLock = new Object();
Christopher Tate487529a2009-04-29 14:03:25 -0700181
Christopher Tate043dadc2009-06-02 16:11:00 -0700182 // The thread performing the sequence of queued backups binds to each app's agent
183 // in succession. Bind notifications are asynchronously delivered through the
184 // Activity Manager; use this lock object to signal when a requested binding has
185 // completed.
Christopher Tate73e02522009-07-15 14:18:26 -0700186 final Object mAgentConnectLock = new Object();
187 IBackupAgent mConnectedAgent;
188 volatile boolean mConnecting;
Christopher Tate55f931a2009-09-29 17:17:34 -0700189 volatile long mLastBackupPass;
190 volatile long mNextBackupPass;
Christopher Tate043dadc2009-06-02 16:11:00 -0700191
Christopher Tate55f931a2009-09-29 17:17:34 -0700192 // A similar synchronization mechanism around clearing apps' data for restore
Christopher Tate73e02522009-07-15 14:18:26 -0700193 final Object mClearDataLock = new Object();
194 volatile boolean mClearingData;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700195
Christopher Tate91717492009-06-26 21:07:13 -0700196 // Transport bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700197 final HashMap<String,IBackupTransport> mTransports
Christopher Tate91717492009-06-26 21:07:13 -0700198 = new HashMap<String,IBackupTransport>();
Christopher Tate73e02522009-07-15 14:18:26 -0700199 String mCurrentTransport;
200 IBackupTransport mLocalTransport, mGoogleTransport;
Christopher Tate80202c82010-01-25 19:37:47 -0800201 ActiveRestoreSession mActiveRestoreSession;
Christopher Tate043dadc2009-06-02 16:11:00 -0700202
Christopher Tate2d449afe2010-03-29 19:14:24 -0700203 class RestoreGetSetsParams {
204 public IBackupTransport transport;
205 public ActiveRestoreSession session;
206 public IRestoreObserver observer;
207
208 RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
209 IRestoreObserver _observer) {
210 transport = _transport;
211 session = _session;
212 observer = _observer;
213 }
214 }
215
Christopher Tate73e02522009-07-15 14:18:26 -0700216 class RestoreParams {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700217 public IBackupTransport transport;
218 public IRestoreObserver observer;
Dan Egnor156411d2009-06-26 13:20:02 -0700219 public long token;
Christopher Tate84725812010-02-04 15:52:40 -0800220 public PackageInfo pkgInfo;
Christopher Tate1bb69062010-02-19 17:02:12 -0800221 public int pmToken; // in post-install restore, the PM's token for this transaction
Chris Tate249345b2010-10-29 12:57:04 -0700222 public boolean needFullBackup;
Christopher Tate84725812010-02-04 15:52:40 -0800223
224 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
Chris Tate249345b2010-10-29 12:57:04 -0700225 long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
Christopher Tate84725812010-02-04 15:52:40 -0800226 transport = _transport;
227 observer = _obs;
228 token = _token;
229 pkgInfo = _pkg;
Christopher Tate1bb69062010-02-19 17:02:12 -0800230 pmToken = _pmToken;
Chris Tate249345b2010-10-29 12:57:04 -0700231 needFullBackup = _needFullBackup;
Christopher Tate84725812010-02-04 15:52:40 -0800232 }
Christopher Tate7d562ec2009-06-25 18:03:43 -0700233
Chris Tate249345b2010-10-29 12:57:04 -0700234 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
235 boolean _needFullBackup) {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700236 transport = _transport;
237 observer = _obs;
Dan Egnor156411d2009-06-26 13:20:02 -0700238 token = _token;
Christopher Tate84725812010-02-04 15:52:40 -0800239 pkgInfo = null;
Christopher Tate1bb69062010-02-19 17:02:12 -0800240 pmToken = 0;
Chris Tate249345b2010-10-29 12:57:04 -0700241 needFullBackup = _needFullBackup;
Christopher Tate7d562ec2009-06-25 18:03:43 -0700242 }
243 }
244
Christopher Tate73e02522009-07-15 14:18:26 -0700245 class ClearParams {
Christopher Tateee0e78a2009-07-02 11:17:03 -0700246 public IBackupTransport transport;
247 public PackageInfo packageInfo;
248
249 ClearParams(IBackupTransport _transport, PackageInfo _info) {
250 transport = _transport;
251 packageInfo = _info;
252 }
253 }
254
Christopher Tate4a627c72011-04-01 14:43:32 -0700255 class FullParams {
256 public ParcelFileDescriptor fd;
257 public final AtomicBoolean latch;
258 public IFullBackupRestoreObserver observer;
259
260 FullParams() {
261 latch = new AtomicBoolean(false);
262 }
263 }
264
265 class FullBackupParams extends FullParams {
266 public boolean includeApks;
267 public boolean includeShared;
268 public boolean allApps;
269 public String[] packages;
270
271 FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
272 boolean doAllApps, String[] pkgList) {
273 fd = output;
274 includeApks = saveApks;
275 includeShared = saveShared;
276 allApps = doAllApps;
277 packages = pkgList;
278 }
279 }
280
281 class FullRestoreParams extends FullParams {
282 FullRestoreParams(ParcelFileDescriptor input) {
283 fd = input;
284 }
285 }
286
Christopher Tate44a27902010-01-27 17:15:49 -0800287 // Bookkeeping of in-flight operations for timeout etc. purposes. The operation
288 // token is the index of the entry in the pending-operations list.
289 static final int OP_PENDING = 0;
290 static final int OP_ACKNOWLEDGED = 1;
291 static final int OP_TIMEOUT = -1;
292
293 final SparseIntArray mCurrentOperations = new SparseIntArray();
294 final Object mCurrentOpLock = new Object();
295 final Random mTokenGenerator = new Random();
296
Christopher Tate4a627c72011-04-01 14:43:32 -0700297 final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
298
Christopher Tate5cb400b2009-06-25 16:03:14 -0700299 // Where we keep our journal files and other bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700300 File mBaseStateDir;
301 File mDataDir;
302 File mJournalDir;
303 File mJournal;
Christopher Tate73e02522009-07-15 14:18:26 -0700304
Christopher Tate84725812010-02-04 15:52:40 -0800305 // Keep a log of all the apps we've ever backed up, and what the
306 // dataset tokens are for both the current backup dataset and
307 // the ancestral dataset.
Christopher Tate73e02522009-07-15 14:18:26 -0700308 private File mEverStored;
Christopher Tate73e02522009-07-15 14:18:26 -0700309 HashSet<String> mEverStoredApps = new HashSet<String>();
310
Christopher Tateb49ceb32010-02-08 16:22:24 -0800311 static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes
Christopher Tate84725812010-02-04 15:52:40 -0800312 File mTokenFile;
Christopher Tateb49ceb32010-02-08 16:22:24 -0800313 Set<String> mAncestralPackages = null;
Christopher Tate84725812010-02-04 15:52:40 -0800314 long mAncestralToken = 0;
315 long mCurrentToken = 0;
316
Christopher Tate4cc86e12009-09-21 19:36:51 -0700317 // Persistently track the need to do a full init
318 static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
319 HashSet<String> mPendingInits = new HashSet<String>(); // transport names
Christopher Tateaa088442009-06-16 18:25:46 -0700320
Christopher Tate4a627c72011-04-01 14:43:32 -0700321 // Utility: build a new random integer token
322 int generateToken() {
323 int token;
324 do {
325 synchronized (mTokenGenerator) {
326 token = mTokenGenerator.nextInt();
327 }
328 } while (token < 0);
329 return token;
330 }
331
Christopher Tate44a27902010-01-27 17:15:49 -0800332 // ----- Asynchronous backup/restore handler thread -----
333
334 private class BackupHandler extends Handler {
335 public BackupHandler(Looper looper) {
336 super(looper);
337 }
338
339 public void handleMessage(Message msg) {
340
341 switch (msg.what) {
342 case MSG_RUN_BACKUP:
343 {
344 mLastBackupPass = System.currentTimeMillis();
345 mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
346
347 IBackupTransport transport = getTransport(mCurrentTransport);
348 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800349 Slog.v(TAG, "Backup requested but no transport available");
Christopher Tate44a27902010-01-27 17:15:49 -0800350 mWakelock.release();
351 break;
352 }
353
354 // snapshot the pending-backup set and work on that
355 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
Christopher Tatec61da312010-02-05 10:41:27 -0800356 File oldJournal = mJournal;
Christopher Tate44a27902010-01-27 17:15:49 -0800357 synchronized (mQueueLock) {
Christopher Tatec61da312010-02-05 10:41:27 -0800358 // Do we have any work to do? Construct the work queue
359 // then release the synchronization lock to actually run
360 // the backup.
Christopher Tate44a27902010-01-27 17:15:49 -0800361 if (mPendingBackups.size() > 0) {
362 for (BackupRequest b: mPendingBackups.values()) {
363 queue.add(b);
364 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800365 if (DEBUG) Slog.v(TAG, "clearing pending backups");
Christopher Tate44a27902010-01-27 17:15:49 -0800366 mPendingBackups.clear();
367
368 // Start a new backup-queue journal file too
Christopher Tate44a27902010-01-27 17:15:49 -0800369 mJournal = null;
370
Christopher Tate44a27902010-01-27 17:15:49 -0800371 }
372 }
Christopher Tatec61da312010-02-05 10:41:27 -0800373
374 if (queue.size() > 0) {
375 // At this point, we have started a new journal file, and the old
376 // file identity is being passed to the backup processing thread.
377 // When it completes successfully, that old journal file will be
378 // deleted. If we crash prior to that, the old journal is parsed
379 // at next boot and the journaled requests fulfilled.
380 (new PerformBackupTask(transport, queue, oldJournal)).run();
381 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800382 Slog.v(TAG, "Backup requested but nothing pending");
Christopher Tatec61da312010-02-05 10:41:27 -0800383 mWakelock.release();
384 }
Christopher Tate44a27902010-01-27 17:15:49 -0800385 break;
386 }
387
388 case MSG_RUN_FULL_BACKUP:
Christopher Tate4a627c72011-04-01 14:43:32 -0700389 {
390 FullBackupParams params = (FullBackupParams)msg.obj;
391 (new PerformFullBackupTask(params.fd, params.observer, params.includeApks,
392 params.includeShared, params.allApps, params.packages,
393 params.latch)).run();
Christopher Tate44a27902010-01-27 17:15:49 -0800394 break;
Christopher Tate4a627c72011-04-01 14:43:32 -0700395 }
Christopher Tate44a27902010-01-27 17:15:49 -0800396
397 case MSG_RUN_RESTORE:
398 {
399 RestoreParams params = (RestoreParams)msg.obj;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800400 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
Christopher Tate44a27902010-01-27 17:15:49 -0800401 (new PerformRestoreTask(params.transport, params.observer,
Chris Tate249345b2010-10-29 12:57:04 -0700402 params.token, params.pkgInfo, params.pmToken,
403 params.needFullBackup)).run();
Christopher Tate44a27902010-01-27 17:15:49 -0800404 break;
405 }
406
Christopher Tate75a99702011-05-18 16:28:19 -0700407 case MSG_RUN_FULL_RESTORE:
408 {
409 FullRestoreParams params = (FullRestoreParams)msg.obj;
410 (new PerformFullRestoreTask(params.fd, params.observer, params.latch)).run();
411 break;
412 }
413
Christopher Tate44a27902010-01-27 17:15:49 -0800414 case MSG_RUN_CLEAR:
415 {
416 ClearParams params = (ClearParams)msg.obj;
417 (new PerformClearTask(params.transport, params.packageInfo)).run();
418 break;
419 }
420
421 case MSG_RUN_INITIALIZE:
422 {
423 HashSet<String> queue;
424
425 // Snapshot the pending-init queue and work on that
426 synchronized (mQueueLock) {
427 queue = new HashSet<String>(mPendingInits);
428 mPendingInits.clear();
429 }
430
431 (new PerformInitializeTask(queue)).run();
432 break;
433 }
434
Christopher Tate2d449afe2010-03-29 19:14:24 -0700435 case MSG_RUN_GET_RESTORE_SETS:
436 {
437 // Like other async operations, this is entered with the wakelock held
438 RestoreSet[] sets = null;
439 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
440 try {
441 sets = params.transport.getAvailableRestoreSets();
442 // cache the result in the active session
443 synchronized (params.session) {
444 params.session.mRestoreSets = sets;
445 }
446 if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
447 } catch (Exception e) {
448 Slog.e(TAG, "Error from transport getting set list");
449 } finally {
450 if (params.observer != null) {
451 try {
452 params.observer.restoreSetsAvailable(sets);
453 } catch (RemoteException re) {
454 Slog.e(TAG, "Unable to report listing to observer");
455 } catch (Exception e) {
456 Slog.e(TAG, "Restore observer threw", e);
457 }
458 }
459
Christopher Tate2a935092011-03-03 17:30:32 -0800460 // Done: reset the session timeout clock
461 removeMessages(MSG_RESTORE_TIMEOUT);
462 sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
463
Christopher Tate2d449afe2010-03-29 19:14:24 -0700464 mWakelock.release();
465 }
466 break;
467 }
468
Christopher Tate44a27902010-01-27 17:15:49 -0800469 case MSG_TIMEOUT:
470 {
471 synchronized (mCurrentOpLock) {
472 final int token = msg.arg1;
473 int state = mCurrentOperations.get(token, OP_TIMEOUT);
474 if (state == OP_PENDING) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800475 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + token);
Christopher Tate44a27902010-01-27 17:15:49 -0800476 mCurrentOperations.put(token, OP_TIMEOUT);
477 }
478 mCurrentOpLock.notifyAll();
479 }
480 break;
481 }
Christopher Tate73a3cb32010-12-13 18:27:26 -0800482
483 case MSG_RESTORE_TIMEOUT:
484 {
485 synchronized (BackupManagerService.this) {
486 if (mActiveRestoreSession != null) {
487 // Client app left the restore session dangling. We know that it
488 // can't be in the middle of an actual restore operation because
489 // those are executed serially on this same handler thread. Clean
490 // up now.
491 Slog.w(TAG, "Restore session timed out; aborting");
492 post(mActiveRestoreSession.new EndRestoreRunnable(
493 BackupManagerService.this, mActiveRestoreSession));
494 }
495 }
496 }
Christopher Tate4a627c72011-04-01 14:43:32 -0700497
498 case MSG_FULL_CONFIRMATION_TIMEOUT:
499 {
500 synchronized (mFullConfirmations) {
501 FullParams params = mFullConfirmations.get(msg.arg1);
502 if (params != null) {
503 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
504
505 // Release the waiter; timeout == completion
506 signalFullBackupRestoreCompletion(params);
507
508 // Remove the token from the set
509 mFullConfirmations.delete(msg.arg1);
510
511 // Report a timeout to the observer, if any
512 if (params.observer != null) {
513 try {
514 params.observer.onTimeout();
515 } catch (RemoteException e) {
516 /* don't care if the app has gone away */
517 }
518 }
519 } else {
520 Slog.d(TAG, "couldn't find params for token " + msg.arg1);
521 }
522 }
523 break;
524 }
Christopher Tate44a27902010-01-27 17:15:49 -0800525 }
526 }
527 }
528
529 // ----- Main service implementation -----
530
Christopher Tate487529a2009-04-29 14:03:25 -0700531 public BackupManagerService(Context context) {
532 mContext = context;
533 mPackageManager = context.getPackageManager();
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -0700534 mPackageManagerBinder = AppGlobals.getPackageManager();
Christopher Tate181fafa2009-05-14 11:12:14 -0700535 mActivityManager = ActivityManagerNative.getDefault();
Christopher Tate487529a2009-04-29 14:03:25 -0700536
Christopher Tateb6787f22009-07-02 17:40:45 -0700537 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
538 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
539
Christopher Tate44a27902010-01-27 17:15:49 -0800540 mBackupManagerBinder = asInterface(asBinder());
541
542 // spin up the backup/restore handler thread
543 mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
544 mHandlerThread.start();
545 mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
546
Christopher Tate22b87872009-05-04 16:41:53 -0700547 // Set up our bookkeeping
Christopher Tateb6787f22009-07-02 17:40:45 -0700548 boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
Dianne Hackborncf098292009-07-01 19:55:20 -0700549 Settings.Secure.BACKUP_ENABLED, 0) != 0;
Christopher Tate8031a3d2009-07-06 16:36:05 -0700550 mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
Joe Onoratoab9a2a52009-07-27 08:56:39 -0700551 Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
Christopher Tatecce9da52010-02-03 15:11:15 -0800552 mAutoRestore = Settings.Secure.getInt(context.getContentResolver(),
Christopher Tate5035fda2010-02-25 18:01:14 -0800553 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
Oscar Montemayora8529f62009-11-18 10:14:20 -0800554 // If Encrypted file systems is enabled or disabled, this call will return the
555 // correct directory.
Jason parksa3cdaa52011-01-13 14:15:43 -0600556 mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
Oscar Montemayora8529f62009-11-18 10:14:20 -0800557 mBaseStateDir.mkdirs();
Christopher Tatef4172472009-05-05 15:50:03 -0700558 mDataDir = Environment.getDownloadCacheDirectory();
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700559
Christopher Tate4cc86e12009-09-21 19:36:51 -0700560 // Alarm receivers for scheduled backups & initialization operations
Christopher Tateb6787f22009-07-02 17:40:45 -0700561 mRunBackupReceiver = new RunBackupReceiver();
Christopher Tate4cc86e12009-09-21 19:36:51 -0700562 IntentFilter filter = new IntentFilter();
563 filter.addAction(RUN_BACKUP_ACTION);
564 context.registerReceiver(mRunBackupReceiver, filter,
565 android.Manifest.permission.BACKUP, null);
566
567 mRunInitReceiver = new RunInitializeReceiver();
568 filter = new IntentFilter();
569 filter.addAction(RUN_INITIALIZE_ACTION);
570 context.registerReceiver(mRunInitReceiver, filter,
571 android.Manifest.permission.BACKUP, null);
Christopher Tateb6787f22009-07-02 17:40:45 -0700572
573 Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
Christopher Tateb6787f22009-07-02 17:40:45 -0700574 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
575 mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
576
Christopher Tate4cc86e12009-09-21 19:36:51 -0700577 Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
578 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
579 mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
580
Christopher Tatecde87f42009-06-12 12:55:53 -0700581 // Set up the backup-request journaling
Christopher Tate5cb400b2009-06-25 16:03:14 -0700582 mJournalDir = new File(mBaseStateDir, "pending");
583 mJournalDir.mkdirs(); // creates mBaseStateDir along the way
Dan Egnor852f8e42009-09-30 11:20:45 -0700584 mJournal = null; // will be created on first use
Christopher Tatecde87f42009-06-12 12:55:53 -0700585
Christopher Tate73e02522009-07-15 14:18:26 -0700586 // Set up the various sorts of package tracking we do
587 initPackageTracking();
588
Christopher Tateabce4e82009-06-18 18:35:32 -0700589 // Build our mapping of uid to backup client services. This implicitly
590 // schedules a backup pass on the Package Manager metadata the first
591 // time anything needs to be backed up.
Christopher Tate3799bc22009-05-06 16:13:56 -0700592 synchronized (mBackupParticipants) {
593 addPackageParticipantsLocked(null);
Christopher Tate487529a2009-04-29 14:03:25 -0700594 }
595
Dan Egnor87a02bc2009-06-17 02:30:10 -0700596 // Set up our transport options and initialize the default transport
597 // TODO: Have transports register themselves somehow?
598 // TODO: Don't create transports that we don't need to?
Dan Egnor87a02bc2009-06-17 02:30:10 -0700599 mLocalTransport = new LocalTransport(context); // This is actually pretty cheap
Christopher Tate91717492009-06-26 21:07:13 -0700600 ComponentName localName = new ComponentName(context, LocalTransport.class);
601 registerTransport(localName.flattenToShortString(), mLocalTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700602
Christopher Tate91717492009-06-26 21:07:13 -0700603 mGoogleTransport = null;
Dianne Hackborncf098292009-07-01 19:55:20 -0700604 mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
605 Settings.Secure.BACKUP_TRANSPORT);
606 if ("".equals(mCurrentTransport)) {
607 mCurrentTransport = null;
Christopher Tatece0bf062009-07-01 11:43:53 -0700608 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800609 if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -0700610
611 // Attach to the Google backup transport. When this comes up, it will set
612 // itself as the current transport because we explicitly reset mCurrentTransport
613 // to null.
Christopher Tatea32504f2010-04-21 17:58:07 -0700614 ComponentName transportComponent = new ComponentName("com.google.android.backup",
615 "com.google.android.backup.BackupTransportService");
616 try {
617 // If there's something out there that is supposed to be the Google
618 // backup transport, make sure it's legitimately part of the OS build
619 // and not an app lying about its package name.
620 ApplicationInfo info = mPackageManager.getApplicationInfo(
621 transportComponent.getPackageName(), 0);
622 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
623 if (DEBUG) Slog.v(TAG, "Binding to Google transport");
624 Intent intent = new Intent().setComponent(transportComponent);
625 context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
626 } else {
627 Slog.w(TAG, "Possible Google transport spoof: ignoring " + info);
628 }
629 } catch (PackageManager.NameNotFoundException nnf) {
630 // No such package? No binding.
631 if (DEBUG) Slog.v(TAG, "Google transport not present");
632 }
Christopher Tateaa088442009-06-16 18:25:46 -0700633
Christopher Tatecde87f42009-06-12 12:55:53 -0700634 // Now that we know about valid backup participants, parse any
Christopher Tate49401dd2009-07-01 12:34:29 -0700635 // leftover journal files into the pending backup set
Christopher Tatecde87f42009-06-12 12:55:53 -0700636 parseLeftoverJournals();
637
Christopher Tateb6787f22009-07-02 17:40:45 -0700638 // Power management
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700639 mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
Christopher Tateb6787f22009-07-02 17:40:45 -0700640
641 // Start the backup passes going
642 setBackupEnabled(areEnabled);
643 }
644
645 private class RunBackupReceiver extends BroadcastReceiver {
646 public void onReceive(Context context, Intent intent) {
647 if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
Christopher Tateb6787f22009-07-02 17:40:45 -0700648 synchronized (mQueueLock) {
Christopher Tate4cc86e12009-09-21 19:36:51 -0700649 if (mPendingInits.size() > 0) {
650 // If there are pending init operations, we process those
651 // and then settle into the usual periodic backup schedule.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800652 if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700653 try {
654 mAlarmManager.cancel(mRunInitIntent);
655 mRunInitIntent.send();
656 } catch (PendingIntent.CanceledException ce) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800657 Slog.e(TAG, "Run init intent cancelled");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700658 // can't really do more than bail here
659 }
660 } else {
Christopher Tatec2af5d32010-02-02 15:18:58 -0800661 // Don't run backups now if we're disabled or not yet
662 // fully set up.
663 if (mEnabled && mProvisioned) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800664 if (DEBUG) Slog.v(TAG, "Running a backup pass");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700665
666 // Acquire the wakelock and pass it to the backup thread. it will
667 // be released once backup concludes.
668 mWakelock.acquire();
669
670 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
671 mBackupHandler.sendMessage(msg);
672 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800673 Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
Christopher Tate4cc86e12009-09-21 19:36:51 -0700674 }
675 }
676 }
677 }
678 }
679 }
680
681 private class RunInitializeReceiver extends BroadcastReceiver {
682 public void onReceive(Context context, Intent intent) {
683 if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
684 synchronized (mQueueLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800685 if (DEBUG) Slog.v(TAG, "Running a device init");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700686
687 // Acquire the wakelock and pass it to the init thread. it will
688 // be released once init concludes.
Christopher Tateb6787f22009-07-02 17:40:45 -0700689 mWakelock.acquire();
690
Christopher Tate4cc86e12009-09-21 19:36:51 -0700691 Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
Christopher Tateb6787f22009-07-02 17:40:45 -0700692 mBackupHandler.sendMessage(msg);
693 }
694 }
Christopher Tate49401dd2009-07-01 12:34:29 -0700695 }
Christopher Tateb6787f22009-07-02 17:40:45 -0700696 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700697
Christopher Tate73e02522009-07-15 14:18:26 -0700698 private void initPackageTracking() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800699 if (DEBUG) Slog.v(TAG, "Initializing package tracking");
Christopher Tate73e02522009-07-15 14:18:26 -0700700
Christopher Tate84725812010-02-04 15:52:40 -0800701 // Remember our ancestral dataset
702 mTokenFile = new File(mBaseStateDir, "ancestral");
703 try {
704 RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
Christopher Tateb49ceb32010-02-08 16:22:24 -0800705 int version = tf.readInt();
706 if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
707 mAncestralToken = tf.readLong();
708 mCurrentToken = tf.readLong();
709
710 int numPackages = tf.readInt();
711 if (numPackages >= 0) {
712 mAncestralPackages = new HashSet<String>();
713 for (int i = 0; i < numPackages; i++) {
714 String pkgName = tf.readUTF();
715 mAncestralPackages.add(pkgName);
716 }
717 }
718 }
Brad Fitzpatrick725d8f02010-11-15 11:12:42 -0800719 tf.close();
Christopher Tate1168baa2010-02-17 13:03:40 -0800720 } catch (FileNotFoundException fnf) {
721 // Probably innocuous
Joe Onorato8a9b2202010-02-26 18:56:32 -0800722 Slog.v(TAG, "No ancestral data");
Christopher Tate84725812010-02-04 15:52:40 -0800723 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800724 Slog.w(TAG, "Unable to read token file", e);
Christopher Tate84725812010-02-04 15:52:40 -0800725 }
726
Christopher Tatee97e8072009-07-15 16:45:50 -0700727 // Keep a log of what apps we've ever backed up. Because we might have
728 // rebooted in the middle of an operation that was removing something from
729 // this log, we sanity-check its contents here and reconstruct it.
Christopher Tate73e02522009-07-15 14:18:26 -0700730 mEverStored = new File(mBaseStateDir, "processed");
Christopher Tatee97e8072009-07-15 16:45:50 -0700731 File tempProcessedFile = new File(mBaseStateDir, "processed.new");
Christopher Tate73e02522009-07-15 14:18:26 -0700732
Christopher Tatee97e8072009-07-15 16:45:50 -0700733 // If we were in the middle of removing something from the ever-backed-up
734 // file, there might be a transient "processed.new" file still present.
Dan Egnor852f8e42009-09-30 11:20:45 -0700735 // Ignore it -- we'll validate "processed" against the current package set.
Christopher Tatee97e8072009-07-15 16:45:50 -0700736 if (tempProcessedFile.exists()) {
737 tempProcessedFile.delete();
738 }
739
Dan Egnor852f8e42009-09-30 11:20:45 -0700740 // If there are previous contents, parse them out then start a new
741 // file to continue the recordkeeping.
742 if (mEverStored.exists()) {
743 RandomAccessFile temp = null;
744 RandomAccessFile in = null;
745
746 try {
747 temp = new RandomAccessFile(tempProcessedFile, "rws");
748 in = new RandomAccessFile(mEverStored, "r");
749
750 while (true) {
751 PackageInfo info;
752 String pkg = in.readUTF();
753 try {
754 info = mPackageManager.getPackageInfo(pkg, 0);
755 mEverStoredApps.add(pkg);
756 temp.writeUTF(pkg);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800757 if (DEBUG) Slog.v(TAG, " + " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700758 } catch (NameNotFoundException e) {
759 // nope, this package was uninstalled; don't include it
Joe Onorato8a9b2202010-02-26 18:56:32 -0800760 if (DEBUG) Slog.v(TAG, " - " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700761 }
762 }
763 } catch (EOFException e) {
764 // Once we've rewritten the backup history log, atomically replace the
765 // old one with the new one then reopen the file for continuing use.
766 if (!tempProcessedFile.renameTo(mEverStored)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800767 Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -0700768 }
769 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800770 Slog.e(TAG, "Error in processed file", e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700771 } finally {
772 try { if (temp != null) temp.close(); } catch (IOException e) {}
773 try { if (in != null) in.close(); } catch (IOException e) {}
774 }
775 }
776
Christopher Tate73e02522009-07-15 14:18:26 -0700777 // Register for broadcasts about package install, etc., so we can
778 // update the provider list.
779 IntentFilter filter = new IntentFilter();
780 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
781 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
782 filter.addDataScheme("package");
783 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800784 // Register for events related to sdcard installation.
785 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800786 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
787 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800788 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
Christopher Tate73e02522009-07-15 14:18:26 -0700789 }
790
Christopher Tatecde87f42009-06-12 12:55:53 -0700791 private void parseLeftoverJournals() {
Dan Egnor852f8e42009-09-30 11:20:45 -0700792 for (File f : mJournalDir.listFiles()) {
793 if (mJournal == null || f.compareTo(mJournal) != 0) {
794 // This isn't the current journal, so it must be a leftover. Read
795 // out the package names mentioned there and schedule them for
796 // backup.
797 RandomAccessFile in = null;
798 try {
Joe Onorato431bb222010-10-18 19:13:23 -0400799 Slog.i(TAG, "Found stale backup journal, scheduling");
Dan Egnor852f8e42009-09-30 11:20:45 -0700800 in = new RandomAccessFile(f, "r");
801 while (true) {
802 String packageName = in.readUTF();
Joe Onorato431bb222010-10-18 19:13:23 -0400803 Slog.i(TAG, " " + packageName);
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -0700804 dataChangedImpl(packageName);
Christopher Tatecde87f42009-06-12 12:55:53 -0700805 }
Dan Egnor852f8e42009-09-30 11:20:45 -0700806 } catch (EOFException e) {
807 // no more data; we're done
808 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800809 Slog.e(TAG, "Can't read " + f, e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700810 } finally {
811 // close/delete the file
812 try { if (in != null) in.close(); } catch (IOException e) {}
813 f.delete();
Christopher Tatecde87f42009-06-12 12:55:53 -0700814 }
815 }
816 }
817 }
818
Christopher Tate4cc86e12009-09-21 19:36:51 -0700819 // Maintain persistent state around whether need to do an initialize operation.
820 // Must be called with the queue lock held.
821 void recordInitPendingLocked(boolean isPending, String transportName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800822 if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
Christopher Tate4cc86e12009-09-21 19:36:51 -0700823 + " on transport " + transportName);
824 try {
825 IBackupTransport transport = getTransport(transportName);
826 String transportDirName = transport.transportDirName();
827 File stateDir = new File(mBaseStateDir, transportDirName);
828 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
829
830 if (isPending) {
831 // We need an init before we can proceed with sending backup data.
832 // Record that with an entry in our set of pending inits, as well as
833 // journaling it via creation of a sentinel file.
834 mPendingInits.add(transportName);
835 try {
836 (new FileOutputStream(initPendingFile)).close();
837 } catch (IOException ioe) {
838 // Something is badly wrong with our permissions; just try to move on
839 }
840 } else {
841 // No more initialization needed; wipe the journal and reset our state.
842 initPendingFile.delete();
843 mPendingInits.remove(transportName);
844 }
845 } catch (RemoteException e) {
846 // can't happen; the transport is local
847 }
848 }
849
Christopher Tated55e18a2009-09-21 10:12:59 -0700850 // Reset all of our bookkeeping, in response to having been told that
851 // the backend data has been wiped [due to idle expiry, for example],
852 // so we must re-upload all saved settings.
853 void resetBackupState(File stateFileDir) {
854 synchronized (mQueueLock) {
855 // Wipe the "what we've ever backed up" tracking
Christopher Tated55e18a2009-09-21 10:12:59 -0700856 mEverStoredApps.clear();
Dan Egnor852f8e42009-09-30 11:20:45 -0700857 mEverStored.delete();
Christopher Tated55e18a2009-09-21 10:12:59 -0700858
Christopher Tate84725812010-02-04 15:52:40 -0800859 mCurrentToken = 0;
860 writeRestoreTokens();
861
Christopher Tated55e18a2009-09-21 10:12:59 -0700862 // Remove all the state files
863 for (File sf : stateFileDir.listFiles()) {
Christopher Tate4cc86e12009-09-21 19:36:51 -0700864 // ... but don't touch the needs-init sentinel
865 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
866 sf.delete();
867 }
Christopher Tated55e18a2009-09-21 10:12:59 -0700868 }
Christopher Tate45597642011-04-04 16:59:21 -0700869 }
Christopher Tated55e18a2009-09-21 10:12:59 -0700870
Christopher Tate45597642011-04-04 16:59:21 -0700871 // Enqueue a new backup of every participant
872 int N = mBackupParticipants.size();
873 for (int i=0; i<N; i++) {
874 int uid = mBackupParticipants.keyAt(i);
875 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
876 for (ApplicationInfo app: participants) {
877 dataChangedImpl(app.packageName);
Christopher Tated55e18a2009-09-21 10:12:59 -0700878 }
879 }
880 }
881
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800882 // Add a transport to our set of available backends. If 'transport' is null, this
883 // is an unregistration, and the transport's entry is removed from our bookkeeping.
Christopher Tate91717492009-06-26 21:07:13 -0700884 private void registerTransport(String name, IBackupTransport transport) {
885 synchronized (mTransports) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800886 if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport);
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800887 if (transport != null) {
888 mTransports.put(name, transport);
889 } else {
890 mTransports.remove(name);
Christopher Tateb0dcaaf2010-01-29 16:27:04 -0800891 if ((mCurrentTransport != null) && mCurrentTransport.equals(name)) {
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800892 mCurrentTransport = null;
893 }
894 // Nothing further to do in the unregistration case
895 return;
896 }
Christopher Tate91717492009-06-26 21:07:13 -0700897 }
Christopher Tate4cc86e12009-09-21 19:36:51 -0700898
899 // If the init sentinel file exists, we need to be sure to perform the init
900 // as soon as practical. We also create the state directory at registration
901 // time to ensure it's present from the outset.
902 try {
903 String transportName = transport.transportDirName();
904 File stateDir = new File(mBaseStateDir, transportName);
905 stateDir.mkdirs();
906
907 File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
908 if (initSentinel.exists()) {
909 synchronized (mQueueLock) {
910 mPendingInits.add(transportName);
911
912 // TODO: pick a better starting time than now + 1 minute
913 long delay = 1000 * 60; // one minute, in milliseconds
914 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
915 System.currentTimeMillis() + delay, mRunInitIntent);
916 }
917 }
918 } catch (RemoteException e) {
919 // can't happen, the transport is local
920 }
Christopher Tate91717492009-06-26 21:07:13 -0700921 }
922
Christopher Tate3799bc22009-05-06 16:13:56 -0700923 // ----- Track installation/removal of packages -----
924 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
925 public void onReceive(Context context, Intent intent) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800926 if (DEBUG) Slog.d(TAG, "Received broadcast " + intent);
Christopher Tate3799bc22009-05-06 16:13:56 -0700927
Christopher Tate3799bc22009-05-06 16:13:56 -0700928 String action = intent.getAction();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800929 boolean replacing = false;
930 boolean added = false;
931 Bundle extras = intent.getExtras();
932 String pkgList[] = null;
933 if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
934 Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
935 Uri uri = intent.getData();
936 if (uri == null) {
937 return;
938 }
939 String pkgName = uri.getSchemeSpecificPart();
940 if (pkgName != null) {
941 pkgList = new String[] { pkgName };
942 }
943 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
944 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800945 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800946 added = true;
947 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800948 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800949 added = false;
950 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
951 }
952 if (pkgList == null || pkgList.length == 0) {
953 return;
954 }
955 if (added) {
Christopher Tate3799bc22009-05-06 16:13:56 -0700956 synchronized (mBackupParticipants) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800957 for (String pkgName : pkgList) {
958 if (replacing) {
959 // The package was just upgraded
960 updatePackageParticipantsLocked(pkgName);
961 } else {
962 // The package was just added
963 addPackageParticipantsLocked(pkgName);
964 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700965 }
966 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800967 } else {
968 if (replacing) {
Christopher Tate3799bc22009-05-06 16:13:56 -0700969 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
970 } else {
971 synchronized (mBackupParticipants) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800972 for (String pkgName : pkgList) {
973 removePackageParticipantsLocked(pkgName);
974 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700975 }
976 }
977 }
978 }
979 };
980
Dan Egnor87a02bc2009-06-17 02:30:10 -0700981 // ----- Track connection to GoogleBackupTransport service -----
982 ServiceConnection mGoogleConnection = new ServiceConnection() {
983 public void onServiceConnected(ComponentName name, IBinder service) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800984 if (DEBUG) Slog.v(TAG, "Connected to Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -0700985 mGoogleTransport = IBackupTransport.Stub.asInterface(service);
Christopher Tate91717492009-06-26 21:07:13 -0700986 registerTransport(name.flattenToShortString(), mGoogleTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700987 }
988
989 public void onServiceDisconnected(ComponentName name) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800990 if (DEBUG) Slog.v(TAG, "Disconnected from Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -0700991 mGoogleTransport = null;
Christopher Tate91717492009-06-26 21:07:13 -0700992 registerTransport(name.flattenToShortString(), null);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700993 }
994 };
995
Christopher Tate181fafa2009-05-14 11:12:14 -0700996 // Add the backup agents in the given package to our set of known backup participants.
997 // If 'packageName' is null, adds all backup agents in the whole system.
Christopher Tate3799bc22009-05-06 16:13:56 -0700998 void addPackageParticipantsLocked(String packageName) {
Christopher Tate181fafa2009-05-14 11:12:14 -0700999 // Look for apps that define the android:backupAgent attribute
Joe Onorato8a9b2202010-02-26 18:56:32 -08001000 if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: " + packageName);
Dan Egnorefe52642009-06-24 00:16:33 -07001001 List<PackageInfo> targetApps = allAgentPackages();
Christopher Tate181fafa2009-05-14 11:12:14 -07001002 addPackageParticipantsLockedInner(packageName, targetApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001003 }
1004
Christopher Tate181fafa2009-05-14 11:12:14 -07001005 private void addPackageParticipantsLockedInner(String packageName,
Dan Egnorefe52642009-06-24 00:16:33 -07001006 List<PackageInfo> targetPkgs) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001007 if (DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001008 Slog.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
Dan Egnorefe52642009-06-24 00:16:33 -07001009 for (PackageInfo p : targetPkgs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001010 Slog.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName
Christopher Tate5e1ab332009-09-01 20:32:49 -07001011 + " uid=" + p.applicationInfo.uid
1012 + " killAfterRestore="
1013 + (((p.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) ? "true" : "false")
Christopher Tate5e1ab332009-09-01 20:32:49 -07001014 );
Christopher Tate181fafa2009-05-14 11:12:14 -07001015 }
1016 }
1017
Dan Egnorefe52642009-06-24 00:16:33 -07001018 for (PackageInfo pkg : targetPkgs) {
1019 if (packageName == null || pkg.packageName.equals(packageName)) {
1020 int uid = pkg.applicationInfo.uid;
Christopher Tate181fafa2009-05-14 11:12:14 -07001021 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001022 if (set == null) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001023 set = new HashSet<ApplicationInfo>();
Christopher Tate3799bc22009-05-06 16:13:56 -07001024 mBackupParticipants.put(uid, set);
1025 }
Dan Egnorefe52642009-06-24 00:16:33 -07001026 set.add(pkg.applicationInfo);
Christopher Tate73e02522009-07-15 14:18:26 -07001027
1028 // If we've never seen this app before, schedule a backup for it
1029 if (!mEverStoredApps.contains(pkg.packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001030 if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName
Christopher Tate73e02522009-07-15 14:18:26 -07001031 + " never backed up; scheduling");
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07001032 dataChangedImpl(pkg.packageName);
Christopher Tate73e02522009-07-15 14:18:26 -07001033 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001034 }
Christopher Tate487529a2009-04-29 14:03:25 -07001035 }
1036 }
1037
Christopher Tate6785dd82009-06-18 15:58:25 -07001038 // Remove the given package's entry from our known active set. If
1039 // 'packageName' is null, *all* participating apps will be removed.
Christopher Tate3799bc22009-05-06 16:13:56 -07001040 void removePackageParticipantsLocked(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001041 if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: " + packageName);
Christopher Tatec28083a2010-12-14 16:16:44 -08001042 List<String> allApps = new ArrayList<String>();
Christopher Tate181fafa2009-05-14 11:12:14 -07001043 if (packageName != null) {
Christopher Tatec28083a2010-12-14 16:16:44 -08001044 allApps.add(packageName);
Christopher Tate181fafa2009-05-14 11:12:14 -07001045 } else {
1046 // all apps with agents
Christopher Tatec28083a2010-12-14 16:16:44 -08001047 List<PackageInfo> knownPackages = allAgentPackages();
1048 for (PackageInfo pkg : knownPackages) {
1049 allApps.add(pkg.packageName);
1050 }
Christopher Tate181fafa2009-05-14 11:12:14 -07001051 }
1052 removePackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001053 }
1054
Joe Onorato8ad02812009-05-13 01:41:44 -04001055 private void removePackageParticipantsLockedInner(String packageName,
Christopher Tatec28083a2010-12-14 16:16:44 -08001056 List<String> allPackageNames) {
Christopher Tate043dadc2009-06-02 16:11:00 -07001057 if (DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001058 Slog.v(TAG, "removePackageParticipantsLockedInner (" + packageName
Christopher Tatec28083a2010-12-14 16:16:44 -08001059 + ") removing " + allPackageNames.size() + " entries");
1060 for (String p : allPackageNames) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001061 Slog.v(TAG, " - " + p);
Christopher Tate043dadc2009-06-02 16:11:00 -07001062 }
1063 }
Christopher Tatec28083a2010-12-14 16:16:44 -08001064 for (String pkg : allPackageNames) {
1065 if (packageName == null || pkg.equals(packageName)) {
1066 int uid = -1;
1067 try {
1068 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1069 uid = info.applicationInfo.uid;
1070 } catch (NameNotFoundException e) {
1071 // we don't know this package name, so just skip it for now
1072 continue;
1073 }
1074
Christopher Tate181fafa2009-05-14 11:12:14 -07001075 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001076 if (set != null) {
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001077 // Find the existing entry with the same package name, and remove it.
1078 // We can't just remove(app) because the instances are different.
1079 for (ApplicationInfo entry: set) {
Christopher Tatec28083a2010-12-14 16:16:44 -08001080 if (entry.packageName.equals(pkg)) {
1081 if (DEBUG) Slog.v(TAG, " removing participant " + pkg);
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001082 set.remove(entry);
Christopher Tatec28083a2010-12-14 16:16:44 -08001083 removeEverBackedUp(pkg);
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001084 break;
1085 }
1086 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001087 if (set.size() == 0) {
Dan Egnorefe52642009-06-24 00:16:33 -07001088 mBackupParticipants.delete(uid);
1089 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001090 }
1091 }
1092 }
1093 }
1094
Christopher Tate181fafa2009-05-14 11:12:14 -07001095 // Returns the set of all applications that define an android:backupAgent attribute
Christopher Tate73e02522009-07-15 14:18:26 -07001096 List<PackageInfo> allAgentPackages() {
Christopher Tate6785dd82009-06-18 15:58:25 -07001097 // !!! TODO: cache this and regenerate only when necessary
Dan Egnorefe52642009-06-24 00:16:33 -07001098 int flags = PackageManager.GET_SIGNATURES;
1099 List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
1100 int N = packages.size();
1101 for (int a = N-1; a >= 0; a--) {
Christopher Tate0749dcd2009-08-13 15:13:03 -07001102 PackageInfo pkg = packages.get(a);
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001103 try {
1104 ApplicationInfo app = pkg.applicationInfo;
1105 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
Christopher Tatea87240c2010-02-12 14:12:34 -08001106 || app.backupAgentName == null) {
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001107 packages.remove(a);
1108 }
1109 else {
1110 // we will need the shared library path, so look that up and store it here
1111 app = mPackageManager.getApplicationInfo(pkg.packageName,
1112 PackageManager.GET_SHARED_LIBRARY_FILES);
1113 pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
1114 }
1115 } catch (NameNotFoundException e) {
Dan Egnorefe52642009-06-24 00:16:33 -07001116 packages.remove(a);
Christopher Tate181fafa2009-05-14 11:12:14 -07001117 }
1118 }
Dan Egnorefe52642009-06-24 00:16:33 -07001119 return packages;
Christopher Tate181fafa2009-05-14 11:12:14 -07001120 }
Christopher Tateaa088442009-06-16 18:25:46 -07001121
Christopher Tate3799bc22009-05-06 16:13:56 -07001122 // Reset the given package's known backup participants. Unlike add/remove, the update
1123 // action cannot be passed a null package name.
1124 void updatePackageParticipantsLocked(String packageName) {
1125 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001126 Slog.e(TAG, "updatePackageParticipants called with null package name");
Christopher Tate3799bc22009-05-06 16:13:56 -07001127 return;
1128 }
Joe Onorato8a9b2202010-02-26 18:56:32 -08001129 if (DEBUG) Slog.v(TAG, "updatePackageParticipantsLocked: " + packageName);
Christopher Tate3799bc22009-05-06 16:13:56 -07001130
1131 // brute force but small code size
Dan Egnorefe52642009-06-24 00:16:33 -07001132 List<PackageInfo> allApps = allAgentPackages();
Christopher Tatec28083a2010-12-14 16:16:44 -08001133 List<String> allAppNames = new ArrayList<String>();
1134 for (PackageInfo pkg : allApps) {
1135 allAppNames.add(pkg.packageName);
1136 }
1137 removePackageParticipantsLockedInner(packageName, allAppNames);
Christopher Tate181fafa2009-05-14 11:12:14 -07001138 addPackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001139 }
1140
Christopher Tate84725812010-02-04 15:52:40 -08001141 // Called from the backup task: record that the given app has been successfully
Christopher Tate73e02522009-07-15 14:18:26 -07001142 // backed up at least once
1143 void logBackupComplete(String packageName) {
Dan Egnor852f8e42009-09-30 11:20:45 -07001144 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
1145
1146 synchronized (mEverStoredApps) {
1147 if (!mEverStoredApps.add(packageName)) return;
1148
1149 RandomAccessFile out = null;
1150 try {
1151 out = new RandomAccessFile(mEverStored, "rws");
1152 out.seek(out.length());
1153 out.writeUTF(packageName);
1154 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001155 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -07001156 } finally {
1157 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tate73e02522009-07-15 14:18:26 -07001158 }
1159 }
1160 }
1161
Christopher Tatee97e8072009-07-15 16:45:50 -07001162 // Remove our awareness of having ever backed up the given package
1163 void removeEverBackedUp(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001164 if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName + ", new set:");
Christopher Tatee97e8072009-07-15 16:45:50 -07001165
Dan Egnor852f8e42009-09-30 11:20:45 -07001166 synchronized (mEverStoredApps) {
1167 // Rewrite the file and rename to overwrite. If we reboot in the middle,
1168 // we'll recognize on initialization time that the package no longer
1169 // exists and fix it up then.
1170 File tempKnownFile = new File(mBaseStateDir, "processed.new");
1171 RandomAccessFile known = null;
1172 try {
1173 known = new RandomAccessFile(tempKnownFile, "rws");
1174 mEverStoredApps.remove(packageName);
1175 for (String s : mEverStoredApps) {
1176 known.writeUTF(s);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001177 if (DEBUG) Slog.v(TAG, " " + s);
Christopher Tatee97e8072009-07-15 16:45:50 -07001178 }
Dan Egnor852f8e42009-09-30 11:20:45 -07001179 known.close();
1180 known = null;
1181 if (!tempKnownFile.renameTo(mEverStored)) {
1182 throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
1183 }
1184 } catch (IOException e) {
1185 // Bad: we couldn't create the new copy. For safety's sake we
1186 // abandon the whole process and remove all what's-backed-up
1187 // state entirely, meaning we'll force a backup pass for every
1188 // participant on the next boot or [re]install.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001189 Slog.w(TAG, "Error rewriting " + mEverStored, e);
Dan Egnor852f8e42009-09-30 11:20:45 -07001190 mEverStoredApps.clear();
1191 tempKnownFile.delete();
1192 mEverStored.delete();
1193 } finally {
1194 try { if (known != null) known.close(); } catch (IOException e) {}
Christopher Tatee97e8072009-07-15 16:45:50 -07001195 }
1196 }
1197 }
1198
Christopher Tateb49ceb32010-02-08 16:22:24 -08001199 // Persistently record the current and ancestral backup tokens as well
1200 // as the set of packages with data [supposedly] available in the
1201 // ancestral dataset.
Christopher Tate84725812010-02-04 15:52:40 -08001202 void writeRestoreTokens() {
1203 try {
1204 RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
Christopher Tateb49ceb32010-02-08 16:22:24 -08001205
1206 // First, the version number of this record, for futureproofing
1207 af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
1208
1209 // Write the ancestral and current tokens
Christopher Tate84725812010-02-04 15:52:40 -08001210 af.writeLong(mAncestralToken);
1211 af.writeLong(mCurrentToken);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001212
1213 // Now write the set of ancestral packages
1214 if (mAncestralPackages == null) {
1215 af.writeInt(-1);
1216 } else {
1217 af.writeInt(mAncestralPackages.size());
Joe Onorato8a9b2202010-02-26 18:56:32 -08001218 if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
Christopher Tateb49ceb32010-02-08 16:22:24 -08001219 for (String pkgName : mAncestralPackages) {
1220 af.writeUTF(pkgName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001221 if (DEBUG) Slog.v(TAG, " " + pkgName);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001222 }
1223 }
Christopher Tate84725812010-02-04 15:52:40 -08001224 af.close();
1225 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001226 Slog.w(TAG, "Unable to write token file:", e);
Christopher Tate84725812010-02-04 15:52:40 -08001227 }
1228 }
1229
Dan Egnor87a02bc2009-06-17 02:30:10 -07001230 // Return the given transport
Christopher Tate91717492009-06-26 21:07:13 -07001231 private IBackupTransport getTransport(String transportName) {
1232 synchronized (mTransports) {
1233 IBackupTransport transport = mTransports.get(transportName);
1234 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001235 Slog.w(TAG, "Requested unavailable transport: " + transportName);
Christopher Tate91717492009-06-26 21:07:13 -07001236 }
1237 return transport;
Christopher Tate8c850b72009-06-07 19:33:20 -07001238 }
Christopher Tate8c850b72009-06-07 19:33:20 -07001239 }
1240
Christopher Tatedf01dea2009-06-09 20:45:02 -07001241 // fire off a backup agent, blocking until it attaches or times out
1242 IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
1243 IBackupAgent agent = null;
1244 synchronized(mAgentConnectLock) {
1245 mConnecting = true;
1246 mConnectedAgent = null;
1247 try {
1248 if (mActivityManager.bindBackupAgent(app, mode)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001249 Slog.d(TAG, "awaiting agent for " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001250
1251 // success; wait for the agent to arrive
Christopher Tate75a99702011-05-18 16:28:19 -07001252 // only wait 10 seconds for the bind to happen
Christopher Tatec7b31e32009-06-10 15:49:30 -07001253 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1254 while (mConnecting && mConnectedAgent == null
1255 && (System.currentTimeMillis() < timeoutMark)) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07001256 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001257 mAgentConnectLock.wait(5000);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001258 } catch (InterruptedException e) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001259 // just bail
Christopher Tatedf01dea2009-06-09 20:45:02 -07001260 return null;
1261 }
1262 }
1263
1264 // if we timed out with no connect, abort and move on
1265 if (mConnecting == true) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001266 Slog.w(TAG, "Timeout waiting for agent " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001267 return null;
1268 }
1269 agent = mConnectedAgent;
1270 }
1271 } catch (RemoteException e) {
1272 // can't happen
1273 }
1274 }
1275 return agent;
1276 }
1277
Christopher Tatec7b31e32009-06-10 15:49:30 -07001278 // clear an application's data, blocking until the operation completes or times out
1279 void clearApplicationDataSynchronous(String packageName) {
Christopher Tatef7c886b2009-06-26 15:34:09 -07001280 // Don't wipe packages marked allowClearUserData=false
1281 try {
1282 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1283 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001284 if (DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
Christopher Tatef7c886b2009-06-26 15:34:09 -07001285 + packageName);
1286 return;
1287 }
1288 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001289 Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
Christopher Tatef7c886b2009-06-26 15:34:09 -07001290 return;
1291 }
1292
Christopher Tatec7b31e32009-06-10 15:49:30 -07001293 ClearDataObserver observer = new ClearDataObserver();
1294
1295 synchronized(mClearDataLock) {
1296 mClearingData = true;
Christopher Tate9dfdac52009-08-06 14:57:53 -07001297 try {
1298 mActivityManager.clearApplicationUserData(packageName, observer);
1299 } catch (RemoteException e) {
1300 // can't happen because the activity manager is in this process
1301 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001302
1303 // only wait 10 seconds for the clear data to happen
1304 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1305 while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
1306 try {
1307 mClearDataLock.wait(5000);
1308 } catch (InterruptedException e) {
1309 // won't happen, but still.
1310 mClearingData = false;
1311 }
1312 }
1313 }
1314 }
1315
1316 class ClearDataObserver extends IPackageDataObserver.Stub {
Dan Egnor852f8e42009-09-30 11:20:45 -07001317 public void onRemoveCompleted(String packageName, boolean succeeded) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001318 synchronized(mClearDataLock) {
1319 mClearingData = false;
Christopher Tatef68eb502009-06-16 11:02:01 -07001320 mClearDataLock.notifyAll();
Christopher Tatec7b31e32009-06-10 15:49:30 -07001321 }
1322 }
1323 }
1324
Christopher Tate1bb69062010-02-19 17:02:12 -08001325 // Get the restore-set token for the best-available restore set for this package:
1326 // the active set if possible, else the ancestral one. Returns zero if none available.
1327 long getAvailableRestoreToken(String packageName) {
1328 long token = mAncestralToken;
1329 synchronized (mQueueLock) {
1330 if (mEverStoredApps.contains(packageName)) {
1331 token = mCurrentToken;
1332 }
1333 }
1334 return token;
1335 }
1336
Christopher Tate44a27902010-01-27 17:15:49 -08001337 // -----
1338 // Utility methods used by the asynchronous-with-timeout backup/restore operations
1339 boolean waitUntilOperationComplete(int token) {
1340 int finalState = OP_PENDING;
1341 synchronized (mCurrentOpLock) {
1342 try {
1343 while ((finalState = mCurrentOperations.get(token, OP_TIMEOUT)) == OP_PENDING) {
1344 try {
1345 mCurrentOpLock.wait();
1346 } catch (InterruptedException e) {}
1347 }
1348 } catch (IndexOutOfBoundsException e) {
1349 // the operation has been mysteriously cleared from our
1350 // bookkeeping -- consider this a success and ignore it.
1351 }
1352 }
1353 mBackupHandler.removeMessages(MSG_TIMEOUT);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001354 if (DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
Christopher Tate1bb69062010-02-19 17:02:12 -08001355 + " complete: finalState=" + finalState);
Christopher Tate44a27902010-01-27 17:15:49 -08001356 return finalState == OP_ACKNOWLEDGED;
1357 }
1358
1359 void prepareOperationTimeout(int token, long interval) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001360 if (DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
Christopher Tate1bb69062010-02-19 17:02:12 -08001361 + " interval=" + interval);
Christopher Tate4a627c72011-04-01 14:43:32 -07001362 synchronized (mCurrentOpLock) {
1363 mCurrentOperations.put(token, OP_PENDING);
1364 Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
1365 mBackupHandler.sendMessageDelayed(msg, interval);
1366 }
Christopher Tate44a27902010-01-27 17:15:49 -08001367 }
1368
Christopher Tate043dadc2009-06-02 16:11:00 -07001369 // ----- Back up a set of applications via a worker thread -----
1370
Christopher Tate44a27902010-01-27 17:15:49 -08001371 class PerformBackupTask implements Runnable {
Christopher Tate043dadc2009-06-02 16:11:00 -07001372 private static final String TAG = "PerformBackupThread";
Christopher Tateaa088442009-06-16 18:25:46 -07001373 IBackupTransport mTransport;
Christopher Tate043dadc2009-06-02 16:11:00 -07001374 ArrayList<BackupRequest> mQueue;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001375 File mStateDir;
Christopher Tatecde87f42009-06-12 12:55:53 -07001376 File mJournal;
Christopher Tate043dadc2009-06-02 16:11:00 -07001377
Christopher Tate44a27902010-01-27 17:15:49 -08001378 public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue,
Christopher Tatecde87f42009-06-12 12:55:53 -07001379 File journal) {
Christopher Tateaa088442009-06-16 18:25:46 -07001380 mTransport = transport;
Christopher Tate043dadc2009-06-02 16:11:00 -07001381 mQueue = queue;
Christopher Tatecde87f42009-06-12 12:55:53 -07001382 mJournal = journal;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001383
1384 try {
1385 mStateDir = new File(mBaseStateDir, transport.transportDirName());
1386 } catch (RemoteException e) {
1387 // can't happen; the transport is local
1388 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001389 }
1390
Christopher Tate043dadc2009-06-02 16:11:00 -07001391 public void run() {
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001392 int status = BackupConstants.TRANSPORT_OK;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001393 long startRealtime = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001394 if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
Christopher Tate043dadc2009-06-02 16:11:00 -07001395
Christopher Tate79588342009-06-30 16:11:49 -07001396 // Backups run at background priority
1397 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1398
Christopher Tate043dadc2009-06-02 16:11:00 -07001399 try {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001400 EventLog.writeEvent(EventLogTags.BACKUP_START, mTransport.transportDirName());
Dan Egnor01445162009-09-21 17:04:05 -07001401
Dan Egnor852f8e42009-09-30 11:20:45 -07001402 // If we haven't stored package manager metadata yet, we must init the transport.
1403 File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
1404 if (status == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001405 Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
Dan Egnor852f8e42009-09-30 11:20:45 -07001406 resetBackupState(mStateDir); // Just to make sure.
Dan Egnor01445162009-09-21 17:04:05 -07001407 status = mTransport.initializeDevice();
Dan Egnor726247c2009-09-29 19:12:31 -07001408 if (status == BackupConstants.TRANSPORT_OK) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001409 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07001410 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001411 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Joe Onorato8a9b2202010-02-26 18:56:32 -08001412 Slog.e(TAG, "Transport error in initializeDevice()");
Dan Egnor726247c2009-09-29 19:12:31 -07001413 }
Dan Egnor01445162009-09-21 17:04:05 -07001414 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001415
1416 // The package manager doesn't have a proper <application> etc, but since
1417 // it's running here in the system process we can just set up its agent
1418 // directly and use a synthetic BackupRequest. We always run this pass
1419 // because it's cheap and this way we guarantee that we don't get out of
1420 // step even if we're selecting among various transports at run time.
Dan Egnor01445162009-09-21 17:04:05 -07001421 if (status == BackupConstants.TRANSPORT_OK) {
1422 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
1423 mPackageManager, allAgentPackages());
Christopher Tate4a627c72011-04-01 14:43:32 -07001424 BackupRequest pmRequest = new BackupRequest(new ApplicationInfo());
Dan Egnor01445162009-09-21 17:04:05 -07001425 pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
1426 status = processOneBackup(pmRequest,
1427 IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
1428 }
Christopher Tate90967f42009-09-20 15:28:33 -07001429
Dan Egnor01445162009-09-21 17:04:05 -07001430 if (status == BackupConstants.TRANSPORT_OK) {
1431 // Now run all the backups in our queue
1432 status = doQueuedBackups(mTransport);
1433 }
1434
1435 if (status == BackupConstants.TRANSPORT_OK) {
1436 // Tell the transport to finish everything it has buffered
1437 status = mTransport.finishBackup();
1438 if (status == BackupConstants.TRANSPORT_OK) {
1439 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001440 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, mQueue.size(), millis);
Dan Egnor01445162009-09-21 17:04:05 -07001441 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001442 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(finish)");
Joe Onorato8a9b2202010-02-26 18:56:32 -08001443 Slog.e(TAG, "Transport error in finishBackup()");
Dan Egnor01445162009-09-21 17:04:05 -07001444 }
1445 }
1446
Dan Egnor01445162009-09-21 17:04:05 -07001447 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
Christopher Tated55e18a2009-09-21 10:12:59 -07001448 // The backend reports that our dataset has been wiped. We need to
1449 // reset all of our bookkeeping and instead run a new backup pass for
Christopher Tatec2af5d32010-02-02 15:18:58 -08001450 // everything.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001451 EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
Christopher Tated55e18a2009-09-21 10:12:59 -07001452 resetBackupState(mStateDir);
Dan Egnorbb9001c2009-07-27 12:20:13 -07001453 }
1454 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001455 Slog.e(TAG, "Error in backup thread", e);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001456 status = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001457 } finally {
Christopher Tate84725812010-02-04 15:52:40 -08001458 // If everything actually went through and this is the first time we've
1459 // done a backup, we can now record what the current backup dataset token
1460 // is.
Christopher Tate29505552010-06-24 15:58:01 -07001461 if ((mCurrentToken == 0) && (status == BackupConstants.TRANSPORT_OK)) {
Christopher Tate84725812010-02-04 15:52:40 -08001462 try {
1463 mCurrentToken = mTransport.getCurrentRestoreSet();
1464 } catch (RemoteException e) { /* cannot happen */ }
1465 writeRestoreTokens();
1466 }
1467
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001468 // If things went wrong, we need to re-stage the apps we had expected
1469 // to be backing up in this pass. This journals the package names in
1470 // the current active pending-backup file, not in the we are holding
1471 // here in mJournal.
1472 if (status != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001473 Slog.w(TAG, "Backup pass unsuccessful, restaging");
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001474 for (BackupRequest req : mQueue) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07001475 dataChangedImpl(req.appInfo.packageName);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001476 }
Christopher Tate21ab6a52009-09-24 18:01:46 -07001477
1478 // We also want to reset the backup schedule based on whatever
1479 // the transport suggests by way of retry/backoff time.
1480 try {
1481 startBackupAlarmsLocked(mTransport.requestBackupTime());
1482 } catch (RemoteException e) { /* cannot happen */ }
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001483 }
1484
1485 // Either backup was successful, in which case we of course do not need
1486 // this pass's journal any more; or it failed, in which case we just
1487 // re-enqueued all of these packages in the current active journal.
1488 // Either way, we no longer need this pass's journal.
Dan Egnor852f8e42009-09-30 11:20:45 -07001489 if (mJournal != null && !mJournal.delete()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001490 Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001491 }
1492
Christopher Tatec2af5d32010-02-02 15:18:58 -08001493 // Only once we're entirely finished do we release the wakelock
Dan Egnor852f8e42009-09-30 11:20:45 -07001494 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
Dan Egnor852f8e42009-09-30 11:20:45 -07001495 backupNow();
1496 }
1497
Dan Egnorbb9001c2009-07-27 12:20:13 -07001498 mWakelock.release();
Christopher Tatecde87f42009-06-12 12:55:53 -07001499 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001500 }
1501
Dan Egnor01445162009-09-21 17:04:05 -07001502 private int doQueuedBackups(IBackupTransport transport) {
Christopher Tate043dadc2009-06-02 16:11:00 -07001503 for (BackupRequest request : mQueue) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001504 Slog.d(TAG, "starting agent for backup of " + request);
Christopher Tate043dadc2009-06-02 16:11:00 -07001505
Christopher Tatec28083a2010-12-14 16:16:44 -08001506 // Verify that the requested app exists; it might be something that
1507 // requested a backup but was then uninstalled. The request was
1508 // journalled and rather than tamper with the journal it's safer
1509 // to sanity-check here.
1510 try {
1511 mPackageManager.getPackageInfo(request.appInfo.packageName, 0);
1512 } catch (NameNotFoundException e) {
1513 Slog.d(TAG, "Package does not exist; skipping");
1514 continue;
1515 }
1516
Christopher Tate043dadc2009-06-02 16:11:00 -07001517 IBackupAgent agent = null;
Christopher Tate043dadc2009-06-02 16:11:00 -07001518 try {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001519 mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
Christopher Tate4a627c72011-04-01 14:43:32 -07001520 agent = bindToAgentSynchronous(request.appInfo,
1521 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001522 if (agent != null) {
Dan Egnor01445162009-09-21 17:04:05 -07001523 int result = processOneBackup(request, agent, transport);
1524 if (result != BackupConstants.TRANSPORT_OK) return result;
Christopher Tate043dadc2009-06-02 16:11:00 -07001525 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001526 } catch (SecurityException ex) {
1527 // Try for the next one.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001528 Slog.d(TAG, "error in bind/backup", ex);
Dan Egnor01445162009-09-21 17:04:05 -07001529 } finally {
1530 try { // unbind even on timeout, just in case
1531 mActivityManager.unbindBackupAgent(request.appInfo);
1532 } catch (RemoteException e) {}
Christopher Tate043dadc2009-06-02 16:11:00 -07001533 }
1534 }
Dan Egnor01445162009-09-21 17:04:05 -07001535
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001536 mWakelock.setWorkSource(null);
1537
Dan Egnor01445162009-09-21 17:04:05 -07001538 return BackupConstants.TRANSPORT_OK;
Christopher Tate043dadc2009-06-02 16:11:00 -07001539 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001540
Dan Egnor01445162009-09-21 17:04:05 -07001541 private int processOneBackup(BackupRequest request, IBackupAgent agent,
1542 IBackupTransport transport) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001543 final String packageName = request.appInfo.packageName;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001544 if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001545
Dan Egnorbb9001c2009-07-27 12:20:13 -07001546 File savedStateName = new File(mStateDir, packageName);
1547 File backupDataName = new File(mDataDir, packageName + ".data");
1548 File newStateName = new File(mStateDir, packageName + ".new");
1549
1550 ParcelFileDescriptor savedState = null;
1551 ParcelFileDescriptor backupData = null;
1552 ParcelFileDescriptor newState = null;
1553
1554 PackageInfo packInfo;
Christopher Tate4a627c72011-04-01 14:43:32 -07001555 final int token = generateToken();
Christopher Tatec7b31e32009-06-10 15:49:30 -07001556 try {
1557 // Look up the package info & signatures. This is first so that if it
1558 // throws an exception, there's no file setup yet that would need to
1559 // be unraveled.
Christopher Tateabce4e82009-06-18 18:35:32 -07001560 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
1561 // The metadata 'package' is synthetic
1562 packInfo = new PackageInfo();
1563 packInfo.packageName = packageName;
1564 } else {
1565 packInfo = mPackageManager.getPackageInfo(packageName,
Christopher Tatec7b31e32009-06-10 15:49:30 -07001566 PackageManager.GET_SIGNATURES);
Christopher Tateabce4e82009-06-18 18:35:32 -07001567 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001568
Christopher Tatec7b31e32009-06-10 15:49:30 -07001569 // In a full backup, we pass a null ParcelFileDescriptor as
Christopher Tate4a627c72011-04-01 14:43:32 -07001570 // the saved-state "file". This is by definition an incremental,
1571 // so we build a saved state file to pass.
1572 savedState = ParcelFileDescriptor.open(savedStateName,
1573 ParcelFileDescriptor.MODE_READ_ONLY |
1574 ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
Christopher Tatec7b31e32009-06-10 15:49:30 -07001575
Dan Egnorbb9001c2009-07-27 12:20:13 -07001576 backupData = ParcelFileDescriptor.open(backupDataName,
1577 ParcelFileDescriptor.MODE_READ_WRITE |
1578 ParcelFileDescriptor.MODE_CREATE |
1579 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001580
Dan Egnorbb9001c2009-07-27 12:20:13 -07001581 newState = ParcelFileDescriptor.open(newStateName,
1582 ParcelFileDescriptor.MODE_READ_WRITE |
1583 ParcelFileDescriptor.MODE_CREATE |
1584 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001585
Christopher Tate44a27902010-01-27 17:15:49 -08001586 // Initiate the target's backup pass
1587 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
Christopher Tate4a627c72011-04-01 14:43:32 -07001588 agent.doBackup(savedState, backupData, newState, false,
1589 token, mBackupManagerBinder);
Christopher Tate44a27902010-01-27 17:15:49 -08001590 boolean success = waitUntilOperationComplete(token);
1591
1592 if (!success) {
1593 // timeout -- bail out into the failed-transaction logic
1594 throw new RuntimeException("Backup timeout");
1595 }
1596
Dan Egnorbb9001c2009-07-27 12:20:13 -07001597 logBackupComplete(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001598 if (DEBUG) Slog.v(TAG, "doBackup() success");
Christopher Tatec7b31e32009-06-10 15:49:30 -07001599 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001600 Slog.e(TAG, "Error backing up " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001601 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString());
Dan Egnorbb9001c2009-07-27 12:20:13 -07001602 backupDataName.delete();
1603 newStateName.delete();
Christopher Tated55e18a2009-09-21 10:12:59 -07001604 return BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001605 } finally {
1606 try { if (savedState != null) savedState.close(); } catch (IOException e) {}
1607 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1608 try { if (newState != null) newState.close(); } catch (IOException e) {}
1609 savedState = backupData = newState = null;
Christopher Tate44a27902010-01-27 17:15:49 -08001610 synchronized (mCurrentOpLock) {
1611 mCurrentOperations.clear();
1612 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001613 }
1614
1615 // Now propagate the newly-backed-up data to the transport
Dan Egnor01445162009-09-21 17:04:05 -07001616 int result = BackupConstants.TRANSPORT_OK;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001617 try {
1618 int size = (int) backupDataName.length();
1619 if (size > 0) {
Dan Egnor01445162009-09-21 17:04:05 -07001620 if (result == BackupConstants.TRANSPORT_OK) {
1621 backupData = ParcelFileDescriptor.open(backupDataName,
1622 ParcelFileDescriptor.MODE_READ_ONLY);
1623 result = transport.performBackup(packInfo, backupData);
1624 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001625
Dan Egnor83861e72009-09-17 16:17:55 -07001626 // TODO - We call finishBackup() for each application backed up, because
1627 // we need to know now whether it succeeded or failed. Instead, we should
1628 // hold off on finishBackup() until the end, which implies holding off on
1629 // renaming *all* the output state files (see below) until that happens.
1630
Dan Egnor01445162009-09-21 17:04:05 -07001631 if (result == BackupConstants.TRANSPORT_OK) {
1632 result = transport.finishBackup();
Dan Egnor83861e72009-09-17 16:17:55 -07001633 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001634 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001635 if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
Dan Egnorbb9001c2009-07-27 12:20:13 -07001636 }
1637
1638 // After successful transport, delete the now-stale data
1639 // and juggle the files so that next time we supply the agent
1640 // with the new state file it just created.
Dan Egnor01445162009-09-21 17:04:05 -07001641 if (result == BackupConstants.TRANSPORT_OK) {
1642 backupDataName.delete();
1643 newStateName.renameTo(savedStateName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001644 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, packageName, size);
Dan Egnor01445162009-09-21 17:04:05 -07001645 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001646 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
Dan Egnor01445162009-09-21 17:04:05 -07001647 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001648 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001649 Slog.e(TAG, "Transport error backing up " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001650 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
Dan Egnor01445162009-09-21 17:04:05 -07001651 result = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001652 } finally {
1653 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
Christopher Tatec7b31e32009-06-10 15:49:30 -07001654 }
Christopher Tated55e18a2009-09-21 10:12:59 -07001655
Dan Egnor01445162009-09-21 17:04:05 -07001656 return result;
Christopher Tatec7b31e32009-06-10 15:49:30 -07001657 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001658 }
1659
Christopher Tatedf01dea2009-06-09 20:45:02 -07001660
Christopher Tate4a627c72011-04-01 14:43:32 -07001661 // ----- Full backup to a file/socket -----
1662
1663 class PerformFullBackupTask implements Runnable {
1664 ParcelFileDescriptor mOutputFile;
1665 IFullBackupRestoreObserver mObserver;
1666 boolean mIncludeApks;
1667 boolean mIncludeShared;
1668 boolean mAllApps;
1669 String[] mPackages;
1670 AtomicBoolean mLatchObject;
1671 File mFilesDir;
1672 File mManifestFile;
1673
1674 PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
1675 boolean includeApks, boolean includeShared,
1676 boolean doAllApps, String[] packages, AtomicBoolean latch) {
1677 mOutputFile = fd;
1678 mObserver = observer;
1679 mIncludeApks = includeApks;
1680 mIncludeShared = includeShared;
1681 mAllApps = doAllApps;
1682 mPackages = packages;
1683 mLatchObject = latch;
1684
1685 mFilesDir = new File("/data/system");
1686 mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
1687 }
1688
1689 @Override
1690 public void run() {
1691 final List<PackageInfo> packagesToBackup;
1692
Christopher Tate75a99702011-05-18 16:28:19 -07001693 Slog.i(TAG, "--- Performing full-dataset restore ---");
Christopher Tate4a627c72011-04-01 14:43:32 -07001694 sendStartBackup();
1695
1696 // doAllApps supersedes the package set if any
1697 if (mAllApps) {
1698 packagesToBackup = mPackageManager.getInstalledPackages(
1699 PackageManager.GET_SIGNATURES);
1700 } else {
1701 packagesToBackup = new ArrayList<PackageInfo>();
1702 for (String pkgName : mPackages) {
1703 try {
1704 packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
1705 PackageManager.GET_SIGNATURES));
1706 } catch (NameNotFoundException e) {
1707 Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
1708 }
1709 }
1710 }
1711
1712 // Now back up the app data via the agent mechanism
1713 PackageInfo pkg = null;
1714 try {
1715 int N = packagesToBackup.size();
1716 for (int i = 0; i < N; i++) {
1717 pkg = packagesToBackup.get(i);
1718
1719 Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
1720
1721 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
1722 IApplicationThread.BACKUP_MODE_FULL);
1723 if (agent != null) {
1724 try {
Christopher Tatedc92c822011-05-13 15:38:02 -07001725 ApplicationInfo app = pkg.applicationInfo;
Christopher Tate4a627c72011-04-01 14:43:32 -07001726 boolean sendApk = mIncludeApks
1727 && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
1728 && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
1729 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
1730
1731 sendOnBackupPackage(pkg.packageName);
1732
1733 {
1734 BackupDataOutput output = new BackupDataOutput(
1735 mOutputFile.getFileDescriptor());
1736
1737 if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
1738 writeAppManifest(pkg, mManifestFile, sendApk);
1739 FullBackup.backupToTar(pkg.packageName, null, null,
1740 mFilesDir.getAbsolutePath(),
1741 mManifestFile.getAbsolutePath(),
1742 output);
1743 }
1744
1745 if (DEBUG) Slog.d(TAG, "Calling doBackup()");
1746 final int token = generateToken();
1747 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
1748 agent.doBackup(null, mOutputFile, null, sendApk,
1749 token, mBackupManagerBinder);
1750 boolean success = waitUntilOperationComplete(token);
1751 if (!success) {
1752 Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
1753 } else {
1754 if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
1755 }
Christopher Tate4a627c72011-04-01 14:43:32 -07001756 } catch (IOException e) {
1757 Slog.e(TAG, "Error backing up " + pkg.packageName, e);
1758 }
1759 } else {
1760 Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
1761 }
1762 tearDown(pkg);
1763 }
1764 } catch (RemoteException e) {
1765 Slog.e(TAG, "App died during full backup");
1766 } finally {
1767 if (pkg != null) {
1768 tearDown(pkg);
1769 }
1770 try {
1771 mOutputFile.close();
1772 } catch (IOException e) {
1773 /* nothing we can do about this */
1774 }
1775 synchronized (mCurrentOpLock) {
1776 mCurrentOperations.clear();
1777 }
1778 synchronized (mLatchObject) {
1779 mLatchObject.set(true);
1780 mLatchObject.notifyAll();
1781 }
1782 sendEndBackup();
1783 mWakelock.release();
1784 if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
1785 }
1786 }
1787
1788 private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
1789 throws IOException {
1790 // Manifest format. All data are strings ending in LF:
1791 // BACKUP_MANIFEST_VERSION, currently 1
1792 //
1793 // Version 1:
1794 // package name
1795 // package's versionCode
Christopher Tate75a99702011-05-18 16:28:19 -07001796 // platform versionCode
1797 // getInstallerPackageName() for this package (maybe empty)
1798 // boolean: "1" if archive includes .apk; any other string means not
Christopher Tate4a627c72011-04-01 14:43:32 -07001799 // number of signatures == N
1800 // N*: signature byte array in ascii format per Signature.toCharsString()
1801 StringBuilder builder = new StringBuilder(4096);
1802 StringBuilderPrinter printer = new StringBuilderPrinter(builder);
1803
1804 printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
1805 printer.println(pkg.packageName);
1806 printer.println(Integer.toString(pkg.versionCode));
Christopher Tate75a99702011-05-18 16:28:19 -07001807 printer.println(Integer.toString(Build.VERSION.SDK_INT));
1808
1809 String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
1810 printer.println((installerName != null) ? installerName : "");
1811
Christopher Tate4a627c72011-04-01 14:43:32 -07001812 printer.println(withApk ? "1" : "0");
1813 if (pkg.signatures == null) {
1814 printer.println("0");
1815 } else {
1816 printer.println(Integer.toString(pkg.signatures.length));
1817 for (Signature sig : pkg.signatures) {
1818 printer.println(sig.toCharsString());
1819 }
1820 }
1821
1822 FileOutputStream outstream = new FileOutputStream(manifestFile);
Christopher Tate4a627c72011-04-01 14:43:32 -07001823 outstream.write(builder.toString().getBytes());
1824 outstream.close();
1825 }
1826
1827 private void tearDown(PackageInfo pkg) {
1828 final ApplicationInfo app = pkg.applicationInfo;
1829 try {
1830 // unbind and tidy up even on timeout or failure, just in case
1831 mActivityManager.unbindBackupAgent(app);
1832
Christopher Tatedc92c822011-05-13 15:38:02 -07001833 // The agent was running with a stub Application object, so shut it down.
1834 // !!! We hardcode the confirmation UI's package name here rather than use a
1835 // manifest flag! TODO something less direct.
1836 if (app.uid != Process.SYSTEM_UID
1837 && !pkg.packageName.equals("com.android.backupconfirm")) {
Christopher Tate4a627c72011-04-01 14:43:32 -07001838 if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
1839 mActivityManager.killApplicationProcess(app.processName, app.uid);
1840 } else {
1841 if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
1842 }
1843 } catch (RemoteException e) {
1844 Slog.d(TAG, "Lost app trying to shut down");
1845 }
1846 }
1847
1848 // wrappers for observer use
1849 void sendStartBackup() {
1850 if (mObserver != null) {
1851 try {
1852 mObserver.onStartBackup();
1853 } catch (RemoteException e) {
1854 Slog.w(TAG, "full backup observer went away: startBackup");
1855 mObserver = null;
1856 }
1857 }
1858 }
1859
1860 void sendOnBackupPackage(String name) {
1861 if (mObserver != null) {
1862 try {
1863 // TODO: use a more user-friendly name string
1864 mObserver.onBackupPackage(name);
1865 } catch (RemoteException e) {
1866 Slog.w(TAG, "full backup observer went away: backupPackage");
1867 mObserver = null;
1868 }
1869 }
1870 }
1871
1872 void sendEndBackup() {
1873 if (mObserver != null) {
1874 try {
1875 mObserver.onEndBackup();
1876 } catch (RemoteException e) {
1877 Slog.w(TAG, "full backup observer went away: endBackup");
1878 mObserver = null;
1879 }
1880 }
1881 }
1882 }
1883
1884
Christopher Tate75a99702011-05-18 16:28:19 -07001885 // ----- Full restore from a file/socket -----
1886
1887 // Description of a file in the restore datastream
1888 static class FileMetadata {
1889 String packageName; // name of the owning app
1890 String installerPackageName; // name of the market-type app that installed the owner
1891 int type; // e.g. FullBackup.TYPE_DIRECTORY
1892 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN
1893 String path; // subpath within the semantic domain
1894 long mode; // e.g. 0666 (actually int)
1895 long mtime; // last mod time, UTC time_t (actually int)
1896 long size; // bytes of content
1897 }
1898
1899 enum RestorePolicy {
1900 IGNORE,
1901 ACCEPT,
1902 ACCEPT_IF_APK
1903 }
1904
1905 class PerformFullRestoreTask implements Runnable {
1906 ParcelFileDescriptor mInputFile;
1907 IFullBackupRestoreObserver mObserver;
1908 AtomicBoolean mLatchObject;
1909 IBackupAgent mAgent;
1910 String mAgentPackage;
1911 ApplicationInfo mTargetApp;
1912 ParcelFileDescriptor[] mPipes = null;
1913
1914 // possible handling states for a given package in the restore dataset
1915 final HashMap<String, RestorePolicy> mPackagePolicies
1916 = new HashMap<String, RestorePolicy>();
1917
1918 // installer package names for each encountered app, derived from the manifests
1919 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
1920
1921 // Signatures for a given package found in its manifest file
1922 final HashMap<String, Signature[]> mManifestSignatures
1923 = new HashMap<String, Signature[]>();
1924
1925 // Packages we've already wiped data on when restoring their first file
1926 final HashSet<String> mClearedPackages = new HashSet<String>();
1927
1928 PerformFullRestoreTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
1929 AtomicBoolean latch) {
1930 mInputFile = fd;
1931 mObserver = observer;
1932 mLatchObject = latch;
1933 mAgent = null;
1934 mAgentPackage = null;
1935 mTargetApp = null;
1936
1937 // Which packages we've already wiped data on. We prepopulate this
1938 // with a whitelist of packages known to be unclearable.
1939 mClearedPackages.add("android");
1940 mClearedPackages.add("com.android.backupconfirm");
1941 mClearedPackages.add("com.android.providers.settings");
1942 }
1943
1944 class RestoreFileRunnable implements Runnable {
1945 IBackupAgent mAgent;
1946 FileMetadata mInfo;
1947 ParcelFileDescriptor mSocket;
1948 int mToken;
1949
1950 RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
1951 ParcelFileDescriptor socket, int token) throws IOException {
1952 mAgent = agent;
1953 mInfo = info;
1954 mToken = token;
1955
1956 // This class is used strictly for process-local binder invocations. The
1957 // semantics of ParcelFileDescriptor differ in this case; in particular, we
1958 // do not automatically get a 'dup'ed descriptor that we can can continue
1959 // to use asynchronously from the caller. So, we make sure to dup it ourselves
1960 // before proceeding to do the restore.
1961 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
1962 }
1963
1964 @Override
1965 public void run() {
1966 try {
1967 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
1968 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
1969 mToken, mBackupManagerBinder);
1970 } catch (RemoteException e) {
1971 // never happens; this is used strictly for local binder calls
1972 }
1973 }
1974 }
1975
1976 @Override
1977 public void run() {
1978 Slog.i(TAG, "--- Performing full-dataset restore ---");
1979 sendStartRestore();
1980
1981 try {
1982 byte[] buffer = new byte[32 * 1024];
1983 FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
1984
1985 boolean didRestore;
1986 do {
1987 didRestore = restoreOneFile(instream, buffer);
1988 } while (didRestore);
1989
1990 if (DEBUG) Slog.v(TAG, "Done consuming input tarfile");
1991 } finally {
1992 tearDownPipes();
1993 tearDownAgent(mTargetApp);
1994
1995 try {
1996 mInputFile.close();
1997 } catch (IOException e) {
1998 /* nothing we can do about this */
1999 }
2000 synchronized (mCurrentOpLock) {
2001 mCurrentOperations.clear();
2002 }
2003 synchronized (mLatchObject) {
2004 mLatchObject.set(true);
2005 mLatchObject.notifyAll();
2006 }
2007 sendEndRestore();
2008 mWakelock.release();
2009 if (DEBUG) Slog.d(TAG, "Full restore pass complete.");
2010 }
2011 }
2012
2013 boolean restoreOneFile(InputStream instream, byte[] buffer) {
2014 FileMetadata info;
2015 try {
2016 info = readTarHeaders(instream);
2017 if (info != null) {
2018 if (DEBUG) {
2019 dumpFileMetadata(info);
2020 }
2021
2022 final String pkg = info.packageName;
2023 if (!pkg.equals(mAgentPackage)) {
2024 // okay, change in package; set up our various
2025 // bookkeeping if we haven't seen it yet
2026 if (!mPackagePolicies.containsKey(pkg)) {
2027 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2028 }
2029
2030 // Clean up the previous agent relationship if necessary,
2031 // and let the observer know we're considering a new app.
2032 if (mAgent != null) {
2033 if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
2034 tearDownPipes();
2035 tearDownAgent(mTargetApp);
2036 mTargetApp = null;
2037 mAgentPackage = null;
2038 }
2039 }
2040
2041 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2042 mPackagePolicies.put(pkg, readAppManifest(info, instream));
2043 mPackageInstallers.put(pkg, info.installerPackageName);
2044 // We've read only the manifest content itself at this point,
2045 // so consume the footer before looping around to the next
2046 // input file
2047 skipTarPadding(info.size, instream);
2048 sendOnRestorePackage(pkg);
2049 } else {
2050 // Non-manifest, so it's actual file data. Is this a package
2051 // we're ignoring?
2052 boolean okay = true;
2053 RestorePolicy policy = mPackagePolicies.get(pkg);
2054 switch (policy) {
2055 case IGNORE:
2056 okay = false;
2057 break;
2058
2059 case ACCEPT_IF_APK:
2060 // If we're in accept-if-apk state, then the first file we
2061 // see MUST be the apk.
2062 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2063 if (DEBUG) Slog.d(TAG, "APK file; installing");
2064 // Try to install the app.
2065 String installerName = mPackageInstallers.get(pkg);
2066 okay = installApk(info, installerName, instream);
2067 // good to go; promote to ACCEPT
2068 mPackagePolicies.put(pkg, (okay)
2069 ? RestorePolicy.ACCEPT
2070 : RestorePolicy.IGNORE);
2071 // At this point we've consumed this file entry
2072 // ourselves, so just strip the tar footer and
2073 // go on to the next file in the input stream
2074 skipTarPadding(info.size, instream);
2075 return true;
2076 } else {
2077 // File data before (or without) the apk. We can't
2078 // handle it coherently in this case so ignore it.
2079 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2080 okay = false;
2081 }
2082 break;
2083
2084 case ACCEPT:
2085 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2086 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
2087 // we can take the data without the apk, so we
2088 // *want* to do so. skip the apk by declaring this
2089 // one file not-okay without changing the restore
2090 // policy for the package.
2091 okay = false;
2092 }
2093 break;
2094
2095 default:
2096 // Something has gone dreadfully wrong when determining
2097 // the restore policy from the manifest. Ignore the
2098 // rest of this package's data.
2099 Slog.e(TAG, "Invalid policy from manifest");
2100 okay = false;
2101 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2102 break;
2103 }
2104
2105 // If the policy is satisfied, go ahead and set up to pipe the
2106 // data to the agent.
2107 if (DEBUG && okay && mAgent != null) {
2108 Slog.i(TAG, "Reusing existing agent instance");
2109 }
2110 if (okay && mAgent == null) {
2111 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
2112
2113 try {
2114 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
2115
2116 // If we haven't sent any data to this app yet, we probably
2117 // need to clear it first. Check that.
2118 if (!mClearedPackages.contains(pkg)) {
2119 // apps with their own full backup agents are
2120 // responsible for coherently managing a full
2121 // restore.
2122 if (mTargetApp.fullBackupAgentName == null) {
2123 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
2124 clearApplicationDataSynchronous(pkg);
2125 } else {
2126 if (DEBUG) Slog.d(TAG, "full backup agent ("
2127 + mTargetApp.fullBackupAgentName + ") => no clear");
2128 }
2129 mClearedPackages.add(pkg);
2130 } else {
2131 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
2132 }
2133
2134 // All set; now set up the IPC and launch the agent
2135 setUpPipes();
2136 mAgent = bindToAgentSynchronous(mTargetApp,
2137 IApplicationThread.BACKUP_MODE_RESTORE_FULL);
2138 mAgentPackage = pkg;
2139 } catch (IOException e) {
2140 // fall through to error handling
2141 } catch (NameNotFoundException e) {
2142 // fall through to error handling
2143 }
2144
2145 if (mAgent == null) {
2146 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
2147 okay = false;
2148 tearDownPipes();
2149 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2150 }
2151 }
2152
2153 // Sanity check: make sure we never give data to the wrong app. This
2154 // should never happen but a little paranoia here won't go amiss.
2155 if (okay && !pkg.equals(mAgentPackage)) {
2156 Slog.e(TAG, "Restoring data for " + pkg
2157 + " but agent is for " + mAgentPackage);
2158 okay = false;
2159 }
2160
2161 // At this point we have an agent ready to handle the full
2162 // restore data as well as a pipe for sending data to
2163 // that agent. Tell the agent to start reading from the
2164 // pipe.
2165 if (okay) {
2166 boolean agentSuccess = true;
2167 long toCopy = info.size;
2168 final int token = generateToken();
2169 try {
2170 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
2171 + info.path);
2172 prepareOperationTimeout(token,
2173 TIMEOUT_FULL_BACKUP_INTERVAL);
2174 // fire up the app's agent listening on the socket. If
2175 // the agent is running in the system process we can't
2176 // just invoke it asynchronously, so we provide a thread
2177 // for it here.
2178 if (mTargetApp.processName.equals("system")) {
2179 Slog.d(TAG, "system process agent - spinning a thread");
2180 RestoreFileRunnable runner = new RestoreFileRunnable(
2181 mAgent, info, mPipes[0], token);
2182 new Thread(runner).start();
2183 } else {
2184 mAgent.doRestoreFile(mPipes[0], info.size, info.type,
2185 info.domain, info.path, info.mode, info.mtime,
2186 token, mBackupManagerBinder);
2187 }
2188 } catch (IOException e) {
2189 // couldn't dup the socket for a process-local restore
2190 Slog.d(TAG, "Couldn't establish restore");
2191 agentSuccess = false;
2192 okay = false;
2193 } catch (RemoteException e) {
2194 // whoops, remote agent went away. We'll eat the content
2195 // ourselves, then, and not copy it over.
2196 Slog.e(TAG, "Agent crashed during full restore");
2197 agentSuccess = false;
2198 okay = false;
2199 }
2200
2201 // Copy over the data if the agent is still good
2202 if (okay) {
2203 boolean pipeOkay = true;
2204 FileOutputStream pipe = new FileOutputStream(
2205 mPipes[1].getFileDescriptor());
2206 if (DEBUG) Slog.d(TAG, "Piping data to agent");
2207 while (toCopy > 0) {
2208 int toRead = (toCopy > buffer.length)
2209 ? buffer.length : (int)toCopy;
2210 int nRead = instream.read(buffer, 0, toRead);
2211 if (nRead <= 0) break;
2212 toCopy -= nRead;
2213
2214 // send it to the output pipe as long as things
2215 // are still good
2216 if (pipeOkay) {
2217 try {
2218 pipe.write(buffer, 0, nRead);
2219 } catch (IOException e) {
2220 Slog.e(TAG,
2221 "Failed to write to restore pipe", e);
2222 pipeOkay = false;
2223 }
2224 }
2225 }
2226
2227 // done sending that file! Now we just need to consume
2228 // the delta from info.size to the end of block.
2229 skipTarPadding(info.size, instream);
2230
2231 // and now that we've sent it all, wait for the remote
2232 // side to acknowledge receipt
2233 agentSuccess = waitUntilOperationComplete(token);
2234 }
2235
2236 // okay, if the remote end failed at any point, deal with
2237 // it by ignoring the rest of the restore on it
2238 if (!agentSuccess) {
2239 mBackupHandler.removeMessages(MSG_TIMEOUT);
2240 tearDownPipes();
2241 tearDownAgent(mTargetApp);
2242 mAgent = null;
2243 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2244 }
2245 }
2246
2247 // Problems setting up the agent communication, or an already-
2248 // ignored package: skip to the next tar stream entry by
2249 // reading and discarding this file.
2250 if (!okay) {
2251 if (DEBUG) Slog.d(TAG, "[discarding file content]");
2252 long bytesToConsume = (info.size + 511) & ~511;
2253 while (bytesToConsume > 0) {
2254 int toRead = (bytesToConsume > buffer.length)
2255 ? buffer.length : (int)bytesToConsume;
2256 long nRead = instream.read(buffer, 0, toRead);
2257 if (nRead <= 0) break;
2258 bytesToConsume -= nRead;
2259 }
2260 }
2261 }
2262 }
2263 } catch (IOException e) {
2264 Slog.w(TAG, "io exception on restore socket read", e);
2265 // treat as EOF
2266 info = null;
2267 }
2268
2269 return (info != null);
2270 }
2271
2272 void setUpPipes() throws IOException {
2273 mPipes = ParcelFileDescriptor.createPipe();
2274 }
2275
2276 void tearDownPipes() {
2277 if (mPipes != null) {
2278 if (mPipes[0] != null) {
2279 try {
2280 mPipes[0].close();
2281 mPipes[0] = null;
2282 mPipes[1].close();
2283 mPipes[1] = null;
2284 } catch (IOException e) {
2285 Slog.w(TAG, "Couldn't close agent pipes", e);
2286 }
2287 }
2288 mPipes = null;
2289 }
2290 }
2291
2292 void tearDownAgent(ApplicationInfo app) {
2293 if (mAgent != null) {
2294 try {
2295 // unbind and tidy up even on timeout or failure, just in case
2296 mActivityManager.unbindBackupAgent(app);
2297
2298 // The agent was running with a stub Application object, so shut it down.
2299 // !!! We hardcode the confirmation UI's package name here rather than use a
2300 // manifest flag! TODO something less direct.
2301 if (app.uid != Process.SYSTEM_UID
2302 && !app.packageName.equals("com.android.backupconfirm")) {
2303 if (DEBUG) Slog.d(TAG, "Killing host process");
2304 mActivityManager.killApplicationProcess(app.processName, app.uid);
2305 } else {
2306 if (DEBUG) Slog.d(TAG, "Not killing after full restore");
2307 }
2308 } catch (RemoteException e) {
2309 Slog.d(TAG, "Lost app trying to shut down");
2310 }
2311 mAgent = null;
2312 }
2313 }
2314
2315 class RestoreInstallObserver extends IPackageInstallObserver.Stub {
2316 final AtomicBoolean mDone = new AtomicBoolean();
2317 int mResult;
2318
2319 public void reset() {
2320 synchronized (mDone) {
2321 mDone.set(false);
2322 }
2323 }
2324
2325 public void waitForCompletion() {
2326 synchronized (mDone) {
2327 while (mDone.get() == false) {
2328 try {
2329 mDone.wait();
2330 } catch (InterruptedException e) { }
2331 }
2332 }
2333 }
2334
2335 int getResult() {
2336 return mResult;
2337 }
2338
2339 @Override
2340 public void packageInstalled(String packageName, int returnCode)
2341 throws RemoteException {
2342 synchronized (mDone) {
2343 mResult = returnCode;
2344 mDone.set(true);
2345 mDone.notifyAll();
2346 }
2347 }
2348 }
2349 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
2350
2351 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
2352 boolean okay = true;
2353
2354 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
2355
2356 // The file content is an .apk file. Copy it out to a staging location and
2357 // attempt to install it.
2358 File apkFile = new File(mDataDir, info.packageName);
2359 try {
2360 FileOutputStream apkStream = new FileOutputStream(apkFile);
2361 byte[] buffer = new byte[32 * 1024];
2362 long size = info.size;
2363 while (size > 0) {
2364 long toRead = (buffer.length < size) ? buffer.length : size;
2365 int didRead = instream.read(buffer, 0, (int)toRead);
2366 apkStream.write(buffer, 0, didRead);
2367 size -= didRead;
2368 }
2369 apkStream.close();
2370
2371 // make sure the installer can read it
2372 apkFile.setReadable(true, false);
2373
2374 // Now install it
2375 Uri packageUri = Uri.fromFile(apkFile);
2376 mInstallObserver.reset();
2377 mPackageManager.installPackage(packageUri, mInstallObserver,
2378 PackageManager.INSTALL_REPLACE_EXISTING, installerPackage);
2379 mInstallObserver.waitForCompletion();
2380
2381 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
2382 // The only time we continue to accept install of data even if the
2383 // apk install failed is if we had already determined that we could
2384 // accept the data regardless.
2385 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
2386 okay = false;
2387 }
2388 }
2389 } catch (IOException e) {
2390 Slog.e(TAG, "Unable to transcribe restored apk for install");
2391 okay = false;
2392 } finally {
2393 apkFile.delete();
2394 }
2395
2396 return okay;
2397 }
2398
2399 // Given an actual file content size, consume the post-content padding mandated
2400 // by the tar format.
2401 void skipTarPadding(long size, InputStream instream) throws IOException {
2402 long partial = (size + 512) % 512;
2403 if (partial > 0) {
2404 byte[] buffer = new byte[512];
2405 instream.read(buffer, 0, 512 - (int)partial);
2406 }
2407 }
2408
2409 // Returns a policy constant; takes a buffer arg to reduce memory churn
2410 RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
2411 throws IOException {
2412 // Fail on suspiciously large manifest files
2413 if (info.size > 64 * 1024) {
2414 throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
2415 }
2416 byte[] buffer = new byte[(int) info.size];
2417 int nRead = 0;
2418 while (nRead < info.size) {
2419 nRead += instream.read(buffer, nRead, (int)info.size - nRead);
2420 }
2421
2422 RestorePolicy policy = RestorePolicy.IGNORE;
2423 String[] str = new String[1];
2424 int offset = 0;
2425
2426 try {
2427 offset = extractLine(buffer, offset, str);
2428 int version = Integer.parseInt(str[0]);
2429 if (version == BACKUP_MANIFEST_VERSION) {
2430 offset = extractLine(buffer, offset, str);
2431 String manifestPackage = str[0];
2432 // TODO: handle <original-package>
2433 if (manifestPackage.equals(info.packageName)) {
2434 offset = extractLine(buffer, offset, str);
2435 version = Integer.parseInt(str[0]); // app version
2436 offset = extractLine(buffer, offset, str);
2437 int platformVersion = Integer.parseInt(str[0]);
2438 offset = extractLine(buffer, offset, str);
2439 info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
2440 offset = extractLine(buffer, offset, str);
2441 boolean hasApk = str[0].equals("1");
2442 offset = extractLine(buffer, offset, str);
2443 int numSigs = Integer.parseInt(str[0]);
2444 Signature[] sigs = null;
2445 if (numSigs > 0) {
2446 sigs = new Signature[numSigs];
2447 for (int i = 0; i < numSigs; i++) {
2448 offset = extractLine(buffer, offset, str);
2449 sigs[i] = new Signature(str[0]);
2450 }
2451
2452 // Okay, got the manifest info we need...
2453 try {
2454 // Verify signatures against any installed version; if they
2455 // don't match, then we fall though and ignore the data. The
2456 // signatureMatch() method explicitly ignores the signature
2457 // check for packages installed on the system partition, because
2458 // such packages are signed with the platform cert instead of
2459 // the app developer's cert, so they're different on every
2460 // device.
2461 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2462 info.packageName, PackageManager.GET_SIGNATURES);
2463 if (signaturesMatch(sigs, pkgInfo)) {
2464 if (pkgInfo.versionCode >= version) {
2465 Slog.i(TAG, "Sig + version match; taking data");
2466 policy = RestorePolicy.ACCEPT;
2467 } else {
2468 // The data is from a newer version of the app than
2469 // is presently installed. That means we can only
2470 // use it if the matching apk is also supplied.
2471 Slog.d(TAG, "Data version " + version
2472 + " is newer than installed version "
2473 + pkgInfo.versionCode + " - requiring apk");
2474 policy = RestorePolicy.ACCEPT_IF_APK;
2475 }
2476 }
2477 } catch (NameNotFoundException e) {
2478 // Okay, the target app isn't installed. We can process
2479 // the restore properly only if the dataset provides the
2480 // apk file and we can successfully install it.
2481 if (DEBUG) Slog.i(TAG, "Package " + info.packageName
2482 + " not installed; requiring apk in dataset");
2483 policy = RestorePolicy.ACCEPT_IF_APK;
2484 }
2485
2486 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
2487 Slog.i(TAG, "Cannot restore package " + info.packageName
2488 + " without the matching .apk");
2489 }
2490 } else {
2491 Slog.i(TAG, "Missing signature on backed-up package "
2492 + info.packageName);
2493 }
2494 } else {
2495 Slog.i(TAG, "Expected package " + info.packageName
2496 + " but restore manifest claims " + manifestPackage);
2497 }
2498 } else {
2499 Slog.i(TAG, "Unknown restore manifest version " + version
2500 + " for package " + info.packageName);
2501 }
2502 } catch (NumberFormatException e) {
2503 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
2504 }
2505
2506 return policy;
2507 }
2508
2509 // Builds a line from a byte buffer starting at 'offset', and returns
2510 // the index of the next unconsumed data in the buffer.
2511 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
2512 final int end = buffer.length;
2513 if (offset >= end) throw new IOException("Incomplete data");
2514
2515 int pos;
2516 for (pos = offset; pos < end; pos++) {
2517 byte c = buffer[pos];
2518 // at LF we declare end of line, and return the next char as the
2519 // starting point for the next time through
2520 if (c == '\n') {
2521 break;
2522 }
2523 }
2524 outStr[0] = new String(buffer, offset, pos - offset);
2525 pos++; // may be pointing an extra byte past the end but that's okay
2526 return pos;
2527 }
2528
2529 void dumpFileMetadata(FileMetadata info) {
2530 if (DEBUG) {
2531 StringBuilder b = new StringBuilder(128);
2532
2533 // mode string
2534 b.append((info.type == FullBackup.TYPE_DIRECTORY) ? 'd' : '-');
2535 b.append(((info.mode & 0400) != 0) ? 'r' : '-');
2536 b.append(((info.mode & 0200) != 0) ? 'w' : '-');
2537 b.append(((info.mode & 0100) != 0) ? 'x' : '-');
2538 b.append(((info.mode & 0040) != 0) ? 'r' : '-');
2539 b.append(((info.mode & 0020) != 0) ? 'w' : '-');
2540 b.append(((info.mode & 0010) != 0) ? 'x' : '-');
2541 b.append(((info.mode & 0004) != 0) ? 'r' : '-');
2542 b.append(((info.mode & 0002) != 0) ? 'w' : '-');
2543 b.append(((info.mode & 0001) != 0) ? 'x' : '-');
2544 b.append(String.format(" %9d ", info.size));
2545
2546 Date stamp = new Date(info.mtime);
2547 b.append(new SimpleDateFormat("MMM dd kk:mm:ss ").format(stamp));
2548
2549 b.append(info.packageName);
2550 b.append(" :: ");
2551 b.append(info.domain);
2552 b.append(" :: ");
2553 b.append(info.path);
2554
2555 Slog.i(TAG, b.toString());
2556 }
2557 }
2558 // Consume a tar file header block [sequence] and accumulate the relevant metadata
2559 FileMetadata readTarHeaders(InputStream instream) throws IOException {
2560 byte[] block = new byte[512];
2561 FileMetadata info = null;
2562
2563 boolean gotHeader = readTarHeader(instream, block);
2564 if (gotHeader) {
2565 // okay, presume we're okay, and extract the various metadata
2566 info = new FileMetadata();
2567 info.size = extractRadix(block, 124, 12, 8);
2568 info.mtime = extractRadix(block, 136, 12, 8);
2569 info.mode = extractRadix(block, 100, 8, 8);
2570
2571 info.path = extractString(block, 345, 155); // prefix
2572 String path = extractString(block, 0, 100);
2573 if (path.length() > 0) {
2574 if (info.path.length() > 0) info.path += '/';
2575 info.path += path;
2576 }
2577
2578 // tar link indicator field: 1 byte at offset 156 in the header.
2579 int typeChar = block[156];
2580 if (typeChar == 'x') {
2581 // pax extended header, so we need to read that
2582 gotHeader = readPaxExtendedHeader(instream, info);
2583 if (gotHeader) {
2584 // and after a pax extended header comes another real header -- read
2585 // that to find the real file type
2586 gotHeader = readTarHeader(instream, block);
2587 }
2588 if (!gotHeader) throw new IOException("Bad or missing pax header");
2589
2590 typeChar = block[156];
2591 }
2592
2593 switch (typeChar) {
2594 case '0': info.type = FullBackup.TYPE_FILE; break;
2595 case '5': info.type = FullBackup.TYPE_DIRECTORY; break;
2596 case 0: {
2597 // presume EOF
2598 return null;
2599 }
2600 default: {
2601 Slog.e(TAG, "Unknown tar entity type: " + typeChar);
2602 throw new IOException("Unknown entity type " + typeChar);
2603 }
2604 }
2605
2606 // Parse out the path
2607 //
2608 // first: apps/shared/unrecognized
2609 if (FullBackup.SHARED_PREFIX.regionMatches(0,
2610 info.path, 0, FullBackup.SHARED_PREFIX.length())) {
2611 // File in shared storage. !!! TODO: implement this.
2612 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
2613 info.domain = FullBackup.SHARED_STORAGE_TOKEN;
2614 } else if (FullBackup.APPS_PREFIX.regionMatches(0,
2615 info.path, 0, FullBackup.APPS_PREFIX.length())) {
2616 // App content! Parse out the package name and domain
2617
2618 // strip the apps/ prefix
2619 info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
2620
2621 // extract the package name
2622 int slash = info.path.indexOf('/');
2623 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
2624 info.packageName = info.path.substring(0, slash);
2625 info.path = info.path.substring(slash+1);
2626
2627 // if it's a manifest we're done, otherwise parse out the domains
2628 if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2629 slash = info.path.indexOf('/');
2630 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
2631 info.domain = info.path.substring(0, slash);
2632 // validate that it's one of the domains we understand
2633 if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
2634 && !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
2635 && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
2636 && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
2637 && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
2638 && !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
2639 && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
2640 throw new IOException("Unrecognized domain " + info.domain);
2641 }
2642
2643 info.path = info.path.substring(slash + 1);
2644 }
2645 }
2646 }
2647 return info;
2648 }
2649
2650 boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
2651 int nRead = instream.read(block, 0, 512);
2652 if (nRead > 0 && nRead != 512) {
2653 // if we read only a partial block, then things are
2654 // clearly screwed up. terminate the restore.
2655 throw new IOException("Partial header block: " + nRead);
2656 }
2657 return (nRead > 0);
2658 }
2659
2660 // overwrites 'info' fields based on the pax extended header
2661 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
2662 throws IOException {
2663 // We should never see a pax extended header larger than this
2664 if (info.size > 32*1024) {
2665 Slog.w(TAG, "Suspiciously large pax header size " + info.size
2666 + " - aborting");
2667 throw new IOException("Sanity failure: pax header size " + info.size);
2668 }
2669
2670 // read whole blocks, not just the content size
2671 int numBlocks = (int)((info.size + 511) >> 9);
2672 byte[] data = new byte[numBlocks * 512];
2673 int nRead = instream.read(data);
2674 if (nRead != data.length) {
2675 return false;
2676 }
2677
2678 final int contentSize = (int) info.size;
2679 int offset = 0;
2680 do {
2681 // extract the line at 'offset'
2682 int eol = offset+1;
2683 while (eol < contentSize && data[eol] != ' ') eol++;
2684 if (eol >= contentSize) {
2685 // error: we just hit EOD looking for the end of the size field
2686 throw new IOException("Invalid pax data");
2687 }
2688 // eol points to the space between the count and the key
2689 int linelen = (int) extractRadix(data, offset, eol - offset, 10);
2690 int key = eol + 1; // start of key=value
2691 eol = offset + linelen - 1; // trailing LF
2692 int value;
2693 for (value = key+1; data[value] != '=' && value <= eol; value++);
2694 if (value > eol) {
2695 throw new IOException("Invalid pax declaration");
2696 }
2697
2698 // pax requires that key/value strings be in UTF-8
2699 String keyStr = new String(data, key, value-key, "UTF-8");
2700 // -1 to strip the trailing LF
2701 String valStr = new String(data, value+1, eol-value-1, "UTF-8");
2702
2703 if ("path".equals(keyStr)) {
2704 info.path = valStr;
2705 } else if ("size".equals(keyStr)) {
2706 info.size = Long.parseLong(valStr);
2707 } else {
2708 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
2709 }
2710
2711 offset += linelen;
2712 } while (offset < contentSize);
2713
2714 return true;
2715 }
2716
2717 long extractRadix(byte[] data, int offset, int maxChars, int radix)
2718 throws IOException {
2719 long value = 0;
2720 final int end = offset + maxChars;
2721 for (int i = offset; i < end; i++) {
2722 final byte b = data[i];
2723 if (b == 0 || b == ' ') break;
2724 if (b < '0' || b > ('0' + radix - 1)) {
2725 throw new IOException("Invalid number in header");
2726 }
2727 value = radix * value + (b - '0');
2728 }
2729 return value;
2730 }
2731
2732 String extractString(byte[] data, int offset, int maxChars) throws IOException {
2733 final int end = offset + maxChars;
2734 int eos = offset;
2735 // tar string fields can end with either NUL or SPC
2736 while (eos < end && data[eos] != 0 && data[eos] != ' ') eos++;
2737 return new String(data, offset, eos-offset, "US-ASCII");
2738 }
2739
2740 void sendStartRestore() {
2741 if (mObserver != null) {
2742 try {
2743 mObserver.onStartRestore();
2744 } catch (RemoteException e) {
2745 Slog.w(TAG, "full restore observer went away: startRestore");
2746 mObserver = null;
2747 }
2748 }
2749 }
2750
2751 void sendOnRestorePackage(String name) {
2752 if (mObserver != null) {
2753 try {
2754 // TODO: use a more user-friendly name string
2755 mObserver.onRestorePackage(name);
2756 } catch (RemoteException e) {
2757 Slog.w(TAG, "full restore observer went away: restorePackage");
2758 mObserver = null;
2759 }
2760 }
2761 }
2762
2763 void sendEndRestore() {
2764 if (mObserver != null) {
2765 try {
2766 mObserver.onEndRestore();
2767 } catch (RemoteException e) {
2768 Slog.w(TAG, "full restore observer went away: endRestore");
2769 mObserver = null;
2770 }
2771 }
2772 }
2773 }
2774
Christopher Tatedf01dea2009-06-09 20:45:02 -07002775 // ----- Restore handling -----
2776
Christopher Tate78dd4a72009-11-04 11:49:08 -08002777 private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
2778 // If the target resides on the system partition, we allow it to restore
2779 // data from the like-named package in a restore set even if the signatures
2780 // do not match. (Unlike general applications, those flashed to the system
2781 // partition will be signed with the device's platform certificate, so on
2782 // different phones the same system app will have different signatures.)
2783 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002784 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
Christopher Tate78dd4a72009-11-04 11:49:08 -08002785 return true;
2786 }
2787
Christopher Tate20efdf6b2009-06-18 19:41:36 -07002788 // Allow unsigned apps, but not signed on one device and unsigned on the other
2789 // !!! TODO: is this the right policy?
Christopher Tate78dd4a72009-11-04 11:49:08 -08002790 Signature[] deviceSigs = target.signatures;
Joe Onorato8a9b2202010-02-26 18:56:32 -08002791 if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
Christopher Tate6aa41f42009-06-19 14:14:22 -07002792 + " device=" + deviceSigs);
Christopher Tate20efdf6b2009-06-18 19:41:36 -07002793 if ((storedSigs == null || storedSigs.length == 0)
2794 && (deviceSigs == null || deviceSigs.length == 0)) {
2795 return true;
2796 }
2797 if (storedSigs == null || deviceSigs == null) {
2798 return false;
2799 }
2800
Christopher Tateabce4e82009-06-18 18:35:32 -07002801 // !!! TODO: this demands that every stored signature match one
2802 // that is present on device, and does not demand the converse.
2803 // Is this this right policy?
2804 int nStored = storedSigs.length;
2805 int nDevice = deviceSigs.length;
2806
2807 for (int i=0; i < nStored; i++) {
2808 boolean match = false;
2809 for (int j=0; j < nDevice; j++) {
2810 if (storedSigs[i].equals(deviceSigs[j])) {
2811 match = true;
2812 break;
2813 }
2814 }
2815 if (!match) {
2816 return false;
2817 }
2818 }
2819 return true;
2820 }
2821
Christopher Tate44a27902010-01-27 17:15:49 -08002822 class PerformRestoreTask implements Runnable {
Christopher Tatedf01dea2009-06-09 20:45:02 -07002823 private IBackupTransport mTransport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07002824 private IRestoreObserver mObserver;
Dan Egnor156411d2009-06-26 13:20:02 -07002825 private long mToken;
Christopher Tate84725812010-02-04 15:52:40 -08002826 private PackageInfo mTargetPackage;
Christopher Tate5cb400b2009-06-25 16:03:14 -07002827 private File mStateDir;
Christopher Tate1bb69062010-02-19 17:02:12 -08002828 private int mPmToken;
Chris Tate249345b2010-10-29 12:57:04 -07002829 private boolean mNeedFullBackup;
Christopher Tatedf01dea2009-06-09 20:45:02 -07002830
Christopher Tate5cbbf562009-06-22 16:44:51 -07002831 class RestoreRequest {
2832 public PackageInfo app;
2833 public int storedAppVersion;
2834
2835 RestoreRequest(PackageInfo _app, int _version) {
2836 app = _app;
2837 storedAppVersion = _version;
2838 }
2839 }
2840
Christopher Tate44a27902010-01-27 17:15:49 -08002841 PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
Chris Tate249345b2010-10-29 12:57:04 -07002842 long restoreSetToken, PackageInfo targetPackage, int pmToken,
2843 boolean needFullBackup) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07002844 mTransport = transport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07002845 mObserver = observer;
Christopher Tate9bbc21a2009-06-10 20:23:25 -07002846 mToken = restoreSetToken;
Christopher Tate84725812010-02-04 15:52:40 -08002847 mTargetPackage = targetPackage;
Christopher Tate1bb69062010-02-19 17:02:12 -08002848 mPmToken = pmToken;
Chris Tate249345b2010-10-29 12:57:04 -07002849 mNeedFullBackup = needFullBackup;
Christopher Tate5cb400b2009-06-25 16:03:14 -07002850
2851 try {
2852 mStateDir = new File(mBaseStateDir, transport.transportDirName());
2853 } catch (RemoteException e) {
2854 // can't happen; the transport is local
2855 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07002856 }
2857
Christopher Tatedf01dea2009-06-09 20:45:02 -07002858 public void run() {
Dan Egnorbb9001c2009-07-27 12:20:13 -07002859 long startRealtime = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08002860 if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport
Christopher Tate84725812010-02-04 15:52:40 -08002861 + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
Christopher Tate1bb69062010-02-19 17:02:12 -08002862 + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken);
Christopher Tatedf01dea2009-06-09 20:45:02 -07002863
Christopher Tateb49ceb32010-02-08 16:22:24 -08002864 PackageManagerBackupAgent pmAgent = null;
Christopher Tate7d562ec2009-06-25 18:03:43 -07002865 int error = -1; // assume error
2866
Dan Egnorefe52642009-06-24 00:16:33 -07002867 // build the set of apps to restore
Christopher Tatedf01dea2009-06-09 20:45:02 -07002868 try {
Dan Egnorbb9001c2009-07-27 12:20:13 -07002869 // TODO: Log this before getAvailableRestoreSets, somehow
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002870 EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken);
Christopher Tateabce4e82009-06-18 18:35:32 -07002871
Dan Egnorefe52642009-06-24 00:16:33 -07002872 // Get the list of all packages which have backup enabled.
2873 // (Include the Package Manager metadata pseudo-package first.)
2874 ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
2875 PackageInfo omPackage = new PackageInfo();
2876 omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
2877 restorePackages.add(omPackage);
Christopher Tatedf01dea2009-06-09 20:45:02 -07002878
Dan Egnorefe52642009-06-24 00:16:33 -07002879 List<PackageInfo> agentPackages = allAgentPackages();
Christopher Tate84725812010-02-04 15:52:40 -08002880 if (mTargetPackage == null) {
2881 restorePackages.addAll(agentPackages);
2882 } else {
2883 // Just one package to attempt restore of
2884 restorePackages.add(mTargetPackage);
2885 }
Dan Egnorefe52642009-06-24 00:16:33 -07002886
Christopher Tate7d562ec2009-06-25 18:03:43 -07002887 // let the observer know that we're running
2888 if (mObserver != null) {
2889 try {
2890 // !!! TODO: get an actual count from the transport after
2891 // its startRestore() runs?
2892 mObserver.restoreStarting(restorePackages.size());
2893 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002894 Slog.d(TAG, "Restore observer died at restoreStarting");
Christopher Tate7d562ec2009-06-25 18:03:43 -07002895 mObserver = null;
2896 }
2897 }
2898
Dan Egnor01445162009-09-21 17:04:05 -07002899 if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) !=
2900 BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002901 Slog.e(TAG, "Error starting restore operation");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002902 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07002903 return;
2904 }
2905
2906 String packageName = mTransport.nextRestorePackage();
2907 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002908 Slog.e(TAG, "Error getting first restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002909 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07002910 return;
2911 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002912 Slog.i(TAG, "No restore data available");
Dan Egnorbb9001c2009-07-27 12:20:13 -07002913 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002914 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
Dan Egnorefe52642009-06-24 00:16:33 -07002915 return;
2916 } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002917 Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
Dan Egnorefe52642009-06-24 00:16:33 -07002918 + "\", found only \"" + packageName + "\"");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002919 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07002920 "Package manager data missing");
Dan Egnorefe52642009-06-24 00:16:33 -07002921 return;
2922 }
2923
2924 // Pull the Package Manager metadata from the restore set first
Christopher Tateb49ceb32010-02-08 16:22:24 -08002925 pmAgent = new PackageManagerBackupAgent(
Dan Egnorefe52642009-06-24 00:16:33 -07002926 mPackageManager, agentPackages);
Chris Tate249345b2010-10-29 12:57:04 -07002927 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()),
2928 mNeedFullBackup);
Dan Egnorefe52642009-06-24 00:16:33 -07002929
Christopher Tate8c032472009-07-02 14:28:47 -07002930 // Verify that the backup set includes metadata. If not, we can't do
2931 // signature/version verification etc, so we simply do not proceed with
2932 // the restore operation.
Christopher Tate3d7cd132009-07-07 14:23:07 -07002933 if (!pmAgent.hasMetadata()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002934 Slog.e(TAG, "No restore metadata available, so not restoring settings");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002935 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07002936 "Package manager restore metadata missing");
Christopher Tate8c032472009-07-02 14:28:47 -07002937 return;
2938 }
2939
Christopher Tate7d562ec2009-06-25 18:03:43 -07002940 int count = 0;
Dan Egnorefe52642009-06-24 00:16:33 -07002941 for (;;) {
2942 packageName = mTransport.nextRestorePackage();
Dan Egnorbb9001c2009-07-27 12:20:13 -07002943
Dan Egnorefe52642009-06-24 00:16:33 -07002944 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002945 Slog.e(TAG, "Error getting next restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002946 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07002947 return;
2948 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002949 if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
Dan Egnorefe52642009-06-24 00:16:33 -07002950 break;
Christopher Tatedf01dea2009-06-09 20:45:02 -07002951 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07002952
Christopher Tate7d562ec2009-06-25 18:03:43 -07002953 if (mObserver != null) {
Christopher Tate7d562ec2009-06-25 18:03:43 -07002954 try {
Christopher Tate9c3cee92010-03-25 16:06:43 -07002955 mObserver.onUpdate(count, packageName);
Christopher Tate7d562ec2009-06-25 18:03:43 -07002956 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002957 Slog.d(TAG, "Restore observer died in onUpdate");
Christopher Tate7d562ec2009-06-25 18:03:43 -07002958 mObserver = null;
2959 }
2960 }
2961
Dan Egnorefe52642009-06-24 00:16:33 -07002962 Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
2963 if (metaInfo == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002964 Slog.e(TAG, "Missing metadata for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002965 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07002966 "Package metadata missing");
Dan Egnorefe52642009-06-24 00:16:33 -07002967 continue;
2968 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07002969
Dan Egnorbb9001c2009-07-27 12:20:13 -07002970 PackageInfo packageInfo;
2971 try {
2972 int flags = PackageManager.GET_SIGNATURES;
2973 packageInfo = mPackageManager.getPackageInfo(packageName, flags);
2974 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002975 Slog.e(TAG, "Invalid package restoring data", e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002976 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07002977 "Package missing on device");
2978 continue;
2979 }
2980
Dan Egnorefe52642009-06-24 00:16:33 -07002981 if (metaInfo.versionCode > packageInfo.versionCode) {
Christopher Tate3dda5182010-02-24 16:06:18 -08002982 // Data is from a "newer" version of the app than we have currently
2983 // installed. If the app has not declared that it is prepared to
2984 // handle this case, we do not attempt the restore.
2985 if ((packageInfo.applicationInfo.flags
2986 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
2987 String message = "Version " + metaInfo.versionCode
2988 + " > installed version " + packageInfo.versionCode;
Joe Onorato8a9b2202010-02-26 18:56:32 -08002989 Slog.w(TAG, "Package " + packageName + ": " + message);
Christopher Tate3dda5182010-02-24 16:06:18 -08002990 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
2991 packageName, message);
2992 continue;
2993 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002994 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
Christopher Tate3dda5182010-02-24 16:06:18 -08002995 + " > installed " + packageInfo.versionCode
2996 + " but restoreAnyVersion");
2997 }
Dan Egnorefe52642009-06-24 00:16:33 -07002998 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07002999
Christopher Tate78dd4a72009-11-04 11:49:08 -08003000 if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003001 Slog.w(TAG, "Signature mismatch restoring " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003002 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003003 "Signature mismatch");
Dan Egnorefe52642009-06-24 00:16:33 -07003004 continue;
3005 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003006
Joe Onorato8a9b2202010-02-26 18:56:32 -08003007 if (DEBUG) Slog.v(TAG, "Package " + packageName
Dan Egnorefe52642009-06-24 00:16:33 -07003008 + " restore version [" + metaInfo.versionCode
3009 + "] is compatible with installed version ["
3010 + packageInfo.versionCode + "]");
Christopher Tatec7b31e32009-06-10 15:49:30 -07003011
Christopher Tate3de55bc2010-03-12 17:28:08 -08003012 // Then set up and bind the agent
Dan Egnorefe52642009-06-24 00:16:33 -07003013 IBackupAgent agent = bindToAgentSynchronous(
3014 packageInfo.applicationInfo,
Christopher Tate3de55bc2010-03-12 17:28:08 -08003015 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Dan Egnorefe52642009-06-24 00:16:33 -07003016 if (agent == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003017 Slog.w(TAG, "Can't find backup agent for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003018 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003019 "Restore agent missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003020 continue;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003021 }
3022
Christopher Tate5e1ab332009-09-01 20:32:49 -07003023 // And then finally run the restore on this agent
Dan Egnorefe52642009-06-24 00:16:33 -07003024 try {
Chris Tate249345b2010-10-29 12:57:04 -07003025 processOneRestore(packageInfo, metaInfo.versionCode, agent,
3026 mNeedFullBackup);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003027 ++count;
Dan Egnorefe52642009-06-24 00:16:33 -07003028 } finally {
Christopher Tate5e1ab332009-09-01 20:32:49 -07003029 // unbind and tidy up even on timeout or failure, just in case
Dan Egnorefe52642009-06-24 00:16:33 -07003030 mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
Christopher Tate5e1ab332009-09-01 20:32:49 -07003031
3032 // The agent was probably running with a stub Application object,
3033 // which isn't a valid run mode for the main app logic. Shut
3034 // down the app so that next time it's launched, it gets the
Christopher Tate3dda5182010-02-24 16:06:18 -08003035 // usual full initialization. Note that this is only done for
3036 // full-system restores: when a single app has requested a restore,
3037 // it is explicitly not killed following that operation.
3038 if (mTargetPackage == null && (packageInfo.applicationInfo.flags
Christopher Tate5e1ab332009-09-01 20:32:49 -07003039 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003040 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
Christopher Tate5e1ab332009-09-01 20:32:49 -07003041 + packageInfo.applicationInfo.processName);
3042 mActivityManager.killApplicationProcess(
3043 packageInfo.applicationInfo.processName,
3044 packageInfo.applicationInfo.uid);
3045 }
Dan Egnorefe52642009-06-24 00:16:33 -07003046 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003047 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003048
3049 // if we get this far, report success to the observer
3050 error = 0;
Dan Egnorbb9001c2009-07-27 12:20:13 -07003051 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003052 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, count, millis);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003053 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003054 Slog.e(TAG, "Error in restore thread", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003055 } finally {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003056 if (DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003057
Dan Egnorefe52642009-06-24 00:16:33 -07003058 try {
3059 mTransport.finishRestore();
3060 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003061 Slog.e(TAG, "Error finishing restore", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003062 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003063
3064 if (mObserver != null) {
3065 try {
3066 mObserver.restoreFinished(error);
3067 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003068 Slog.d(TAG, "Restore observer died at restoreFinished");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003069 }
3070 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003071
Christopher Tate84725812010-02-04 15:52:40 -08003072 // If this was a restoreAll operation, record that this was our
Christopher Tateb49ceb32010-02-08 16:22:24 -08003073 // ancestral dataset, as well as the set of apps that are possibly
3074 // restoreable from the dataset
3075 if (mTargetPackage == null && pmAgent != null) {
3076 mAncestralPackages = pmAgent.getRestoredPackages();
Christopher Tate84725812010-02-04 15:52:40 -08003077 mAncestralToken = mToken;
3078 writeRestoreTokens();
3079 }
3080
Christopher Tate1bb69062010-02-19 17:02:12 -08003081 // We must under all circumstances tell the Package Manager to
3082 // proceed with install notifications if it's waiting for us.
3083 if (mPmToken > 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003084 if (DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
Christopher Tate1bb69062010-02-19 17:02:12 -08003085 try {
3086 mPackageManagerBinder.finishPackageInstall(mPmToken);
3087 } catch (RemoteException e) { /* can't happen */ }
3088 }
3089
Christopher Tate73a3cb32010-12-13 18:27:26 -08003090 // Furthermore we need to reset the session timeout clock
3091 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
3092 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
3093 TIMEOUT_RESTORE_INTERVAL);
3094
Christopher Tateb6787f22009-07-02 17:40:45 -07003095 // done; we can finally release the wakelock
3096 mWakelock.release();
Christopher Tatedf01dea2009-06-09 20:45:02 -07003097 }
3098 }
3099
Dan Egnorefe52642009-06-24 00:16:33 -07003100 // Do the guts of a restore of one application, using mTransport.getRestoreData().
Chris Tate249345b2010-10-29 12:57:04 -07003101 void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
3102 boolean needFullBackup) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003103 // !!! TODO: actually run the restore through mTransport
Christopher Tatec7b31e32009-06-10 15:49:30 -07003104 final String packageName = app.packageName;
3105
Joe Onorato8a9b2202010-02-26 18:56:32 -08003106 if (DEBUG) Slog.d(TAG, "processOneRestore packageName=" + packageName);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04003107
Christopher Tatec7b31e32009-06-10 15:49:30 -07003108 // !!! TODO: get the dirs from the transport
3109 File backupDataName = new File(mDataDir, packageName + ".restore");
Dan Egnorbb9001c2009-07-27 12:20:13 -07003110 File newStateName = new File(mStateDir, packageName + ".new");
3111 File savedStateName = new File(mStateDir, packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003112
Dan Egnorbb9001c2009-07-27 12:20:13 -07003113 ParcelFileDescriptor backupData = null;
3114 ParcelFileDescriptor newState = null;
3115
Christopher Tate4a627c72011-04-01 14:43:32 -07003116 final int token = generateToken();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003117 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07003118 // Run the transport's restore pass
Dan Egnorbb9001c2009-07-27 12:20:13 -07003119 backupData = ParcelFileDescriptor.open(backupDataName,
3120 ParcelFileDescriptor.MODE_READ_WRITE |
3121 ParcelFileDescriptor.MODE_CREATE |
3122 ParcelFileDescriptor.MODE_TRUNCATE);
3123
Dan Egnor01445162009-09-21 17:04:05 -07003124 if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003125 Slog.e(TAG, "Error getting restore data for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003126 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003127 return;
Christopher Tatec7b31e32009-06-10 15:49:30 -07003128 }
3129
3130 // Okay, we have the data. Now have the agent do the restore.
Dan Egnorbb9001c2009-07-27 12:20:13 -07003131 backupData.close();
Christopher Tatec7b31e32009-06-10 15:49:30 -07003132 backupData = ParcelFileDescriptor.open(backupDataName,
3133 ParcelFileDescriptor.MODE_READ_ONLY);
3134
Dan Egnorbb9001c2009-07-27 12:20:13 -07003135 newState = ParcelFileDescriptor.open(newStateName,
3136 ParcelFileDescriptor.MODE_READ_WRITE |
3137 ParcelFileDescriptor.MODE_CREATE |
3138 ParcelFileDescriptor.MODE_TRUNCATE);
3139
Christopher Tate44a27902010-01-27 17:15:49 -08003140 // Kick off the restore, checking for hung agents
3141 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL);
3142 agent.doRestore(backupData, appVersionCode, newState, token, mBackupManagerBinder);
3143 boolean success = waitUntilOperationComplete(token);
3144
3145 if (!success) {
3146 throw new RuntimeException("restore timeout");
3147 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003148
3149 // if everything went okay, remember the recorded state now
Christopher Tate90967f42009-09-20 15:28:33 -07003150 //
3151 // !!! TODO: the restored data should be migrated on the server
3152 // side into the current dataset. In that case the new state file
3153 // we just created would reflect the data already extant in the
3154 // backend, so there'd be nothing more to do. Until that happens,
3155 // however, we need to make sure that we record the data to the
3156 // current backend dataset. (Yes, this means shipping the data over
3157 // the wire in both directions. That's bad, but consistency comes
3158 // first, then efficiency.) Once we introduce server-side data
3159 // migration to the newly-restored device's dataset, we will change
3160 // the following from a discard of the newly-written state to the
3161 // "correct" operation of renaming into the canonical state blob.
3162 newStateName.delete(); // TODO: remove; see above comment
3163 //newStateName.renameTo(savedStateName); // TODO: replace with this
3164
Dan Egnorbb9001c2009-07-27 12:20:13 -07003165 int size = (int) backupDataName.length();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003166 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, packageName, size);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003167 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003168 Slog.e(TAG, "Error restoring data for " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003169 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
Dan Egnorbb9001c2009-07-27 12:20:13 -07003170
Christopher Tate96733042009-07-20 14:49:13 -07003171 // If the agent fails restore, it might have put the app's data
3172 // into an incoherent state. For consistency we wipe its data
3173 // again in this case before propagating the exception
Christopher Tate96733042009-07-20 14:49:13 -07003174 clearApplicationDataSynchronous(packageName);
Christopher Tate1531dc82009-07-24 16:37:43 -07003175 } finally {
3176 backupDataName.delete();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003177 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
3178 try { if (newState != null) newState.close(); } catch (IOException e) {}
3179 backupData = newState = null;
Christopher Tate4a627c72011-04-01 14:43:32 -07003180 synchronized (mCurrentOperations) {
3181 mCurrentOperations.delete(token);
3182 }
Chris Tate249345b2010-10-29 12:57:04 -07003183
3184 // If we know a priori that we'll need to perform a full post-restore backup
3185 // pass, clear the new state file data. This means we're discarding work that
3186 // was just done by the app's agent, but this way the agent doesn't need to
3187 // take any special action based on global device state.
3188 if (needFullBackup) {
3189 newStateName.delete();
3190 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003191 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003192 }
3193 }
3194
Christopher Tate44a27902010-01-27 17:15:49 -08003195 class PerformClearTask implements Runnable {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003196 IBackupTransport mTransport;
3197 PackageInfo mPackage;
3198
Christopher Tate44a27902010-01-27 17:15:49 -08003199 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003200 mTransport = transport;
3201 mPackage = packageInfo;
3202 }
3203
Christopher Tateee0e78a2009-07-02 11:17:03 -07003204 public void run() {
3205 try {
3206 // Clear the on-device backup state to ensure a full backup next time
3207 File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
3208 File stateFile = new File(stateDir, mPackage.packageName);
3209 stateFile.delete();
3210
3211 // Tell the transport to remove all the persistent storage for the app
Christopher Tate13f4a642009-09-30 20:06:45 -07003212 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003213 mTransport.clearBackupData(mPackage);
3214 } catch (RemoteException e) {
3215 // can't happen; the transport is local
3216 } finally {
3217 try {
Christopher Tate13f4a642009-09-30 20:06:45 -07003218 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003219 mTransport.finishBackup();
3220 } catch (RemoteException e) {
3221 // can't happen; the transport is local
3222 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003223
3224 // Last but not least, release the cpu
3225 mWakelock.release();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003226 }
3227 }
3228 }
3229
Christopher Tate44a27902010-01-27 17:15:49 -08003230 class PerformInitializeTask implements Runnable {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003231 HashSet<String> mQueue;
3232
Christopher Tate44a27902010-01-27 17:15:49 -08003233 PerformInitializeTask(HashSet<String> transportNames) {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003234 mQueue = transportNames;
3235 }
3236
Christopher Tate4cc86e12009-09-21 19:36:51 -07003237 public void run() {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003238 try {
3239 for (String transportName : mQueue) {
3240 IBackupTransport transport = getTransport(transportName);
3241 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003242 Slog.e(TAG, "Requested init for " + transportName + " but not found");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003243 continue;
3244 }
3245
Joe Onorato8a9b2202010-02-26 18:56:32 -08003246 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003247 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
Dan Egnor726247c2009-09-29 19:12:31 -07003248 long startRealtime = SystemClock.elapsedRealtime();
3249 int status = transport.initializeDevice();
Christopher Tate4cc86e12009-09-21 19:36:51 -07003250
Christopher Tate4cc86e12009-09-21 19:36:51 -07003251 if (status == BackupConstants.TRANSPORT_OK) {
3252 status = transport.finishBackup();
3253 }
3254
3255 // Okay, the wipe really happened. Clean up our local bookkeeping.
3256 if (status == BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003257 Slog.i(TAG, "Device init successful");
Dan Egnor726247c2009-09-29 19:12:31 -07003258 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003259 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07003260 resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003261 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003262 synchronized (mQueueLock) {
3263 recordInitPendingLocked(false, transportName);
3264 }
Dan Egnor726247c2009-09-29 19:12:31 -07003265 } else {
3266 // If this didn't work, requeue this one and try again
3267 // after a suitable interval
Joe Onorato8a9b2202010-02-26 18:56:32 -08003268 Slog.e(TAG, "Transport error in initializeDevice()");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003269 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003270 synchronized (mQueueLock) {
3271 recordInitPendingLocked(true, transportName);
3272 }
3273 // do this via another alarm to make sure of the wakelock states
3274 long delay = transport.requestBackupTime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08003275 if (DEBUG) Slog.w(TAG, "init failed on "
Christopher Tate4cc86e12009-09-21 19:36:51 -07003276 + transportName + " resched in " + delay);
3277 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
3278 System.currentTimeMillis() + delay, mRunInitIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003279 }
Christopher Tate4cc86e12009-09-21 19:36:51 -07003280 }
3281 } catch (RemoteException e) {
3282 // can't happen; the transports are local
3283 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003284 Slog.e(TAG, "Unexpected error performing init", e);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003285 } finally {
Christopher Tatec2af5d32010-02-02 15:18:58 -08003286 // Done; release the wakelock
Christopher Tate4cc86e12009-09-21 19:36:51 -07003287 mWakelock.release();
3288 }
3289 }
3290 }
3291
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003292 private void dataChangedImpl(String packageName) {
3293 HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3294 dataChangedImpl(packageName, targets);
3295 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003296
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003297 private void dataChangedImpl(String packageName, HashSet<ApplicationInfo> targets) {
Christopher Tate487529a2009-04-29 14:03:25 -07003298 // Record that we need a backup pass for the caller. Since multiple callers
3299 // may share a uid, we need to note all candidates within that uid and schedule
3300 // a backup pass for each of them.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003301 EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003302
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003303 if (targets == null) {
3304 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3305 + " uid=" + Binder.getCallingUid());
3306 return;
3307 }
3308
3309 synchronized (mQueueLock) {
3310 // Note that this client has made data changes that need to be backed up
3311 for (ApplicationInfo app : targets) {
3312 // validate the caller-supplied package name against the known set of
3313 // packages associated with this uid
3314 if (app.packageName.equals(packageName)) {
3315 // Add the caller to the set of pending backups. If there is
3316 // one already there, then overwrite it, but no harm done.
Christopher Tate4a627c72011-04-01 14:43:32 -07003317 BackupRequest req = new BackupRequest(app);
Christopher Tatec28083a2010-12-14 16:16:44 -08003318 if (mPendingBackups.put(app.packageName, req) == null) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003319 // Journal this request in case of crash. The put()
3320 // operation returned null when this package was not already
3321 // in the set; we want to avoid touching the disk redundantly.
3322 writeToJournalLocked(packageName);
3323
3324 if (DEBUG) {
3325 int numKeys = mPendingBackups.size();
3326 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
3327 for (BackupRequest b : mPendingBackups.values()) {
3328 Slog.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
3329 }
3330 }
3331 }
3332 }
3333 }
3334 }
3335 }
3336
3337 // Note: packageName is currently unused, but may be in the future
3338 private HashSet<ApplicationInfo> dataChangedTargets(String packageName) {
Christopher Tate63d27002009-06-16 17:16:42 -07003339 // If the caller does not hold the BACKUP permission, it can only request a
3340 // backup of its own data.
Dianne Hackborncf098292009-07-01 19:55:20 -07003341 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tate63d27002009-06-16 17:16:42 -07003342 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003343 synchronized (mBackupParticipants) {
3344 return mBackupParticipants.get(Binder.getCallingUid());
3345 }
3346 }
3347
3348 // a caller with full permission can ask to back up any participating app
3349 // !!! TODO: allow backup of ANY app?
3350 HashSet<ApplicationInfo> targets = new HashSet<ApplicationInfo>();
3351 synchronized (mBackupParticipants) {
Christopher Tate63d27002009-06-16 17:16:42 -07003352 int N = mBackupParticipants.size();
3353 for (int i = 0; i < N; i++) {
3354 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3355 if (s != null) {
3356 targets.addAll(s);
3357 }
3358 }
3359 }
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003360 return targets;
Christopher Tate487529a2009-04-29 14:03:25 -07003361 }
Christopher Tate46758122009-05-06 11:22:00 -07003362
Christopher Tatecde87f42009-06-12 12:55:53 -07003363 private void writeToJournalLocked(String str) {
Dan Egnor852f8e42009-09-30 11:20:45 -07003364 RandomAccessFile out = null;
3365 try {
3366 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
3367 out = new RandomAccessFile(mJournal, "rws");
3368 out.seek(out.length());
3369 out.writeUTF(str);
3370 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003371 Slog.e(TAG, "Can't write " + str + " to backup journal", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07003372 mJournal = null;
3373 } finally {
3374 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tatecde87f42009-06-12 12:55:53 -07003375 }
3376 }
3377
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003378 // ----- IBackupManager binder interface -----
3379
3380 public void dataChanged(final String packageName) {
3381 final HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3382 if (targets == null) {
3383 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3384 + " uid=" + Binder.getCallingUid());
3385 return;
3386 }
3387
3388 mBackupHandler.post(new Runnable() {
3389 public void run() {
3390 dataChangedImpl(packageName, targets);
3391 }
3392 });
3393 }
3394
Christopher Tateee0e78a2009-07-02 11:17:03 -07003395 // Clear the given package's backup data from the current transport
3396 public void clearBackupData(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003397 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003398 PackageInfo info;
3399 try {
3400 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
3401 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003402 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003403 return;
3404 }
3405
3406 // If the caller does not hold the BACKUP permission, it can only request a
3407 // wipe of its own backed-up data.
3408 HashSet<ApplicationInfo> apps;
Christopher Tate4e3e50c2009-07-02 12:14:05 -07003409 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tateee0e78a2009-07-02 11:17:03 -07003410 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
3411 apps = mBackupParticipants.get(Binder.getCallingUid());
3412 } else {
3413 // a caller with full permission can ask to back up any participating app
3414 // !!! TODO: allow data-clear of ANY app?
Joe Onorato8a9b2202010-02-26 18:56:32 -08003415 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003416 apps = new HashSet<ApplicationInfo>();
3417 int N = mBackupParticipants.size();
3418 for (int i = 0; i < N; i++) {
3419 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3420 if (s != null) {
3421 apps.addAll(s);
3422 }
3423 }
3424 }
3425
3426 // now find the given package in the set of candidate apps
3427 for (ApplicationInfo app : apps) {
3428 if (app.packageName.equals(packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003429 if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003430 // found it; fire off the clear request
3431 synchronized (mQueueLock) {
Christopher Tateaa93b042009-08-05 18:21:40 -07003432 long oldId = Binder.clearCallingIdentity();
Christopher Tateb6787f22009-07-02 17:40:45 -07003433 mWakelock.acquire();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003434 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
3435 new ClearParams(getTransport(mCurrentTransport), info));
3436 mBackupHandler.sendMessage(msg);
Christopher Tateaa93b042009-08-05 18:21:40 -07003437 Binder.restoreCallingIdentity(oldId);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003438 }
3439 break;
3440 }
3441 }
3442 }
3443
Christopher Tateace7f092009-06-15 18:07:25 -07003444 // Run a backup pass immediately for any applications that have declared
3445 // that they have pending updates.
Dan Egnor852f8e42009-09-30 11:20:45 -07003446 public void backupNow() {
Joe Onorato5933a492009-07-23 18:24:08 -04003447 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
Christopher Tate043dadc2009-06-02 16:11:00 -07003448
Joe Onorato8a9b2202010-02-26 18:56:32 -08003449 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
Christopher Tate46758122009-05-06 11:22:00 -07003450 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07003451 // Because the alarms we are using can jitter, and we want an *immediate*
3452 // backup pass to happen, we restart the timer beginning with "next time,"
3453 // then manually fire the backup trigger intent ourselves.
3454 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tateb6787f22009-07-02 17:40:45 -07003455 try {
Christopher Tateb6787f22009-07-02 17:40:45 -07003456 mRunBackupIntent.send();
3457 } catch (PendingIntent.CanceledException e) {
3458 // should never happen
Joe Onorato8a9b2202010-02-26 18:56:32 -08003459 Slog.e(TAG, "run-backup intent cancelled!");
Christopher Tateb6787f22009-07-02 17:40:45 -07003460 }
Christopher Tate46758122009-05-06 11:22:00 -07003461 }
3462 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003463
Christopher Tate4a627c72011-04-01 14:43:32 -07003464 // Run a *full* backup pass for the given package, writing the resulting data stream
3465 // to the supplied file descriptor. This method is synchronous and does not return
3466 // to the caller until the backup has been completed.
3467 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
3468 boolean doAllApps, String[] pkgList) {
3469 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3470
3471 // Validate
3472 if (!doAllApps) {
3473 if (!includeShared) {
3474 // If we're backing up shared data (sdcard or equivalent), then we can run
3475 // without any supplied app names. Otherwise, we'd be doing no work, so
3476 // report the error.
3477 if (pkgList == null || pkgList.length == 0) {
3478 throw new IllegalArgumentException(
3479 "Backup requested but neither shared nor any apps named");
3480 }
3481 }
3482 }
3483
3484 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
3485 + " shared=" + includeShared + " all=" + doAllApps
3486 + " pkgs=" + pkgList);
3487
3488 long oldId = Binder.clearCallingIdentity();
3489 try {
3490 FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
3491 doAllApps, pkgList);
3492 final int token = generateToken();
3493 synchronized (mFullConfirmations) {
3494 mFullConfirmations.put(token, params);
3495 }
3496
Christopher Tate75a99702011-05-18 16:28:19 -07003497 // start up the confirmation UI
3498 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
3499 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
3500 Slog.e(TAG, "Unable to launch full backup confirmation");
Christopher Tate4a627c72011-04-01 14:43:32 -07003501 mFullConfirmations.delete(token);
3502 return;
3503 }
Christopher Tate75a99702011-05-18 16:28:19 -07003504
3505 // make sure the screen is lit for the user interaction
Christopher Tate4a627c72011-04-01 14:43:32 -07003506 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3507
3508 // start the confirmation countdown
Christopher Tate75a99702011-05-18 16:28:19 -07003509 startConfirmationTimeout(token, params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003510
3511 // wait for the backup to be performed
3512 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
3513 waitForCompletion(params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003514 } finally {
Christopher Tate4a627c72011-04-01 14:43:32 -07003515 try {
3516 fd.close();
3517 } catch (IOException e) {
3518 // just eat it
3519 }
Christopher Tate75a99702011-05-18 16:28:19 -07003520 Binder.restoreCallingIdentity(oldId);
Christopher Tate4a627c72011-04-01 14:43:32 -07003521 }
Christopher Tate75a99702011-05-18 16:28:19 -07003522 if (DEBUG) Slog.d(TAG, "Full backup done; returning to caller");
3523 }
3524
3525 public void fullRestore(ParcelFileDescriptor fd) {
3526 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3527 Slog.i(TAG, "Beginning full restore...");
3528
3529 long oldId = Binder.clearCallingIdentity();
3530
3531 try {
3532 FullRestoreParams params = new FullRestoreParams(fd);
3533 final int token = generateToken();
3534 synchronized (mFullConfirmations) {
3535 mFullConfirmations.put(token, params);
3536 }
3537
3538 // start up the confirmation UI
3539 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
3540 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
3541 Slog.e(TAG, "Unable to launch full restore confirmation");
3542 mFullConfirmations.delete(token);
3543 return;
3544 }
3545
3546 // make sure the screen is lit for the user interaction
3547 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3548
3549 // start the confirmation countdown
3550 startConfirmationTimeout(token, params);
3551
3552 // wait for the restore to be performed
3553 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
3554 waitForCompletion(params);
3555 } finally {
3556 try {
3557 fd.close();
3558 } catch (IOException e) {
3559 Slog.w(TAG, "Error trying to close fd after full restore: " + e);
3560 }
3561 Binder.restoreCallingIdentity(oldId);
3562 Slog.i(TAG, "Full restore completed");
3563 }
3564 }
3565
3566 boolean startConfirmationUi(int token, String action) {
3567 try {
3568 Intent confIntent = new Intent(action);
3569 confIntent.setClassName("com.android.backupconfirm",
3570 "com.android.backupconfirm.BackupRestoreConfirmation");
3571 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
3572 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3573 mContext.startActivity(confIntent);
3574 } catch (ActivityNotFoundException e) {
3575 return false;
3576 }
3577 return true;
3578 }
3579
3580 void startConfirmationTimeout(int token, FullParams params) {
3581 if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
3582 + TIMEOUT_FULL_CONFIRMATION + " millis");
3583 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
3584 token, 0, params);
3585 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
Christopher Tate4a627c72011-04-01 14:43:32 -07003586 }
3587
3588 void waitForCompletion(FullParams params) {
3589 synchronized (params.latch) {
3590 while (params.latch.get() == false) {
3591 try {
3592 params.latch.wait();
3593 } catch (InterruptedException e) { /* never interrupted */ }
3594 }
3595 }
3596 }
3597
3598 void signalFullBackupRestoreCompletion(FullParams params) {
3599 synchronized (params.latch) {
3600 params.latch.set(true);
3601 params.latch.notifyAll();
3602 }
3603 }
3604
3605 // Confirm that the previously-requested full backup/restore operation can proceed. This
3606 // is used to require a user-facing disclosure about the operation.
3607 public void acknowledgeFullBackupOrRestore(int token, boolean allow,
3608 IFullBackupRestoreObserver observer) {
3609 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
3610 + " allow=" + allow);
3611
3612 // TODO: possibly require not just this signature-only permission, but even
3613 // require that the specific designated confirmation-UI app uid is the caller?
3614 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3615
3616 long oldId = Binder.clearCallingIdentity();
3617 try {
3618
3619 FullParams params;
3620 synchronized (mFullConfirmations) {
3621 params = mFullConfirmations.get(token);
3622 if (params != null) {
3623 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
3624 mFullConfirmations.delete(token);
3625
3626 if (allow) {
3627 params.observer = observer;
3628 final int verb = params instanceof FullBackupParams
Christopher Tate75a99702011-05-18 16:28:19 -07003629 ? MSG_RUN_FULL_BACKUP
Christopher Tate4a627c72011-04-01 14:43:32 -07003630 : MSG_RUN_FULL_RESTORE;
3631
Christopher Tate75a99702011-05-18 16:28:19 -07003632 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
Christopher Tate4a627c72011-04-01 14:43:32 -07003633 mWakelock.acquire();
3634 Message msg = mBackupHandler.obtainMessage(verb, params);
3635 mBackupHandler.sendMessage(msg);
3636 } else {
3637 Slog.w(TAG, "User rejected full backup/restore operation");
3638 // indicate completion without having actually transferred any data
3639 signalFullBackupRestoreCompletion(params);
3640 }
3641 } else {
3642 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
3643 }
3644 }
3645 } finally {
3646 Binder.restoreCallingIdentity(oldId);
3647 }
3648 }
3649
Christopher Tate8031a3d2009-07-06 16:36:05 -07003650 // Enable/disable the backup service
Christopher Tate6ef58a12009-06-29 14:56:28 -07003651 public void setBackupEnabled(boolean enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003652 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4a627c72011-04-01 14:43:32 -07003653 "setBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07003654
Joe Onorato8a9b2202010-02-26 18:56:32 -08003655 Slog.i(TAG, "Backup enabled => " + enable);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003656
Christopher Tate6ef58a12009-06-29 14:56:28 -07003657 boolean wasEnabled = mEnabled;
3658 synchronized (this) {
Dianne Hackborncf098292009-07-01 19:55:20 -07003659 Settings.Secure.putInt(mContext.getContentResolver(),
3660 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
Christopher Tate6ef58a12009-06-29 14:56:28 -07003661 mEnabled = enable;
3662 }
3663
Christopher Tate49401dd2009-07-01 12:34:29 -07003664 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07003665 if (enable && !wasEnabled && mProvisioned) {
Christopher Tate49401dd2009-07-01 12:34:29 -07003666 // if we've just been enabled, start scheduling backup passes
Christopher Tate8031a3d2009-07-06 16:36:05 -07003667 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tate49401dd2009-07-01 12:34:29 -07003668 } else if (!enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003669 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08003670 if (DEBUG) Slog.i(TAG, "Opting out of backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003671
Christopher Tateb6787f22009-07-02 17:40:45 -07003672 mAlarmManager.cancel(mRunBackupIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003673
3674 // This also constitutes an opt-out, so we wipe any data for
3675 // this device from the backend. We start that process with
3676 // an alarm in order to guarantee wakelock states.
3677 if (wasEnabled && mProvisioned) {
3678 // NOTE: we currently flush every registered transport, not just
3679 // the currently-active one.
3680 HashSet<String> allTransports;
3681 synchronized (mTransports) {
3682 allTransports = new HashSet<String>(mTransports.keySet());
3683 }
3684 // build the set of transports for which we are posting an init
3685 for (String transport : allTransports) {
3686 recordInitPendingLocked(true, transport);
3687 }
3688 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
3689 mRunInitIntent);
3690 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07003691 }
3692 }
Christopher Tate49401dd2009-07-01 12:34:29 -07003693 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07003694
Christopher Tatecce9da52010-02-03 15:11:15 -08003695 // Enable/disable automatic restore of app data at install time
3696 public void setAutoRestore(boolean doAutoRestore) {
3697 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3698 "setBackupEnabled");
3699
Joe Onorato8a9b2202010-02-26 18:56:32 -08003700 Slog.i(TAG, "Auto restore => " + doAutoRestore);
Christopher Tatecce9da52010-02-03 15:11:15 -08003701
3702 synchronized (this) {
3703 Settings.Secure.putInt(mContext.getContentResolver(),
3704 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
3705 mAutoRestore = doAutoRestore;
3706 }
3707 }
3708
Christopher Tate8031a3d2009-07-06 16:36:05 -07003709 // Mark the backup service as having been provisioned
3710 public void setBackupProvisioned(boolean available) {
3711 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3712 "setBackupProvisioned");
3713
3714 boolean wasProvisioned = mProvisioned;
3715 synchronized (this) {
3716 Settings.Secure.putInt(mContext.getContentResolver(),
3717 Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
3718 mProvisioned = available;
3719 }
3720
3721 synchronized (mQueueLock) {
3722 if (available && !wasProvisioned && mEnabled) {
3723 // we're now good to go, so start the backup alarms
3724 startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
3725 } else if (!available) {
3726 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08003727 Slog.w(TAG, "Backup service no longer provisioned");
Christopher Tate8031a3d2009-07-06 16:36:05 -07003728 mAlarmManager.cancel(mRunBackupIntent);
3729 }
3730 }
3731 }
3732
3733 private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
Dan Egnorc1c49c02009-10-30 17:35:39 -07003734 // We used to use setInexactRepeating(), but that may be linked to
3735 // backups running at :00 more often than not, creating load spikes.
3736 // Schedule at an exact time for now, and also add a bit of "fuzz".
3737
3738 Random random = new Random();
3739 long when = System.currentTimeMillis() + delayBeforeFirstBackup +
3740 random.nextInt(FUZZ_MILLIS);
3741 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
3742 BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
Christopher Tate55f931a2009-09-29 17:17:34 -07003743 mNextBackupPass = when;
Christopher Tate8031a3d2009-07-06 16:36:05 -07003744 }
3745
Christopher Tate6ef58a12009-06-29 14:56:28 -07003746 // Report whether the backup mechanism is currently enabled
3747 public boolean isBackupEnabled() {
Joe Onorato5933a492009-07-23 18:24:08 -04003748 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07003749 return mEnabled; // no need to synchronize just to read it
3750 }
3751
Christopher Tate91717492009-06-26 21:07:13 -07003752 // Report the name of the currently active transport
3753 public String getCurrentTransport() {
Joe Onorato5933a492009-07-23 18:24:08 -04003754 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4e3e50c2009-07-02 12:14:05 -07003755 "getCurrentTransport");
Joe Onorato8a9b2202010-02-26 18:56:32 -08003756 if (DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -07003757 return mCurrentTransport;
Christopher Tateace7f092009-06-15 18:07:25 -07003758 }
3759
Christopher Tate91717492009-06-26 21:07:13 -07003760 // Report all known, available backup transports
3761 public String[] listAllTransports() {
Christopher Tate34ebd0e2009-07-06 15:44:54 -07003762 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
Christopher Tate043dadc2009-06-02 16:11:00 -07003763
Christopher Tate91717492009-06-26 21:07:13 -07003764 String[] list = null;
3765 ArrayList<String> known = new ArrayList<String>();
3766 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
3767 if (entry.getValue() != null) {
3768 known.add(entry.getKey());
3769 }
3770 }
3771
3772 if (known.size() > 0) {
3773 list = new String[known.size()];
3774 known.toArray(list);
3775 }
3776 return list;
3777 }
3778
3779 // Select which transport to use for the next backup operation. If the given
3780 // name is not one of the available transports, no action is taken and the method
3781 // returns null.
3782 public String selectBackupTransport(String transport) {
Joe Onorato5933a492009-07-23 18:24:08 -04003783 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
Christopher Tate91717492009-06-26 21:07:13 -07003784
3785 synchronized (mTransports) {
3786 String prevTransport = null;
3787 if (mTransports.get(transport) != null) {
3788 prevTransport = mCurrentTransport;
3789 mCurrentTransport = transport;
Dianne Hackborncf098292009-07-01 19:55:20 -07003790 Settings.Secure.putString(mContext.getContentResolver(),
3791 Settings.Secure.BACKUP_TRANSPORT, transport);
Joe Onorato8a9b2202010-02-26 18:56:32 -08003792 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
Christopher Tate91717492009-06-26 21:07:13 -07003793 + " returning " + prevTransport);
3794 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003795 Slog.w(TAG, "Attempt to select unavailable transport " + transport);
Christopher Tate91717492009-06-26 21:07:13 -07003796 }
3797 return prevTransport;
3798 }
Christopher Tate043dadc2009-06-02 16:11:00 -07003799 }
3800
Christopher Tatef5e1c292010-12-08 18:40:26 -08003801 // Supply the configuration Intent for the given transport. If the name is not one
3802 // of the available transports, or if the transport does not supply any configuration
3803 // UI, the method returns null.
3804 public Intent getConfigurationIntent(String transportName) {
3805 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3806 "getConfigurationIntent");
3807
3808 synchronized (mTransports) {
3809 final IBackupTransport transport = mTransports.get(transportName);
3810 if (transport != null) {
3811 try {
3812 final Intent intent = transport.configurationIntent();
3813 if (DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
3814 + intent);
3815 return intent;
3816 } catch (RemoteException e) {
3817 /* fall through to return null */
3818 }
3819 }
3820 }
3821
3822 return null;
3823 }
3824
3825 // Supply the configuration summary string for the given transport. If the name is
3826 // not one of the available transports, or if the transport does not supply any
3827 // summary / destination string, the method can return null.
3828 //
3829 // This string is used VERBATIM as the summary text of the relevant Settings item!
3830 public String getDestinationString(String transportName) {
3831 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3832 "getConfigurationIntent");
3833
3834 synchronized (mTransports) {
3835 final IBackupTransport transport = mTransports.get(transportName);
3836 if (transport != null) {
3837 try {
3838 final String text = transport.currentDestinationString();
3839 if (DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
3840 return text;
3841 } catch (RemoteException e) {
3842 /* fall through to return null */
3843 }
3844 }
3845 }
3846
3847 return null;
3848 }
3849
Christopher Tate043dadc2009-06-02 16:11:00 -07003850 // Callback: a requested backup agent has been instantiated. This should only
3851 // be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07003852 public void agentConnected(String packageName, IBinder agentBinder) {
Christopher Tate043dadc2009-06-02 16:11:00 -07003853 synchronized(mAgentConnectLock) {
3854 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003855 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
Christopher Tate043dadc2009-06-02 16:11:00 -07003856 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
3857 mConnectedAgent = agent;
3858 mConnecting = false;
3859 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003860 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07003861 + " claiming agent connected");
3862 }
3863 mAgentConnectLock.notifyAll();
3864 }
Christopher Tate181fafa2009-05-14 11:12:14 -07003865 }
3866
3867 // Callback: a backup agent has failed to come up, or has unexpectedly quit.
3868 // If the agent failed to come up in the first place, the agentBinder argument
Christopher Tate043dadc2009-06-02 16:11:00 -07003869 // will be null. This should only be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07003870 public void agentDisconnected(String packageName) {
3871 // TODO: handle backup being interrupted
Christopher Tate043dadc2009-06-02 16:11:00 -07003872 synchronized(mAgentConnectLock) {
3873 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
3874 mConnectedAgent = null;
3875 mConnecting = false;
3876 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003877 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07003878 + " claiming agent disconnected");
3879 }
3880 mAgentConnectLock.notifyAll();
3881 }
Christopher Tate181fafa2009-05-14 11:12:14 -07003882 }
Christopher Tate181fafa2009-05-14 11:12:14 -07003883
Christopher Tate1bb69062010-02-19 17:02:12 -08003884 // An application being installed will need a restore pass, then the Package Manager
3885 // will need to be told when the restore is finished.
3886 public void restoreAtInstall(String packageName, int token) {
3887 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003888 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate1bb69062010-02-19 17:02:12 -08003889 + " attemping install-time restore");
3890 return;
3891 }
3892
3893 long restoreSet = getAvailableRestoreToken(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08003894 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
Christopher Tate1bb69062010-02-19 17:02:12 -08003895 + " token=" + Integer.toHexString(token));
3896
Christopher Tatef0872722010-02-25 15:22:48 -08003897 if (mAutoRestore && mProvisioned && restoreSet != 0) {
Christopher Tate1bb69062010-02-19 17:02:12 -08003898 // okay, we're going to attempt a restore of this package from this restore set.
3899 // The eventual message back into the Package Manager to run the post-install
3900 // steps for 'token' will be issued from the restore handling code.
3901
3902 // We can use a synthetic PackageInfo here because:
3903 // 1. We know it's valid, since the Package Manager supplied the name
3904 // 2. Only the packageName field will be used by the restore code
3905 PackageInfo pkg = new PackageInfo();
3906 pkg.packageName = packageName;
3907
3908 mWakelock.acquire();
3909 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
3910 msg.obj = new RestoreParams(getTransport(mCurrentTransport), null,
Chris Tate249345b2010-10-29 12:57:04 -07003911 restoreSet, pkg, token, true);
Christopher Tate1bb69062010-02-19 17:02:12 -08003912 mBackupHandler.sendMessage(msg);
3913 } else {
Christopher Tatef0872722010-02-25 15:22:48 -08003914 // Auto-restore disabled or no way to attempt a restore; just tell the Package
3915 // Manager to proceed with the post-install handling for this package.
Joe Onorato8a9b2202010-02-26 18:56:32 -08003916 if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore");
Christopher Tate1bb69062010-02-19 17:02:12 -08003917 try {
3918 mPackageManagerBinder.finishPackageInstall(token);
3919 } catch (RemoteException e) { /* can't happen */ }
3920 }
3921 }
3922
Christopher Tate8c850b72009-06-07 19:33:20 -07003923 // Hand off a restore session
Chris Tate44ab8452010-11-16 15:10:49 -08003924 public IRestoreSession beginRestoreSession(String packageName, String transport) {
3925 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
3926 + " transport=" + transport);
3927
3928 boolean needPermission = true;
3929 if (transport == null) {
3930 transport = mCurrentTransport;
3931
3932 if (packageName != null) {
3933 PackageInfo app = null;
3934 try {
3935 app = mPackageManager.getPackageInfo(packageName, 0);
3936 } catch (NameNotFoundException nnf) {
3937 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
3938 throw new IllegalArgumentException("Package " + packageName + " not found");
3939 }
3940
3941 if (app.applicationInfo.uid == Binder.getCallingUid()) {
3942 // So: using the current active transport, and the caller has asked
3943 // that its own package will be restored. In this narrow use case
3944 // we do not require the caller to hold the permission.
3945 needPermission = false;
3946 }
3947 }
3948 }
3949
3950 if (needPermission) {
3951 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
3952 "beginRestoreSession");
3953 } else {
3954 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
3955 }
Christopher Tatef68eb502009-06-16 11:02:01 -07003956
3957 synchronized(this) {
3958 if (mActiveRestoreSession != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003959 Slog.d(TAG, "Restore session requested but one already active");
Christopher Tatef68eb502009-06-16 11:02:01 -07003960 return null;
3961 }
Chris Tate44ab8452010-11-16 15:10:49 -08003962 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
Christopher Tate73a3cb32010-12-13 18:27:26 -08003963 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
Christopher Tatef68eb502009-06-16 11:02:01 -07003964 }
3965 return mActiveRestoreSession;
Christopher Tate8c850b72009-06-07 19:33:20 -07003966 }
Christopher Tate043dadc2009-06-02 16:11:00 -07003967
Christopher Tate73a3cb32010-12-13 18:27:26 -08003968 void clearRestoreSession(ActiveRestoreSession currentSession) {
3969 synchronized(this) {
3970 if (currentSession != mActiveRestoreSession) {
3971 Slog.e(TAG, "ending non-current restore session");
3972 } else {
3973 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
3974 mActiveRestoreSession = null;
3975 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
3976 }
3977 }
3978 }
3979
Christopher Tate44a27902010-01-27 17:15:49 -08003980 // Note that a currently-active backup agent has notified us that it has
3981 // completed the given outstanding asynchronous backup/restore operation.
3982 public void opComplete(int token) {
3983 synchronized (mCurrentOpLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003984 if (DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
Christopher Tate44a27902010-01-27 17:15:49 -08003985 mCurrentOperations.put(token, OP_ACKNOWLEDGED);
3986 mCurrentOpLock.notifyAll();
3987 }
3988 }
3989
Christopher Tate9b3905c2009-06-08 15:24:01 -07003990 // ----- Restore session -----
3991
Christopher Tate80202c82010-01-25 19:37:47 -08003992 class ActiveRestoreSession extends IRestoreSession.Stub {
Christopher Tatef68eb502009-06-16 11:02:01 -07003993 private static final String TAG = "RestoreSession";
3994
Chris Tate44ab8452010-11-16 15:10:49 -08003995 private String mPackageName;
Christopher Tate9b3905c2009-06-08 15:24:01 -07003996 private IBackupTransport mRestoreTransport = null;
3997 RestoreSet[] mRestoreSets = null;
Christopher Tate73a3cb32010-12-13 18:27:26 -08003998 boolean mEnded = false;
Christopher Tate9b3905c2009-06-08 15:24:01 -07003999
Chris Tate44ab8452010-11-16 15:10:49 -08004000 ActiveRestoreSession(String packageName, String transport) {
4001 mPackageName = packageName;
Christopher Tate91717492009-06-26 21:07:13 -07004002 mRestoreTransport = getTransport(transport);
Christopher Tate9b3905c2009-06-08 15:24:01 -07004003 }
4004
4005 // --- Binder interface ---
Christopher Tate2d449afe2010-03-29 19:14:24 -07004006 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
Joe Onorato5933a492009-07-23 18:24:08 -04004007 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004008 "getAvailableRestoreSets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004009 if (observer == null) {
4010 throw new IllegalArgumentException("Observer must not be null");
4011 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004012
Christopher Tate73a3cb32010-12-13 18:27:26 -08004013 if (mEnded) {
4014 throw new IllegalStateException("Restore session already ended");
4015 }
4016
Christopher Tate1bb69062010-02-19 17:02:12 -08004017 long oldId = Binder.clearCallingIdentity();
Christopher Tatef68eb502009-06-16 11:02:01 -07004018 try {
Christopher Tate43383042009-07-13 15:17:13 -07004019 if (mRestoreTransport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004020 Slog.w(TAG, "Null transport getting restore sets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004021 return -1;
Dan Egnor0084da52009-07-29 12:57:16 -07004022 }
Christopher Tate2d449afe2010-03-29 19:14:24 -07004023 // spin off the transport request to our service thread
4024 mWakelock.acquire();
4025 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
4026 new RestoreGetSetsParams(mRestoreTransport, this, observer));
4027 mBackupHandler.sendMessage(msg);
4028 return 0;
Dan Egnor0084da52009-07-29 12:57:16 -07004029 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004030 Slog.e(TAG, "Error in getAvailableRestoreSets", e);
Christopher Tate2d449afe2010-03-29 19:14:24 -07004031 return -1;
Christopher Tate1bb69062010-02-19 17:02:12 -08004032 } finally {
4033 Binder.restoreCallingIdentity(oldId);
Christopher Tatef68eb502009-06-16 11:02:01 -07004034 }
Christopher Tate9b3905c2009-06-08 15:24:01 -07004035 }
4036
Christopher Tate84725812010-02-04 15:52:40 -08004037 public synchronized int restoreAll(long token, IRestoreObserver observer) {
Dan Egnor0084da52009-07-29 12:57:16 -07004038 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4039 "performRestore");
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004040
Chris Tate44ab8452010-11-16 15:10:49 -08004041 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
Christopher Tatef2c321a2009-08-10 15:43:36 -07004042 + " observer=" + observer);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004043
Christopher Tate73a3cb32010-12-13 18:27:26 -08004044 if (mEnded) {
4045 throw new IllegalStateException("Restore session already ended");
4046 }
4047
Dan Egnor0084da52009-07-29 12:57:16 -07004048 if (mRestoreTransport == null || mRestoreSets == null) {
Chris Tate44ab8452010-11-16 15:10:49 -08004049 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
4050 return -1;
4051 }
4052
4053 if (mPackageName != null) {
4054 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
Dan Egnor0084da52009-07-29 12:57:16 -07004055 return -1;
4056 }
4057
Christopher Tate21ab6a52009-09-24 18:01:46 -07004058 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07004059 for (int i = 0; i < mRestoreSets.length; i++) {
4060 if (token == mRestoreSets[i].token) {
4061 long oldId = Binder.clearCallingIdentity();
Christopher Tate21ab6a52009-09-24 18:01:46 -07004062 mWakelock.acquire();
4063 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004064 msg.obj = new RestoreParams(mRestoreTransport, observer, token, true);
Christopher Tate21ab6a52009-09-24 18:01:46 -07004065 mBackupHandler.sendMessage(msg);
4066 Binder.restoreCallingIdentity(oldId);
4067 return 0;
4068 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004069 }
4070 }
Christopher Tate0e0b4ae2009-08-10 16:13:47 -07004071
Joe Onorato8a9b2202010-02-26 18:56:32 -08004072 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
Christopher Tate9b3905c2009-06-08 15:24:01 -07004073 return -1;
4074 }
4075
Christopher Tate84725812010-02-04 15:52:40 -08004076 public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004077 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
Christopher Tate84725812010-02-04 15:52:40 -08004078
Christopher Tate73a3cb32010-12-13 18:27:26 -08004079 if (mEnded) {
4080 throw new IllegalStateException("Restore session already ended");
4081 }
4082
Chris Tate44ab8452010-11-16 15:10:49 -08004083 if (mPackageName != null) {
4084 if (! mPackageName.equals(packageName)) {
4085 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
4086 + " on session for package " + mPackageName);
4087 return -1;
4088 }
4089 }
4090
Christopher Tate84725812010-02-04 15:52:40 -08004091 PackageInfo app = null;
4092 try {
4093 app = mPackageManager.getPackageInfo(packageName, 0);
4094 } catch (NameNotFoundException nnf) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004095 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004096 return -1;
4097 }
4098
4099 // If the caller is not privileged and is not coming from the target
4100 // app's uid, throw a permission exception back to the caller.
4101 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
4102 Binder.getCallingPid(), Binder.getCallingUid());
4103 if ((perm == PackageManager.PERMISSION_DENIED) &&
4104 (app.applicationInfo.uid != Binder.getCallingUid())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004105 Slog.w(TAG, "restorePackage: bad packageName=" + packageName
Christopher Tate84725812010-02-04 15:52:40 -08004106 + " or calling uid=" + Binder.getCallingUid());
4107 throw new SecurityException("No permission to restore other packages");
4108 }
4109
Christopher Tate7d411a32010-02-26 11:27:08 -08004110 // If the package has no backup agent, we obviously cannot proceed
4111 if (app.applicationInfo.backupAgentName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004112 Slog.w(TAG, "Asked to restore package " + packageName + " with no agent");
Christopher Tate7d411a32010-02-26 11:27:08 -08004113 return -1;
4114 }
4115
Christopher Tate84725812010-02-04 15:52:40 -08004116 // So far so good; we're allowed to try to restore this package. Now
4117 // check whether there is data for it in the current dataset, falling back
4118 // to the ancestral dataset if not.
Christopher Tate1bb69062010-02-19 17:02:12 -08004119 long token = getAvailableRestoreToken(packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004120
4121 // If we didn't come up with a place to look -- no ancestral dataset and
4122 // the app has never been backed up from this device -- there's nothing
4123 // to do but return failure.
4124 if (token == 0) {
Chris Tate44ab8452010-11-16 15:10:49 -08004125 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
Christopher Tate84725812010-02-04 15:52:40 -08004126 return -1;
4127 }
4128
4129 // Ready to go: enqueue the restore request and claim success
4130 long oldId = Binder.clearCallingIdentity();
4131 mWakelock.acquire();
4132 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004133 msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0, false);
Christopher Tate84725812010-02-04 15:52:40 -08004134 mBackupHandler.sendMessage(msg);
4135 Binder.restoreCallingIdentity(oldId);
4136 return 0;
4137 }
4138
Christopher Tate73a3cb32010-12-13 18:27:26 -08004139 // Posted to the handler to tear down a restore session in a cleanly synchronized way
4140 class EndRestoreRunnable implements Runnable {
4141 BackupManagerService mBackupManager;
4142 ActiveRestoreSession mSession;
4143
4144 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
4145 mBackupManager = manager;
4146 mSession = session;
4147 }
4148
4149 public void run() {
4150 // clean up the session's bookkeeping
4151 synchronized (mSession) {
4152 try {
4153 if (mSession.mRestoreTransport != null) {
4154 mSession.mRestoreTransport.finishRestore();
4155 }
4156 } catch (Exception e) {
4157 Slog.e(TAG, "Error in finishRestore", e);
4158 } finally {
4159 mSession.mRestoreTransport = null;
4160 mSession.mEnded = true;
4161 }
4162 }
4163
4164 // clean up the BackupManagerService side of the bookkeeping
4165 // and cancel any pending timeout message
4166 mBackupManager.clearRestoreSession(mSession);
4167 }
4168 }
4169
Dan Egnor0084da52009-07-29 12:57:16 -07004170 public synchronized void endRestoreSession() {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004171 if (DEBUG) Slog.d(TAG, "endRestoreSession");
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004172
Christopher Tate73a3cb32010-12-13 18:27:26 -08004173 if (mEnded) {
4174 throw new IllegalStateException("Restore session already ended");
Dan Egnor0084da52009-07-29 12:57:16 -07004175 }
4176
Christopher Tate73a3cb32010-12-13 18:27:26 -08004177 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
Christopher Tate9b3905c2009-06-08 15:24:01 -07004178 }
4179 }
4180
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004181 @Override
4182 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004183 long identityToken = Binder.clearCallingIdentity();
4184 try {
4185 dumpInternal(pw);
4186 } finally {
4187 Binder.restoreCallingIdentity(identityToken);
4188 }
4189 }
4190
4191 private void dumpInternal(PrintWriter pw) {
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004192 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07004193 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
Christopher Tate55f931a2009-09-29 17:17:34 -07004194 + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
Christopher Tatec2af5d32010-02-02 15:18:58 -08004195 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
Christopher Tateae06ed92010-02-25 17:13:28 -08004196 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
Christopher Tate55f931a2009-09-29 17:17:34 -07004197 pw.println("Last backup pass: " + mLastBackupPass
4198 + " (now = " + System.currentTimeMillis() + ')');
4199 pw.println(" next scheduled: " + mNextBackupPass);
4200
Christopher Tate91717492009-06-26 21:07:13 -07004201 pw.println("Available transports:");
4202 for (String t : listAllTransports()) {
Dan Egnor852f8e42009-09-30 11:20:45 -07004203 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
4204 try {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004205 IBackupTransport transport = getTransport(t);
4206 File dir = new File(mBaseStateDir, transport.transportDirName());
4207 pw.println(" destination: " + transport.currentDestinationString());
4208 pw.println(" intent: " + transport.configurationIntent());
Dan Egnor852f8e42009-09-30 11:20:45 -07004209 for (File f : dir.listFiles()) {
4210 pw.println(" " + f.getName() + " - " + f.length() + " state bytes");
4211 }
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004212 } catch (Exception e) {
4213 Slog.e(TAG, "Error in transport", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07004214 pw.println(" Error: " + e);
4215 }
Christopher Tate91717492009-06-26 21:07:13 -07004216 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004217
4218 pw.println("Pending init: " + mPendingInits.size());
4219 for (String s : mPendingInits) {
4220 pw.println(" " + s);
4221 }
4222
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004223 int N = mBackupParticipants.size();
Christopher Tate55f931a2009-09-29 17:17:34 -07004224 pw.println("Participants:");
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004225 for (int i=0; i<N; i++) {
4226 int uid = mBackupParticipants.keyAt(i);
4227 pw.print(" uid: ");
4228 pw.println(uid);
Christopher Tate181fafa2009-05-14 11:12:14 -07004229 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
4230 for (ApplicationInfo app: participants) {
Christopher Tate55f931a2009-09-29 17:17:34 -07004231 pw.println(" " + app.packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004232 }
4233 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004234
Christopher Tateb49ceb32010-02-08 16:22:24 -08004235 pw.println("Ancestral packages: "
4236 + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
Christopher Tate5923c972010-04-04 17:45:35 -07004237 if (mAncestralPackages != null) {
4238 for (String pkg : mAncestralPackages) {
4239 pw.println(" " + pkg);
4240 }
Christopher Tateb49ceb32010-02-08 16:22:24 -08004241 }
4242
Christopher Tate73e02522009-07-15 14:18:26 -07004243 pw.println("Ever backed up: " + mEverStoredApps.size());
4244 for (String pkg : mEverStoredApps) {
4245 pw.println(" " + pkg);
4246 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004247
4248 pw.println("Pending backup: " + mPendingBackups.size());
Christopher Tate6aa41f42009-06-19 14:14:22 -07004249 for (BackupRequest req : mPendingBackups.values()) {
Christopher Tate6ef58a12009-06-29 14:56:28 -07004250 pw.println(" " + req);
Christopher Tate181fafa2009-05-14 11:12:14 -07004251 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004252 }
4253 }
Christopher Tate487529a2009-04-29 14:03:25 -07004254}